1(function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 (factory((global.echarts = {}))); 5}(this, (function (exports) { 'use strict'; 6 7 /* 8* Licensed to the Apache Software Foundation (ASF) under one 9* or more contributor license agreements. See the NOTICE file 10* distributed with this work for additional information 11* regarding copyright ownership. The ASF licenses this file 12* to you under the Apache License, Version 2.0 (the 13* "License"); you may not use this file except in compliance 14* with the License. You may obtain a copy of the License at 15* 16* http://www.apache.org/licenses/LICENSE-2.0 17* 18* Unless required by applicable law or agreed to in writing, 19* software distributed under the License is distributed on an 20* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21* KIND, either express or implied. See the License for the 22* specific language governing permissions and limitations 23* under the License. 24*/ 25 26// (1) The code `if (__DEV__) ...` can be removed by build tool. 27// (2) If intend to use `__DEV__`, this module should be imported. Use a global 28// variable `__DEV__` may cause that miss the declaration (see #6535), or the 29// declaration is behind of the using position (for example in `Model.extent`, 30// And tools like rollup can not analysis the dependency if not import). 31 32 var dev; 33 34// In browser 35 if (typeof window !== 'undefined') { 36 dev = window.__DEV__; 37 } 38// In node 39 else if (typeof global !== 'undefined') { 40 dev = global.__DEV__; 41 } 42 43 if (typeof dev === 'undefined') { 44 dev = true; 45 } 46 47 var __DEV__ = dev; 48 49 /** 50 * zrender: é¢ç¸åéîç«´id 51 * 52 * @author errorrik ([email protected]) 53 */ 54 55 var idStart = 0x0907; 56 57 var guid = function () { 58 return idStart++; 59 }; 60 61 /** 62 * echartsçæ§î¬éîî¨çåå 63 * 64 * @desc echartsé©è½°ç°¬Canvaséå²å½Javascripté¥æãæ´æç´é»æªç·µé©ç£îé岿éîç´éîæ°¦æµæç´éîéé¬Ñå¯²ç¹æ°¬åé¨å¬æé¹î¾ç²ºçâ³æµçã£â¬ï¿½ 65 * @author firede[[email protected]] 66 * @desc thanks zepto. 67 */ 68 69 var env = {}; 70 71 if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') { 72 // In Weixin Application 73 env = { 74 browser: {}, 75 os: {}, 76 node: false, 77 wxa: true, // Weixin Application 78 canvasSupported: true, 79 svgSupported: false, 80 touchEventsSupported: true 81 }; 82 } 83 else if (typeof document === 'undefined' && typeof self !== 'undefined') { 84 // In worker 85 env = { 86 browser: {}, 87 os: {}, 88 node: false, 89 worker: true, 90 canvasSupported: true 91 }; 92 } 93 else if (typeof navigator === 'undefined') { 94 // In node 95 env = { 96 browser: {}, 97 os: {}, 98 node: true, 99 worker: false, 100 // Assume canvas is supported 101 canvasSupported: true, 102 svgSupported: true 103 }; 104 } 105 else { 106 env = detect(navigator.userAgent); 107 } 108 109 var env$1 = env; 110 111// Zepto.js 112// (c) 2010-2013 Thomas Fuchs 113// Zepto.js may be freely distributed under the MIT license. 114 115 function detect(ua) { 116 var os = {}; 117 var browser = {}; 118 // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/); 119 // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); 120 // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); 121 // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); 122 // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); 123 // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/); 124 // var touchpad = webos && ua.match(/TouchPad/); 125 // var kindle = ua.match(/Kindle\/([\d.]+)/);
126 // var silk = ua.match(/Silk\/([\d._]+)/); 127 // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/); 128 // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/); 129 // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/); 130 // var playbook = ua.match(/PlayBook/); 131 // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/); 132 var firefox = ua.match(/Firefox\/([\d.]+)/); 133 // var safari = webkit && ua.match(/Mobile\//) && !chrome; 134 // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome; 135 var ie = ua.match(/MSIE\s([\d.]+)/) 136 // IE 11 Trident/7.0; rv:11.0 137 || ua.match(/Trident\/.+?rv:(([\d.]+))/); 138 var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+ 139 140 var weChat = (/micromessenger/i).test(ua); 141 142 // Todo: clean this up with a better OS/browser seperation: 143 // - discern (more) between multiple browsers on android 144 // - decide if kindle fire in silk mode is android or not 145 // - Firefox on Android doesn't specify the Android version 146 // - possibly devide in os, device and browser hashes 147 148 // if (browser.webkit = !!webkit) browser.version = webkit[1]; 149 150 // if (android) os.android = true, os.version = android[2]; 151 // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.'); 152 // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.'); 153 // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null; 154 // if (webos) os.webos = true, os.version = webos[2]; 155 // if (touchpad) os.touchpad = true; 156 // if (blackberry) os.blackberry = true, os.version = blackberry[2]; 157 // if (bb10) os.bb10 = true, os.version = bb10[2];
158 // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2]; 159 // if (playbook) browser.playbook = true; 160 // if (kindle) os.kindle = true, os.version = kindle[1]; 161 // if (silk) browser.silk = true, browser.version = silk[1]; 162 // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true; 163 // if (chrome) browser.chrome = true, browser.version = chrome[1]; 164 if (firefox) { 165 browser.firefox = true; 166 browser.version = firefox[1]; 167 } 168 // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true; 169 // if (webview) browser.webview = true; 170 171 if (ie) { 172 browser.ie = true; 173 browser.version = ie[1]; 174 } 175 176 if (edge) { 177 browser.edge = true; 178 browser.version = edge[1]; 179 } 180 181 // It is difficult to detect WeChat in Win Phone precisely, because ua can 182 // not be set on win phone. So we do not consider Win Phone. 183 if (weChat) { 184 browser.weChat = true; 185 } 186 187 // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || 188 // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
189 // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos || 190 // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || 191 // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/)))); 192 193 return { 194 browser: browser, 195 os: os, 196 node: false, 197 // éçºæcanvaséîå¯éå±¾æ¼é佺î¬ééç°¡ 198 // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9) 199 canvasSupported: !!document.createElement('canvas').getContext, 200 svgSupported: typeof SVGRect !== 'undefined', 201 // works on most browsers 202 // IE10/11 does not support touch event, and MS Edge supports them but not by 203 // default, so we dont check navigator.maxTouchPoints for them here. 204 touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge, 205 // <http://caniuse.com/#search=pointer%20event>. 206 pointerEventsSupported: 'onpointerdown' in window 207 // Firefox supports pointer but not by default, only MS browsers are reliable on pointer 208 // events currently. So we dont use that on other browsers unless tested sufficiently. 209 // Although IE 10 supports pointer event, it use old style and is different from the 210 // standard. So we exclude that. (IE 10 is hardly used on touch device) 211 && (browser.edge || (browser.ie && browser.version >= 11)) 212 // passiveSupported: detectPassiveSupport() 213 }; 214 } 215 216// See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection 217// function detectPassiveSupport() { 218// // Test via a getter in the options object to see if the passive property is accessed 219// var supportsPassive = false; 220// try { 221// var opts = Object.defineProperty({}, 'passive', { 222// get: function() { 223// supportsPassive = true; 224// } 225// }); 226// window.addEventListener('testPassive', function() {}, opts); 227// } catch (e) { 228// } 229// return supportsPassive; 230// } 231 232 /** 233 * @module zrender/core/util 234 */ 235 236// é¢ã¤ç°¬æ¾¶åæmergeéèµæ£¤å¨æ¢äº¶éå²ateç»å¤î®çï¼æ®éîî½ 237 var BUILTIN_OBJECT = { 238 '[object Function]': 1, 239 '[object RegExp]': 1, 240 '[object Date]': 1, 241 '[object Error]': 1, 242 '[object CanvasGradient]': 1, 243 '[object CanvasPattern]': 1, 244 // For node-canvas 245 '[object Image]': 1, 246 '[object Canvas]': 1 247 }; 248 249 var TYPED_ARRAY = { 250 '[object Int8Array]': 1, 251 '[object Uint8Array]': 1, 252 '[object Uint8ClampedArray]': 1, 253 '[object Int16Array]': 1, 254 '[object Uint16Array]': 1, 255 '[object Int32Array]': 1, 256 '[object Uint32Array]': 1, 257 '[object Float32Array]': 1, 258 '[object Float64Array]': 1 259 }; 260 261 var objToString = Object.prototype.toString; 262 263 var arrayProto = Array.prototype; 264 var nativeForEach = arrayProto.forEach; 265 var nativeFilter = arrayProto.filter; 266 var nativeSlice = arrayProto.slice; 267 var nativeMap = arrayProto.map; 268 var nativeReduce = arrayProto.reduce; 269 270// Avoid assign to an exported variable, for transforming to cjs. 271 var methods = {}; 272 273 function $override(name, fn) { 274 // Clear ctx instance for different environment 275 if (name === 'createCanvas') { 276 _ctx = null; 277 } 278 279 methods[name] = fn; 280 } 281 282 /** 283 * Those data types can be cloned: 284 * Plain object, Array, TypedArray, number, string, null, undefined. 285 * Those data types will be assgined using the orginal data: 286 * BUILTIN_OBJECT 287 * Instance of user defined class will be cloned to a plain object, without 288 * properties in prototype. 289 * Other data types is not supported (not sure what will happen). 290 * 291 * Caution: do not support clone Date, for performance consideration. 292 * (There might be a large number of date in `series.data`). 293 * So date should not be modified in and out of echarts. 294 * 295 * @param {*} source 296 * @return {*} new 297 */ 298 function clone(source) { 299 if (source == null || typeof source != 'object') { 300 return source; 301 } 302 303 var result = source; 304 var typeStr = objToString.call(source); 305 306 if (typeStr === '[object Array]') { 307 if (!isPrimitive(source)) { 308 result = []; 309 for (var i = 0, len = source.length; i < len; i++) { 310 result[i] = clone(source[i]); 311 } 312 } 313 } 314 else if (TYPED_ARRAY[typeStr]) { 315 if (!isPrimitive(source)) { 316 var Ctor = source.constructor; 317 if (source.constructor.from) { 318 result = Ctor.from(source); 319 } 320 else { 321 result = new Ctor(source.length); 322 for (var i = 0, len = source.length; i < len; i++) { 323 result[i] = clone(source[i]); 324 } 325 } 326 } 327 } 328 else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) { 329 result = {}; 330 for (var key in source) { 331 if (source.hasOwnProperty(key)) { 332 result[key] = clone(source[key]); 333 } 334 } 335 } 336 337 return result; 338 } 339 340 /** 341 * @memberOf module:zrender/core/util 342 * @param {*} target 343 * @param {*} source 344 * @param {boolean} [overwrite=false] 345 */ 346 function merge(target, source, overwrite) { 347 // We should escapse that source is string 348 // and enter for ... in ... 349 if (!isObject$1(source) || !isObject$1(target)) { 350 return overwrite ? clone(source) : target; 351 } 352 353 for (var key in source) { 354 if (source.hasOwnProperty(key)) { 355 var targetProp = target[key]; 356 var sourceProp = source[key]; 357 358 if (isObject$1(sourceProp) 359 && isObject$1(targetProp) 360 && !isArray(sourceProp) 361 && !isArray(targetProp) 362 && !isDom(sourceProp) 363 && !isDom(targetProp) 364 && !isBuiltInObject(sourceProp) 365 && !isBuiltInObject(targetProp) 366 && !isPrimitive(sourceProp) 367 && !isPrimitive(targetProp) 368 ) { 369 // æ¿¡åçéâ¬çä¾â¬æç¶çåæ´éå±½æ°¨é«æç¶çåªæ¤merge 370 merge(targetProp, sourceProp, overwrite); 371 } 372 else if (overwrite || !(key in target)) { 373 // éï¹å¯éîî©éåverwriteæ¶ç°rueéå±¾å¨é°å湪é©î½ç£çµç¡ èæ¶î ç éå¤îçç´â¬Ñ æ®é¯åå 374 // NOTEé屽湪 target[key] æ¶å¶ ç¨é¦ã§æ®éè·ºâ¬æ¬ç¯éîæ´¿éºã¨î«é©ï¿½ 375 target[key] = clone(source[key], true); 376 } 377 } 378 } 379 380 return target; 381 } 382 383 /** 384 * @param {Array} targetAndSources The first item is target, and the rests are source. 385 * @param {boolean} [overwrite=false] 386 * @return {*} target 387 */ 388 function mergeAll(targetAndSources, overwrite) { 389 var result = targetAndSources[0]; 390 for (var i = 1, len = targetAndSources.length; i < len; i++) { 391 result = merge(result, targetAndSources[i], overwrite); 392 } 393 return result; 394 } 395 396 /** 397 * @param {*} target 398 * @param {*} source 399 * @memberOf module:zrender/core/util 400 */ 401 function extend(target, source) { 402 for (var key in source) { 403 if (source.hasOwnProperty(key)) { 404 target[key] = source[key]; 405 } 406 } 407 return target; 408 } 409 410 /** 411 * @param {*} target 412 * @param {*} source 413 * @param {boolean} [overlay=false] 414 * @memberOf module:zrender/core/util 415 */ 416 function defaults(target, source, overlay) { 417 for (var key in source) { 418 if (source.hasOwnProperty(key) 419 && (overlay ? source[key] != null : target[key] == null) 420 ) { 421 target[key] = source[key]; 422 } 423 } 424 return target; 425 } 426 427 var createCanvas = function () { 428 return methods.createCanvas(); 429 }; 430 431 methods.createCanvas = function () { 432 return document.createElement('canvas'); 433 }; 434 435// FIXME 436 var _ctx; 437 438 function getContext() { 439 if (!_ctx) { 440 // Use util.createCanvas instead of createCanvas
vendor: 11,173 bytes, lines 441-858
441 // because createCanvas may be overwritten in different environment 442 _ctx = createCanvas().getContext('2d'); 443 } 444 return _ctx; 445 } 446 447 /** 448 * éã¨îéæ®ç²æ¶î åç»±çµæ®index 449 * @memberOf module:zrender/core/util 450 */ 451 function indexOf(array, value) { 452 if (array) { 453 if (array.indexOf) { 454 return array.indexOf(value); 455 } 456 for (var i = 0, len = array.length; i < len; i++) { 457 if (array[i] === value) { 458 return i; 459 } 460 } 461 } 462 return -1; 463 } 464 465 /** 466 * éå¯â¬çµè¢«ç¼Ñå£éå´é´ 467 * 468 * @memberOf module:zrender/core/util 469 * @param {Function} clazz å©§æ®è¢« 470 * @param {Function} baseClazz é©è¹è¢« 471 */ 472 function inherits(clazz, baseClazz) { 473 var clazzPrototype = clazz.prototype; 474 function F() {} 475 F.prototype = baseClazz.prototype; 476 clazz.prototype = new F(); 477 478 for (var prop in clazzPrototype) { 479 clazz.prototype[prop] = clazzPrototype[prop]; 480 } 481 clazz.prototype.constructor = clazz; 482 clazz.superClass = baseClazz; 483 } 484 485 /** 486 * @memberOf module:zrender/core/util 487 * @param {Object|Function} target 488 * @param {Object|Function} sorce 489 * @param {boolean} overlay 490 */ 491 function mixin(target, source, overlay) { 492 target = 'prototype' in target ? target.prototype : target; 493 source = 'prototype' in source ? source.prototype : source; 494 495 defaults(target, source, overlay); 496 } 497 498 /** 499 * Consider typed array. 500 * @param {Array|TypedArray} data 501 */ 502 function isArrayLike(data) { 503 if (! data) { 504 return; 505 } 506 if (typeof data == 'string') { 507 return false; 508 } 509 return typeof data.length == 'number'; 510 } 511 512 /** 513 * éæ®ç²é´æ §î®çï¿ äº¶é� 514 * @memberOf module:zrender/core/util 515 * @param {Object|Array} obj 516 * @param {Function} cb 517 * @param {*} [context] 518 */ 519 function each$1(obj, cb, context) { 520 if (!(obj && cb)) { 521 return; 522 } 523 if (obj.forEach && obj.forEach === nativeForEach) { 524 obj.forEach(cb, context); 525 } 526 else if (obj.length === +obj.length) { 527 for (var i = 0, len = obj.length; i < len; i++) { 528 cb.call(context, obj[i], i, obj); 529 } 530 } 531 else { 532 for (var key in obj) { 533 if (obj.hasOwnProperty(key)) { 534 cb.call(context, obj[key], key, obj); 535 } 536 } 537 } 538 } 539 540 /** 541 * éæ®ç²éç²ç 542 * @memberOf module:zrender/core/util 543 * @param {Array} obj 544 * @param {Function} cb 545 * @param {*} [context] 546 * @return {Array} 547 */ 548 function map(obj, cb, context) { 549 if (!(obj && cb)) { 550 return; 551 } 552 if (obj.map && obj.map === nativeMap) { 553 return obj.map(cb, context); 554 } 555 else { 556 var result = []; 557 for (var i = 0, len = obj.length; i < len; i++) { 558 result.push(cb.call(context, obj[i], i, obj)); 559 } 560 return result; 561 } 562 } 563 564 /** 565 * @memberOf module:zrender/core/util 566 * @param {Array} obj 567 * @param {Function} cb 568 * @param {Object} [memo] 569 * @param {*} [context] 570 * @return {Array} 571 */ 572 function reduce(obj, cb, memo, context) { 573 if (!(obj && cb)) { 574 return; 575 } 576 if (obj.reduce && obj.reduce === nativeReduce) { 577 return obj.reduce(cb, memo, context); 578 } 579 else { 580 for (var i = 0, len = obj.length; i < len; i++) { 581 memo = cb.call(context, memo, obj[i], i, obj); 582 } 583 return memo; 584 } 585 } 586 587 /** 588 * éæ®ç²æ©å¨æ¤ 589 * @memberOf module:zrender/core/util 590 * @param {Array} obj 591 * @param {Function} cb 592 * @param {*} [context] 593 * @return {Array} 594 */ 595 function filter(obj, cb, context) { 596 if (!(obj && cb)) { 597 return; 598 } 599 if (obj.filter && obj.filter === nativeFilter) { 600 return obj.filter(cb, context); 601 } 602 else { 603 var result = []; 604 for (var i = 0, len = obj.length; i < len; i++) { 605 if (cb.call(context, obj[i], i, obj)) { 606 result.push(obj[i]); 607 } 608 } 609 return result; 610 } 611 } 612 613 /** 614 * éæ®ç²æ¤¤è§ç ¡éµï¿½ 615 * @memberOf module:zrender/core/util 616 * @param {Array} obj 617 * @param {Function} cb 618 * @param {*} [context] 619 * @return {*} 620 */ 621 function find(obj, cb, context) { 622 if (!(obj && cb)) { 623 return; 624 } 625 for (var i = 0, len = obj.length; i < len; i++) { 626 if (cb.call(context, obj[i], i, obj)) { 627 return obj[i]; 628 } 629 } 630 } 631 632 /** 633 * @memberOf module:zrender/core/util 634 * @param {Function} func 635 * @param {*} context 636 * @return {Function} 637 */ 638 function bind(func, context) { 639 var args = nativeSlice.call(arguments, 2); 640 return function () { 641 return func.apply(context, args.concat(nativeSlice.call(arguments))); 642 }; 643 } 644 645 /** 646 * @memberOf module:zrender/core/util 647 * @param {Function} func 648 * @return {Function} 649 */ 650 function curry(func) { 651 var args = nativeSlice.call(arguments, 1); 652 return function () { 653 return func.apply(this, args.concat(nativeSlice.call(arguments))); 654 }; 655 } 656 657 /** 658 * @memberOf module:zrender/core/util 659 * @param {*} value 660 * @return {boolean} 661 */ 662 function isArray(value) { 663 return objToString.call(value) === '[object Array]'; 664 } 665 666 /** 667 * @memberOf module:zrender/core/util 668 * @param {*} value 669 * @return {boolean} 670 */ 671 function isFunction$1(value) { 672 return typeof value === 'function'; 673 } 674 675 /** 676 * @memberOf module:zrender/core/util 677 * @param {*} value 678 * @return {boolean} 679 */ 680 function isString(value) { 681 return objToString.call(value) === '[object String]'; 682 } 683 684 /** 685 * @memberOf module:zrender/core/util 686 * @param {*} value 687 * @return {boolean} 688 */ 689 function isObject$1(value) { 690 // Avoid a V8 JIT bug in Chrome 19-20. 691 // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. 692 var type = typeof value; 693 return type === 'function' || (!!value && type == 'object'); 694 } 695 696 /** 697 * @memberOf module:zrender/core/util 698 * @param {*} value 699 * @return {boolean} 700 */ 701 function isBuiltInObject(value) { 702 return !!BUILTIN_OBJECT[objToString.call(value)]; 703 } 704 705 /** 706 * @memberOf module:zrender/core/util 707 * @param {*} value 708 * @return {boolean} 709 */ 710 function isTypedArray(value) { 711 return !!TYPED_ARRAY[objToString.call(value)]; 712 } 713 714 /** 715 * @memberOf module:zrender/core/util 716 * @param {*} value 717 * @return {boolean} 718 */ 719 function isDom(value) { 720 return typeof value === 'object' 721 && typeof value.nodeType === 'number' 722 && typeof value.ownerDocument === 'object'; 723 } 724 725 /** 726 * Whether is exactly NaN. Notice isNaN('a') returns true. 727 * @param {*} value 728 * @return {boolean} 729 */ 730 function eqNaN(value) { 731 return value !== value; 732 } 733 734 /** 735 * If value1 is not null, then return value1, otherwise judget rest of values. 736 * Low performance. 737 * @memberOf module:zrender/core/util 738 * @return {*} Final value 739 */ 740 function retrieve(values) { 741 for (var i = 0, len = arguments.length; i < len; i++) { 742 if (arguments[i] != null) { 743 return arguments[i]; 744 } 745 } 746 } 747 748 function retrieve2(value0, value1) { 749 return value0 != null 750 ? value0 751 : value1; 752 } 753 754 function retrieve3(value0, value1, value2) { 755 return value0 != null 756 ? value0 757 : value1 != null 758 ? value1 759 : value2; 760 } 761 762 /** 763 * @memberOf module:zrender/core/util 764 * @param {Array} arr 765 * @param {number} startIndex 766 * @param {number} endIndex 767 * @return {Array} 768 */ 769 function slice() { 770 return Function.call.apply(nativeSlice, arguments); 771 } 772 773 /** 774 * Normalize css liked array configuration 775 * e.g. 776 * 3 => [3, 3, 3, 3] 777 * [4, 2] => [4, 2, 4, 2] 778 * [4, 3, 2] => [4, 3, 2, 3] 779 * @param {number|Array.<number>} val 780 * @return {Array.<number>} 781 */ 782 function normalizeCssArray(val) { 783 if (typeof (val) === 'number') { 784 return [val, val, val, val]; 785 } 786 var len = val.length; 787 if (len === 2) { 788 // vertical | horizontal 789 return [val[0], val[1], val[0], val[1]]; 790 } 791 else if (len === 3) { 792 // top | horizontal | bottom 793 return [val[0], val[1], val[2], val[1]]; 794 } 795 return val; 796 } 797 798 /** 799 * @memberOf module:zrender/core/util 800 * @param {boolean} condition 801 * @param {string} message 802 */ 803 function assert$1(condition, message) { 804 if (!condition) { 805 throw new Error(message); 806 } 807 } 808 809 /** 810 * @memberOf module:zrender/core/util 811 * @param {string} str string to be trimed 812 * @return {string} trimed string 813 */ 814 function trim(str) { 815 if (str == null) { 816 return null; 817 } 818 else if (typeof str.trim === 'function') { 819 return str.trim(); 820 } 821 else { 822 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); 823 } 824 } 825 826 var primitiveKey = '__ec_primitive__'; 827 /** 828 * Set an object as primitive to be ignored traversing children in clone or merge 829 */ 830 function setAsPrimitive(obj) { 831 obj[primitiveKey] = true; 832 } 833 834 function isPrimitive(obj) { 835 return obj[primitiveKey]; 836 } 837 838 /** 839 * @constructor 840 * @param {Object} obj Only apply `ownProperty`. 841 */ 842 function HashMap(obj) { 843 var isArr = isArray(obj); 844 var thisMap = this; 845 846 (obj instanceof HashMap) 847 ? obj.each(visit) 848 : (obj && each$1(obj, visit)); 849 850 function visit(value, key) { 851 isArr ? thisMap.set(value, key) : thisMap.set(key, value); 852 } 853 } 854 855// Add prefix to avoid conflict with Object.prototype. 856 857 HashMap.prototype = { 858 constructor: HashMap,
859 // Do not provide `has` method to avoid defining what is `has`. 860 // (We usually treat `null` and `undefined` as the same, different 861 // from ES6 Map). 862 get: function (key) { 863 return this.hasOwnProperty(key) ? this[key] : null; 864 }, 865 set: function (key, value) { 866 // Comparing with invocation chaining, `return value` is more commonly 867 // used in this case: `var someVal = map.set('a', genVal());` 868 return (this[key] = value); 869 }, 870 // Although util.each can be performed on this hashMap directly, user 871 // should not use the exposed keys, who are prefixed. 872 each: function (cb, context) { 873 context !== void 0 && (cb = bind(cb, context)); 874 for (var key in this) { 875 this.hasOwnProperty(key) && cb(this[key], key); 876 } 877 }, 878 // Do not use this method if performance sensitive. 879 removeKey: function (key) { 880 delete this[key]; 881 } 882 }; 883 884 function createHashMap(obj) { 885 return new HashMap(obj); 886 } 887 888 function concatArray(a, b) { 889 var newArray = new a.constructor(a.length + b.length); 890 for (var i = 0; i < a.length; i++) { 891 newArray[i] = a[i]; 892 } 893 var offset = a.length; 894 for (i = 0; i < b.length; i++) { 895 newArray[i + offset] = b[i]; 896 } 897 return newArray; 898 } 899 900 901 function noop() {} 902 903 904 var zrUtil = (Object.freeze || Object)({ 905 $override: $override, 906 clone: clone, 907 merge: merge, 908 mergeAll: mergeAll, 909 extend: extend, 910 defaults: defaults, 911 createCanvas: createCanvas, 912 getContext: getContext, 913 indexOf: indexOf, 914 inherits: inherits, 915 mixin: mixin, 916 isArrayLike: isArrayLike, 917 each: each$1, 918 map: map, 919 reduce: reduce, 920 filter: filter, 921 find: find, 922 bind: bind, 923 curry: curry, 924 isArray: isArray, 925 isFunction: isFunction$1, 926 isString: isString, 927 isObject: isObject$1, 928 isBuiltInObject: isBuiltInObject, 929 isTypedArray: isTypedArray, 930 isDom: isDom, 931 eqNaN: eqNaN, 932 retrieve: retrieve, 933 retrieve2: retrieve2, 934 retrieve3: retrieve3, 935 slice: slice, 936 normalizeCssArray: normalizeCssArray, 937 assert: assert$1, 938 trim: trim, 939 setAsPrimitive: setAsPrimitive, 940 isPrimitive: isPrimitive, 941 createHashMap: createHashMap, 942 concatArray: concatArray, 943 noop: noop 944 }); 945 946 var ArrayCtor = typeof Float32Array === 'undefined' 947 ? Array 948 : Float32Array; 949 950 /** 951 * éæ¶ç¼æ¶â¬æ¶îæé²ï¿½ 952 * @param {number} [x=0] 953 * @param {number} [y=0] 954 * @return {Vector2} 955 */ 956 function create(x, y) { 957 var out = new ArrayCtor(2); 958 if (x == null) { 959 x = 0; 960 } 961 if (y == null) { 962 y = 0; 963 } 964 out[0] = x; 965 out[1] = y; 966 return out; 967 } 968 969 /** 970 * æ¾¶å¶ å鿦åºéçåµ 971 * @param {Vector2} out 972 * @param {Vector2} v 973 * @return {Vector2} 974 */ 975 function copy(out, v) { 976 out[0] = v[0]; 977 out[1] = v[1]; 978 return out; 979 } 980 981 /** 982 * éå¬®æ®æ¶â¬æ¶îæé²ï¿½ 983 * @param {Vector2} v 984 * @return {Vector2} 985 */ 986 function clone$1(v) { 987 var out = new ArrayCtor(2); 988 out[0] = v[0]; 989 out[1] = v[1]; 990 return out; 991 } 992 993 /** 994 * çå§ç鿦åºé¨åªè¢±æ¶îã 995 * @param {Vector2} out 996 * @param {number} a 997 * @param {number} b 998 * @return {Vector2} ç¼æ´ç 999 */ 1000 function set(out, a, b) { 1001 out[0] = a; 1002 out[1] = b; 1003 return out; 1004 } 1005 1006 /** 1007 * 鿦åºé©ç¨¿å§ 1008 * @param {Vector2} out 1009 * @param {Vector2} v1 1010 * @param {Vector2} v2 1011 */ 1012 function add(out, v1, v2) { 1013 out[0] = v1[0] + v2[0]; 1014 out[1] = v1[1] + v2[1]; 1015 return out; 1016 } 1017 1018 /** 1019 * 鿦åºç¼âæé庣æµé� 1020 * @param {Vector2} out 1021 * @param {Vector2} v1 1022 * @param {Vector2} v2 1023 * @param {number} a 1024 */ 1025 function scaleAndAdd(out, v1, v2, a) { 1026 out[0] = v1[0] + v2[0] * a; 1027 out[1] = v1[1] + v2[1] * a; 1028 return out; 1029 } 1030 1031 /** 1032 * 鿦åºé©ç¨¿åº 1033 * @param {Vector2} out 1034 * @param {Vector2} v1 1035 * @param {Vector2} v2 1036 */ 1037 function sub(out, v1, v2) { 1038 out[0] = v1[0] - v2[0]; 1039 out[1] = v1[1] - v2[1]; 1040 return out; 1041 } 1042 1043 /** 1044 * 鿦åºéå®å®³ 1045 * @param {Vector2} v 1046 * @return {number} 1047 */ 1048 function len(v) { 1049 return Math.sqrt(lenSquare(v));
vendor: 4,461 bytes, lines 1050-1242
1050 } 1051 var length = len; // jshint ignore:line 1052 1053 /** 1054 * 鿦åºéå®å®³éªè«æ 1055 * @param {Vector2} v 1056 * @return {number} 1057 */ 1058 function lenSquare(v) { 1059 return v[0] * v[0] + v[1] * v[1]; 1060 } 1061 var lengthSquare = lenSquare; 1062 1063 /** 1064 * éæ¦åºæ¶æ¨»ç¡¶ 1065 * @param {Vector2} out 1066 * @param {Vector2} v1 1067 * @param {Vector2} v2 1068 */ 1069 function mul(out, v1, v2) { 1070 out[0] = v1[0] * v2[0]; 1071 out[1] = v1[1] * v2[1]; 1072 return out; 1073 } 1074 1075 /** 1076 * 鿦åºéãç¡¶ 1077 * @param {Vector2} out 1078 * @param {Vector2} v1 1079 * @param {Vector2} v2 1080 */ 1081 function div(out, v1, v2) { 1082 out[0] = v1[0] / v2[0]; 1083 out[1] = v1[1] / v2[1]; 1084 return out; 1085 } 1086 1087 /** 1088 * 鿦åºééç®» 1089 * @param {Vector2} v1 1090 * @param {Vector2} v2 1091 * @return {number} 1092 */ 1093 function dot(v1, v2) { 1094 return v1[0] * v2[0] + v1[1] * v2[1]; 1095 } 1096 1097 /** 1098 * 鿦åºç¼âæ 1099 * @param {Vector2} out 1100 * @param {Vector2} v 1101 * @param {number} s 1102 */ 1103 function scale(out, v, s) { 1104 out[0] = v[0] * s; 1105 out[1] = v[1] * s; 1106 return out; 1107 } 1108 1109 /** 1110 * 鿦åºè¤°æç«´é� 1111 * @param {Vector2} out 1112 * @param {Vector2} v 1113 */ 1114 function normalize(out, v) { 1115 var d = len(v); 1116 if (d === 0) { 1117 out[0] = 0; 1118 out[1] = 0; 1119 } 1120 else { 1121 out[0] = v[0] / d; 1122 out[1] = v[1] / d; 1123 } 1124 return out; 1125 } 1126 1127 /** 1128 * çï¼ç»éæ¦åºéç£çªç»ï¿½ 1129 * @param {Vector2} v1 1130 * @param {Vector2} v2 1131 * @return {number} 1132 */ 1133 function distance(v1, v2) { 1134 return Math.sqrt( 1135 (v1[0] - v2[0]) * (v1[0] - v2[0]) 1136 + (v1[1] - v2[1]) * (v1[1] - v2[1]) 1137 ); 1138 } 1139 var dist = distance; 1140 1141 /** 1142 * 鿦åºçºæ¿îéªè«æ 1143 * @param {Vector2} v1 1144 * @param {Vector2} v2 1145 * @return {number} 1146 */ 1147 function distanceSquare(v1, v2) { 1148 return (v1[0] - v2[0]) * (v1[0] - v2[0]) 1149 + (v1[1] - v2[1]) * (v1[1] - v2[1]); 1150 } 1151 var distSquare = distanceSquare; 1152 1153 /** 1154 * å§¹åç¤éæ¦åº 1155 * @param {Vector2} out 1156 * @param {Vector2} v 1157 */ 1158 function negate(out, v) { 1159 out[0] = -v[0]; 1160 out[1] = -v[1]; 1161 return out; 1162 } 1163 1164 /** 1165 * 黿â¬é´è¢±æ¶îå£ 1166 * @param {Vector2} out 1167 * @param {Vector2} v1 1168 * @param {Vector2} v2 1169 * @param {number} t 1170 */ 1171 function lerp(out, v1, v2, t) { 1172 out[0] = v1[0] + t * (v2[0] - v1[0]); 1173 out[1] = v1[1] + t * (v2[1] - v1[1]); 1174 return out; 1175 } 1176 1177 /** 1178 * éâ æ¨å®¸ï¸¿ç®»éæ¦åº 1179 * @param {Vector2} out 1180 * @param {Vector2} v 1181 * @param {Vector2} m 1182 */ 1183 function applyTransform(out, v, m) { 1184 var x = v[0]; 1185 var y = v[1]; 1186 out[0] = m[0] * x + m[2] * y + m[4]; 1187 out[1] = m[1] * x + m[3] * y + m[5]; 1188 return out; 1189 } 1190 1191 /** 1192 * å§¹å袱æ¶îæé²å¿æ¸¶çå¿â¬ï¿½ 1193 * @param {Vector2} out 1194 * @param {Vector2} v1 1195 * @param {Vector2} v2 1196 */ 1197 function min(out, v1, v2) { 1198 out[0] = Math.min(v1[0], v2[0]); 1199 out[1] = Math.min(v1[1], v2[1]); 1200 return out; 1201 } 1202 1203 /** 1204 * å§¹å袱æ¶îæé²å¿æ¸¶æ¾¶Ñâ¬ï¿½ 1205 * @param {Vector2} out 1206 * @param {Vector2} v1 1207 * @param {Vector2} v2 1208 */ 1209 function max(out, v1, v2) { 1210 out[0] = Math.max(v1[0], v2[0]); 1211 out[1] = Math.max(v1[1], v2[1]); 1212 return out; 1213 } 1214 1215 1216 var vector = (Object.freeze || Object)({ 1217 create: create, 1218 copy: copy, 1219 clone: clone$1, 1220 set: set, 1221 add: add, 1222 scaleAndAdd: scaleAndAdd, 1223 sub: sub, 1224 len: len, 1225 length: length, 1226 lenSquare: lenSquare, 1227 lengthSquare: lengthSquare, 1228 mul: mul, 1229 div: div, 1230 dot: dot, 1231 scale: scale, 1232 normalize: normalize, 1233 distance: distance, 1234 dist: dist, 1235 distanceSquare: distanceSquare, 1236 distSquare: distSquare, 1237 negate: negate, 1238 lerp: lerp, 1239 applyTransform: applyTransform, 1240 min: min, 1241 max: max 1242 });
1243 1244// TODO Draggable for group 1245// FIXME Draggable on element which has parent rotation or scale 1246 function Draggable() { 1247 1248 this.on('mousedown', this._dragStart, this); 1249 this.on('mousemove', this._drag, this); 1250 this.on('mouseup', this._dragEnd, this); 1251 this.on('globalout', this._dragEnd, this); 1252 // this._dropTarget = null; 1253 // this._draggingTarget = null; 1254 1255 // this._x = 0; 1256 // this._y = 0; 1257 } 1258 1259 Draggable.prototype = { 1260 1261 constructor: Draggable, 1262 1263 _dragStart: function (e) { 1264 var draggingTarget = e.target; 1265 if (draggingTarget && draggingTarget.draggable) { 1266 this._draggingTarget = draggingTarget; 1267 draggingTarget.dragging = true; 1268 this._x = e.offsetX; 1269 this._y = e.offsetY; 1270 1271 this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event); 1272 } 1273 }, 1274 1275 _drag: function (e) { 1276 var draggingTarget = this._draggingTarget; 1277 if (draggingTarget) { 1278 1279 var x = e.offsetX; 1280 var y = e.offsetY; 1281 1282 var dx = x - this._x; 1283 var dy = y - this._y; 1284 this._x = x; 1285 this._y = y; 1286 1287 draggingTarget.drift(dx, dy, e); 1288 this.dispatchToElement(param(draggingTarget, e), 'drag', e.event); 1289 1290 var dropTarget = this.findHover(x, y, draggingTarget).target; 1291 var lastDropTarget = this._dropTarget; 1292 this._dropTarget = dropTarget; 1293 1294 if (draggingTarget !== dropTarget) { 1295 if (lastDropTarget && dropTarget !== lastDropTarget) { 1296 this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event); 1297 } 1298 if (dropTarget && dropTarget !== lastDropTarget) { 1299 this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event); 1300 } 1301 } 1302 } 1303 }, 1304 1305 _dragEnd: function (e) { 1306 var draggingTarget = this._draggingTarget; 1307 1308 if (draggingTarget) { 1309 draggingTarget.dragging = false; 1310 } 1311 1312 this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event); 1313 1314 if (this._dropTarget) { 1315 this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event); 1316 } 1317 1318 this._draggingTarget = null; 1319 this._dropTarget = null; 1320 } 1321 1322 }; 1323 1324 function param(target, e) { 1325 return {target: target, topTarget: e && e.topTarget}; 1326 } 1327 1328 /** 1329 * æµå¬©æ¬¢éµâç 1330 * @module zrender/mixin/Eventful 1331 * @author Kener (@Kener-鿥å², [email protected]) 1332 * pissang (https://www.github.com/pissang) 1333 */ 1334 1335 var arrySlice = Array.prototype.slice; 1336 1337 /** 1338 * æµå¬©æ¬¢éåå½é£ï¿½ 1339 * @alias module:zrender/mixin/Eventful 1340 * @constructor 1341 */ 1342 var Eventful = function () { 1343 this._$handlers = {}; 1344 }; 1345 1346 Eventful.prototype = { 1347 1348 constructor: Eventful, 1349 1350 /** 1351 * éæî¼çï¹å½ç¼æç¾é宼riggeré庨æ¢å§£ï¿½ 1352 * 1353 * @param {string} event æµå¬©æ¬¢é� 1354 * @param {Function} handler éå¶ ç°²éè¥æ 1355 * @param {Object} context 1356 */ 1357 one: function (event, handler, context) { 1358 var _h = this._$handlers; 1359 1360 if (!handler || !event) { 1361 return this; 1362 } 1363 1364 if (!_h[event]) { 1365 _h[event] = []; 1366 } 1367 1368 for (var i = 0; i < _h[event].length; i++) { 1369 if (_h[event][i].h === handler) { 1370 return this; 1371 } 1372 } 1373 1374 _h[event].push({ 1375 h: handler, 1376 one: true, 1377 ctx: context || this 1378 }); 1379 1380 return this; 1381 }, 1382 1383 /** 1384 * ç¼æç¾æµå¬©æ¬¢ 1385 * @param {string} event æµå¬©æ¬¢é� 1386 * @param {Function} handler æµå¬©æ¬¢æ¾¶åæéè¥æ 1387 * @param {Object} [context] 1388 */ 1389 on: function (event, handler, context) { 1390 var _h = this._$handlers; 1391 1392 if (!handler || !event) { 1393 return this; 1394 } 1395 1396 if (!_h[event]) { 1397 _h[event] = []; 1398 } 1399 1400 for (var i = 0; i < _h[event].length; i++) { 1401 if (_h[event][i].h === handler) { 1402 return this; 1403 } 1404 } 1405 1406 _h[event].push({ 1407 h: handler, 1408 one: false, 1409 ctx: context || this 1410 }); 1411 1412 return this; 1413 }, 1414 1415 /** 1416 * éîæç¼æç¾æµåç°¨æµ ï¿½ 1417 * @param {string} event 1418 * @return {boolean} 1419 */ 1420 isSilent: function (event) { 1421 var _h = this._$handlers; 1422 return _h[event] && _h[event].length; 1423 }, 1424 1425 /** 1426 * çï½ç²¦æµå¬©æ¬¢ 1427 * @param {string} event æµå¬©æ¬¢é� 1428 * @param {Function} [handler] æµå¬©æ¬¢æ¾¶åæéè¥æ 1429 */ 1430 off: function (event, handler) { 1431 var _h = this._$handlers; 1432 1433 if (!event) { 1434 this._$handlers = {}; 1435 return this; 1436 } 1437 1438 if (handler) { 1439 if (_h[event]) { 1440 var newList = []; 1441 for (var i = 0, l = _h[event].length; i < l; i++) { 1442 if (_h[event][i]['h'] != handler) { 1443 newList.push(_h[event][i]); 1444 } 1445 } 1446 _h[event] = newList; 1447 } 1448 1449 if (_h[event] && _h[event].length === 0) { 1450 delete _h[event]; 1451 } 1452 } 1453 else { 1454 delete _h[event]; 1455 } 1456 1457 return this; 1458 }, 1459 1460 /** 1461 * æµå¬©æ¬¢éåå½ 1462 * 1463 * @param {string} type æµå¬©æ¬¢ç»«è¯²ç· 1464 */ 1465 trigger: function (type) { 1466 if (this._$handlers[type]) { 1467 var args = arguments; 1468 var argLen = args.length; 1469 1470 if (argLen > 3) { 1471 args = arrySlice.call(args, 1); 1472 } 1473 1474 var _h = this._$handlers[type]; 1475 var len = _h.length; 1476 for (var i = 0; i < len;) { 1477 // Optimize advise from backbone 1478 switch (argLen) { 1479 case 1: 1480 _h[i]['h'].call(_h[i]['ctx']); 1481 break; 1482 case 2: 1483 _h[i]['h'].call(_h[i]['ctx'], args[1]); 1484 break; 1485 case 3: 1486 _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]); 1487 break; 1488 default: 1489 // have more than 2 given arguments 1490 _h[i]['h'].apply(_h[i]['ctx'], args); 1491 break; 1492 } 1493 1494 if (_h[i]['one']) { 1495 _h.splice(i, 1); 1496 len--; 1497 } 1498 else { 1499 i++; 1500 } 1501 } 1502 } 1503 1504 return this; 1505 }, 1506 1507 /** 1508 * ç¯ï¸½æ¹contexté¨åªç°¨æµ è·ºåé�, éâ¬éåºç«´æ¶î
1508弬éçæ§¸æµå¬©æ¬¢é¥ç¶çé¨å¢ontext 1509 * @param {string} type æµå¬©æ¬¢ç»«è¯²ç· 1510 */ 1511 triggerWithContext: function (type) { 1512 if (this._$handlers[type]) { 1513 var args = arguments; 1514 var argLen = args.length; 1515 1516 if (argLen > 4) { 1517 args = arrySlice.call(args, 1, args.length - 1); 1518 } 1519 var ctx = args[args.length - 1]; 1520 1521 var _h = this._$handlers[type]; 1522 var len = _h.length; 1523 for (var i = 0; i < len;) { 1524 // Optimize advise from backbone 1525 switch (argLen) { 1526 case 1: 1527 _h[i]['h'].call(ctx); 1528 break; 1529 case 2: 1530 _h[i]['h'].call(ctx, args[1]); 1531 break; 1532 case 3: 1533 _h[i]['h'].call(ctx, args[1], args[2]); 1534 break; 1535 default: 1536 // have more than 2 given arguments 1537 _h[i]['h'].apply(ctx, args); 1538 break; 1539 } 1540 1541 if (_h[i]['one']) { 1542 _h.splice(i, 1); 1543 len--; 1544 } 1545 else { 1546 i++; 1547 } 1548 } 1549 } 1550 1551 return this; 1552 } 1553 }; 1554 1555 var SILENT = 'silent'; 1556 1557 function makeEventPacket(eveType, targetInfo, event) { 1558 return { 1559 type: eveType, 1560 event: event, 1561 // target can only be an element that is not silent. 1562 target: targetInfo.target, 1563 // topTarget can be a silent element. 1564 topTarget: targetInfo.topTarget, 1565 cancelBubble: false, 1566 offsetX: event.zrX, 1567 offsetY: event.zrY, 1568 gestureEvent: event.gestureEvent, 1569 pinchX: event.pinchX, 1570 pinchY: event.pinchY, 1571 pinchScale: event.pinchScale, 1572 wheelDelta: event.zrDelta, 1573 zrByTouch: event.zrByTouch, 1574 which: event.which 1575 }; 1576 } 1577 1578 function EmptyProxy () {} 1579 EmptyProxy.prototype.dispose = function () {}; 1580 1581 var handlerNames = [ 1582 'click', 'dblclick', 'mousewheel', 'mouseout', 1583 'mouseup', 'mousedown', 'mousemove', 'contextmenu' 1584 ]; 1585 /** 1586 * @alias module:zrender/Handler 1587 * @constructor 1588 * @extends module:zrender/mixin/Eventful 1589 * @param {module:zrender/Storage} storage Storage instance. 1590 * @param {module:zrender/Painter} painter Painter instance. 1591 * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance. 1592 * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()). 1593 */ 1594 var Handler = function(storage, painter, proxy, painterRoot) { 1595 Eventful.call(this); 1596 1597 this.storage = storage; 1598 1599 this.painter = painter; 1600 1601 this.painterRoot = painterRoot; 1602 1603 proxy = proxy || new EmptyProxy(); 1604 1605 /** 1606 * Proxy of event. can be Dom, WebGLSurface, etc. 1607 */ 1608 this.proxy = null; 1609 1610 /** 1611 * {target, topTarget, x, y} 1612 * @private 1613 * @type {Object} 1614 */ 1615 this._hovered = {}; 1616 1617 /** 1618 * @private 1619 * @type {Date} 1620 */ 1621 this._lastTouchMoment; 1622 1623 /** 1624 * @private 1625 * @type {number} 1626 */ 1627 this._lastX; 1628 1629 /** 1630 * @private 1631 * @type {number} 1632 */ 1633 this._lastY; 1634 1635 1636 Draggable.call(this); 1637 1638 this.setHandlerProxy(proxy); 1639 }; 1640 1641 Handler.prototype = { 1642 1643 constructor: Handler, 1644 1645 setHandlerProxy: function (proxy) { 1646 if (this.proxy) { 1647 this.proxy.dispose(); 1648 } 1649 1650 if (proxy) { 1651 each$1(handlerNames, function (name) { 1652 proxy.on && proxy.on(name, this[name], this); 1653 }, this); 1654 // Attach handler 1655 proxy.handler = this; 1656 } 1657 this.proxy = proxy; 1658 }, 1659 1660 mousemove: function (event) { 1661 var x = event.zrX; 1662 var y = event.zrY; 1663 1664 var lastHovered = this._hovered; 1665 var lastHoveredTarget = lastHovered.target; 1666 1667 // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call
1668 // (like 'setOption' or 'dispatchAction') in event handlers, we should find 1669 // lastHovered again here. Otherwise 'mouseout' can not be triggered normally. 1670 // See #6198. 1671 if (lastHoveredTarget && !lastHoveredTarget.__zr) { 1672 lastHovered = this.findHover(lastHovered.x, lastHovered.y); 1673 lastHoveredTarget = lastHovered.target; 1674 } 1675 1676 var hovered = this._hovered = this.findHover(x, y); 1677 var hoveredTarget = hovered.target; 1678 1679 var proxy = this.proxy; 1680 proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default'); 1681 1682 // Mouse out on previous hovered element 1683 if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) { 1684 this.dispatchToElement(lastHovered, 'mouseout', event); 1685 } 1686 1687 // Mouse moving on one element 1688 this.dispatchToElement(hovered, 'mousemove', event); 1689 1690 // Mouse over on a new element 1691 if (hoveredTarget && hoveredTarget !== lastHoveredTarget) { 1692 this.dispatchToElement(hovered, 'mouseover', event); 1693 } 1694 }, 1695 1696 mouseout: function (event) { 1697 this.dispatchToElement(this._hovered, 'mouseout', event); 1698 1699 // There might be some doms created by upper layer application 1700 // at the same level of painter.getViewportRoot() (e.g., tooltip 1701 // dom created by echarts), where 'globalout' event should not 1702 // be triggered when mouse enters these doms. (But 'mouseout' 1703 // should be triggered at the original hovered element as usual). 1704 var element = event.toElement || event.relatedTarget; 1705 var innerDom; 1706 do { 1707 element = element && element.parentNode; 1708 } 1709 while (element && element.nodeType != 9 && !( 1710 innerDom = element === this.painterRoot 1711 )); 1712 1713 !innerDom && this.trigger('globalout', {event: event}); 1714 }, 1715 1716 /** 1717 * Resize 1718 */ 1719 resize: function (event) { 1720 this._hovered = {}; 1721 }, 1722 1723 /** 1724 * Dispatch event 1725 * @param {string} eventName 1726 * @param {event=} eventArgs 1727 */ 1728 dispatch: function (eventName, eventArgs) { 1729 var handler = this[eventName]; 1730 handler && handler.call(this, eventArgs); 1731 }, 1732 1733 /** 1734 * Dispose 1735 */ 1736 dispose: function () { 1737 1738 this.proxy.dispose(); 1739 1740 this.storage = 1741 this.proxy = 1742 this.painter = null; 1743 }, 1744 1745 /** 1746 * çå§çæ¦æ¨¿î »é¨å¢ursor style 1747 * @param {string} [cursorStyle='default'] æ¸å¬ªî§ crosshair 1748 */ 1749 setCursorStyle: function (cursorStyle) { 1750 var proxy = this.proxy; 1751 proxy.setCursor && proxy.setCursor(cursorStyle); 1752 }, 1753 1754 /** 1755 * æµå¬©æ¬¢éå彿µ ï½æ 1756 * 1757 * @private 1758 * @param {Object} targetInfo {target, topTarget} é©î½ç£é¥æ§è°éåªç¤ 1759 * @param {string} eventName æµå¬©æ¬¢éå¶Ð 1760 * @param {Object} event æµå¬©æ¬¢çµç¡ è 1761 */ 1762 dispatchToElement: function (targetInfo, eventName, event) { 1763 targetInfo = targetInfo || {}; 1764 var el = targetInfo.target; 1765 if (el && el.silent) { 1766 return; 1767 } 1768 var eventHandler = 'on' + eventName; 1769 var eventPacket = makeEventPacket(eventName, targetInfo, event); 1770 1771 while (el) { 1772 el[eventHandler] 1773 && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket)); 1774 1775 el.trigger(eventName, eventPacket); 1776 1777 el = el.parent; 1778 1779 if (eventPacket.cancelBubble) { 1780 break; 1781 } 1782 } 1783 1784 if (!eventPacket.cancelBubble) { 1785 // éæåºéä¼´ã绾� zrender çµç¡ è 1786 this.trigger(eventName, eventPacket); 1787 // éå彿µå¬©æ¬¢éæ®æ¤é´ç¯åç¹æ°«ç®ç� 1788 // é¢ã¦åéå¤å½²é³è湪éã¥ç¬ click æµå¬©æ¬¢æ¶ï¿½ disposeé屾墿µ ã©æ¸¶ç佸å½éî ç¬ painter éîæç樺湪 1789 this.painter && this.painter.eachOtherLayer(function (layer) { 1790 if (typeof(layer[eventHandler]) == 'function') { 1791 layer[eventHandler].call(layer, eventPacket); 1792 } 1793 if (layer.trigger) { 1794 layer.trigger(eventName, eventPacket); 1795 } 1796 }); 1797 } 1798 }, 1799 1800 /** 1801 * @private 1802 * @param {number} x 1803 * @param {number} y 1804 * @param {module:zrender/graphic/Displayable} exclude 1805 * @return {model:zrender/Element} 1806 * @method 1807 */ 1808 findHover: function(x, y, exclude) { 1809 var list = this.storage.getDisplayList(); 1810 var out = {x: x, y: y}; 1811 1812 for (var i = list.length - 1; i >= 0 ; i--) { 1813 var hoverCheckResult; 1814 if (list[i] !== exclude 1815 // getDisplayList may include ignored item in VML mode 1816 && !list[i].ignore 1817 && (hoverCheckResult = isHover(list[i], x, y)) 1818 ) { 1819 !out.topTarget && (out.topTarget = list[i]); 1820 if (hoverCheckResult !== SILENT) { 1821 out.target = list[i]; 1822 break; 1823 } 1824 } 1825 } 1826 1827 return out; 1828 } 1829 }; 1830 1831// Common handlers 1832 each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { 1833 Handler.prototype[name] = function (event) { 1834 // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover 1835 var hovered = this.findHover(event.zrX, event.zrY); 1836 var hoveredTarget = hovered.target; 1837 1838 if (name === 'mousedown') { 1839 this._downEl = hoveredTarget; 1840 this._downPoint = [event.zrX, event.zrY]; 1841 // In case click triggered before mouseup 1842 this._upEl = hoveredTarget; 1843 } 1844 else if (name === 'mouseup') { 1845 this._upEl = hoveredTarget; 1846 } 1847 else if (name === 'click') { 1848 if (this._downEl !== this._upEl 1849 // Original click event is triggered on the whole canvas element, 1850 // including the case that `mousedown` - `mousemove` - `mou
1850seup`, 1851 // which should be filtered, otherwise it will bring trouble to 1852 // pan and zoom. 1853 || !this._downPoint 1854 // Arbitrary value 1855 || dist(this._downPoint, [event.zrX, event.zrY]) > 4 1856 ) { 1857 return; 1858 } 1859 this._downPoint = null; 1860 } 1861 1862 this.dispatchToElement(hovered, name, event); 1863 }; 1864 }); 1865 1866 function isHover(displayable, x, y) { 1867 if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) { 1868 var el = displayable; 1869 var isSilent; 1870 while (el) { 1871 // If clipped by ancestor. 1872 // FIXME: If clipPath has neither stroke nor fill, 1873 // el.clipPath.contain(x, y) will always return false. 1874 if (el.clipPath && !el.clipPath.contain(x, y)) { 1875 return false; 1876 } 1877 if (el.silent) { 1878 isSilent = true; 1879 } 1880 el = el.parent; 1881 } 1882 return isSilent ? SILENT : true; 1883 } 1884 1885 return false; 1886 } 1887 1888 mixin(Handler, Eventful); 1889 mixin(Handler, Draggable); 1890 1891 /** 1892 * 3x2éâ æ¨é¿å¶ç¶ç»«ï¿½ 1893 * @exports zrender/tool/matrix 1894 */ 1895 1896 var ArrayCtor$1 = typeof Float32Array === 'undefined' 1897 ? Array 1898 : Float32Array; 1899 1900 /** 1901 * Create a identity matrix. 1902 * @return {Float32Array|Array.<number>} 1903 */ 1904 function create$1() { 1905 var out = new ArrayCtor$1(6); 1906 identity(out); 1907 1908 return out; 1909 } 1910 1911 /** 1912 * çå§çéâ æ¨æ¶åå´æµ£å¶ç ©é� 1913 * @param {Float32Array|Array.<number>} out 1914 */ 1915 function identity(out) { 1916 out[0] = 1; 1917 out[1] = 0; 1918 out[2] = 0; 1919 out[3] = 1; 1920 out[4] = 0; 1921 out[5] = 0; 1922 return out; 1923 } 1924 1925 /** 1926 * æ¾¶å¶ åéâ æ¨ 1927 * @param {Float32Array|Array.<number>} out 1928 * @param {Float32Array|Array.<number>} m 1929 */ 1930 function copy$1(out, m) { 1931 out[0] = m[0]; 1932 out[1] = m[1]; 1933 out[2] = m[2]; 1934 out[3] = m[3]; 1935 out[4] = m[4]; 1936 out[5] = m[5]; 1937 return out; 1938 } 1939 1940 /** 1941 * éâ æ¨é©éç®» 1942 * @param {Float32Array|Array.<number>} out 1943 * @param {Float32Array|Array.<number>} m1 1944 * @param {Float32Array|Array.<number>} m2 1945 */ 1946 function mul$1(out, m1, m2) { 1947 // Consider matrix.mul(m, m2, m); 1948 // where out is the same as m2. 1949 // So use temp variable to escape error. 1950 var out0 = m1[0] * m2[0] + m1[2] * m2[1]; 1951 var out1 = m1[1] * m2[0] + m1[3] * m2[1]; 1952 var out2 = m1[0] * m2[2] + m1[2] * m2[3]; 1953 var out3 = m1[1] * m2[2] + m1[3] * m2[3]; 1954 var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4]; 1955 var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5]; 1956 out[0] = out0; 1957 out[1] = out1; 1958 out[2] = out2; 1959 out[3] = out3; 1960 out[4] = out4; 1961 out[5] = out5; 1962 return out; 1963 } 1964 1965 /** 1966 * éªå´Ð©é樻崲 1967 * @param {Float32Array|Array.<number>} out 1968 * @param {Float32Array|Array.<number>} a 1969 * @param {Float32Array|Array.<number>} v 1970 */ 1971 function translate(out, a, v) { 1972 out[0] = a[0]; 1973 out[1] = a[1]; 1974 out[2] = a[2]; 1975 out[3] = a[3]; 1976 out[4] = a[4] + v[0]; 1977 out[5] = a[5] + v[1]; 1978 return out; 1979 } 1980 1981 /** 1982 * é嬿µé樻崲 1983 * @param {Float32Array|Array.<number>} out 1984 * @param {Float32Array|Array.<number>} a 1985 * @param {number} rad 1986 */ 1987 function rotate(out, a, rad) { 1988 var aa = a[0]; 1989 var ac = a[2]; 1990 var atx = a[4]; 1991 var ab = a[1]; 1992 var ad = a[3]; 1993 var aty = a[5]; 1994 var st = Math.sin(rad); 1995 var ct = Math.cos(rad); 1996 1997 out[0] = aa * ct + ab * st; 1998 out[1] = -aa * st + ab * ct; 1999 out[2] = ac * ct + ad * st; 2000 out[3] = -ac * st + ct * ad;
2001 out[4] = ct * atx + st * aty; 2002 out[5] = ct * aty - st * atx; 2003 return out; 2004 } 2005 2006 /** 2007 * ç¼âæé樻崲 2008 * @param {Float32Array|Array.<number>} out 2009 * @param {Float32Array|Array.<number>} a 2010 * @param {Float32Array|Array.<number>} v 2011 */ 2012 function scale$1(out, a, v) { 2013 var vx = v[0]; 2014 var vy = v[1]; 2015 out[0] = a[0] * vx; 2016 out[1] = a[1] * vy; 2017 out[2] = a[2] * vx; 2018 out[3] = a[3] * vy; 2019 out[4] = a[4] * vx; 2020 out[5] = a[5] * vy; 2021 return out; 2022 } 2023 2024 /** 2025 * å§¹åâ¬åç ©é� 2026 * @param {Float32Array|Array.<number>} out 2027 * @param {Float32Array|Array.<number>} a 2028 */ 2029 function invert(out, a) { 2030 2031 var aa = a[0]; 2032 var ac = a[2]; 2033 var atx = a[4]; 2034 var ab = a[1]; 2035 var ad = a[3]; 2036 var aty = a[5]; 2037 2038 var det = aa * ad - ab * ac; 2039 if (!det) { 2040 return null; 2041 } 2042 det = 1.0 / det; 2043 2044 out[0] = ad * det; 2045 out[1] = -ab * det; 2046 out[2] = -ac * det; 2047 out[3] = aa * det; 2048 out[4] = (ac * aty - ad * atx) * det; 2049 out[5] = (ab * atx - aa * aty) * det; 2050 return out; 2051 } 2052 2053 /** 2054 * Clone a new matrix. 2055 * @param {Float32Array|Array.<number>} a 2056 */ 2057 function clone$2(a) { 2058 var b = create$1(); 2059 copy$1(b, a); 2060 return b; 2061 } 2062 2063 var matrix = (Object.freeze || Object)({ 2064 create: create$1, 2065 identity: identity, 2066 copy: copy$1, 2067 mul: mul$1, 2068 translate: translate, 2069 rotate: rotate, 2070 scale: scale$1, 2071 invert: invert, 2072 clone: clone$2 2073 }); 2074 2075 /** 2076 * 黿ªç·µé樻崲éµâç 2077 * @module zrender/mixin/Transformable 2078 * @author pissang (https://www.github.com/pissang) 2079 */ 2080 2081 var mIdentity = identity; 2082 2083 var EPSILON = 5e-5; 2084 2085 function isNotAroundZero(val) { 2086 return val > EPSILON || val < -EPSILON; 2087 } 2088 2089 /** 2090 * @alias module:zrender/mixin/Transformable 2091 * @constructor 2092 */ 2093 var Transformable = function (opts) { 2094 opts = opts || {}; 2095 // If there are no given position, rotation, scale 2096 if (!opts.position) { 2097 /** 2098 * éªå´Ð© 2099 * @type {Array.<number>} 2100 * @default [0, 0] 2101 */ 2102 this.position = [0, 0]; 2103 } 2104 if (opts.rotation == null) { 2105 /** 2106 * é嬿µ 2107 * @type {Array.<number>} 2108 * @default 0 2109 */ 2110 this.rotation = 0; 2111 } 2112 if (!opts.scale) { 2113 /** 2114 * ç¼âæ 2115 * @type {Array.<number>} 2116 * @default [1, 1] 2117 */ 2118 this.scale = [1, 1]; 2119 } 2120 /** 2121 * é嬿µéå²ç¼éå§æ®éçºå£ 2122 * @type {Array.<number>} 2123 * @default null 2124 */ 2125 this.origin = this.origin || null; 2126 }; 2127 2128 var transformableProto = Transformable.prototype; 2129 transformableProto.transform = null; 2130 2131 /** 2132 * éãæéîæéâ¬çä½¹æ¹é§æç£é樻崲 2133 * æ¿¡åçé夿½éå§å½é¹ï¿½, éæ¬ç² position, rotation, scaleæµ ã¥å¼·éæ°å¦éåæ®transformçï¼ç»é鸿åé¬î¤æ®transforméâ æ¨ 2134 */ 2135 transformableProto.needLocalTransform = function () { 2136 return isNotAroundZero(this.rotation) 2137 || isNotAroundZero(this.position[0]) 2138 || isNotAroundZero(this.position[1]) 2139 || isNotAroundZero(this.scale[0] - 1) 2140 || isNotAroundZero(this.scale[1] - 1); 2141 }; 2142 2143 transformableProto.updateTransform = function () { 2144 var parent = this.parent; 2145 var parentHasTransform = parent && parent.transform; 2146 var needLocalTransform = this.needLocalTransform(); 2147 2148 var m = this.transform; 2149 if (!(needLocalTransform || parentHasTransform)) { 2150 m && mIdentity(m); 2151 return; 2152 } 2153 2154 m = m || create$1(); 2155 2156 if (needLocalTransform) { 2157 this.getLocalTransform(m); 2158 } 2159 else { 2160 mIdentity(m); 2161 } 2162 2163 // æ´ææ¤éæ°å¦éç°å½é¹ï¿½ 2164 if (parentHasTransform) { 2165 if (needLocalTransform) { 2166 mul$1(m, parent.transform, m); 2167 } 2168 else { 2169 copy$1(m, parent.transform); 2170 } 2171 } 2172 // æ·æ¿ç¨æ©æ¬é鿍»å´²éâ æ¨ 2173 this.transform = m; 2174 2175 this.invTransform = this.invTransform || create$1(); 2176 invert(this.invTransform, m); 2177 }; 2178 2179 transformableProto.getLocalTransform = function (m) { 2180 return Transformable.getLocalTransform(this, m); 2181 }; 2182 2183 /** 2184 * çåå宸辩æ®transformæ´ææ¤éç±ontextæ¶ï¿½ 2185 * @param {CanvasRenderingContext2D} ctx 2186 */ 2187 transformableProto.setTransform = function (ctx) { 2188 var m = this.transform; 2189 var dpr = ctx.dpr || 1; 2190 if (m) { 2191 ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]); 2192 } 2193 else {
2194 ctx.setTransform(dpr, 0, 0, dpr, 0, 0); 2195 } 2196 }; 2197 2198 transformableProto.restoreTransform = function (ctx) { 2199 var dpr = ctx.dpr || 1; 2200 ctx.setTransform(dpr, 0, 0, dpr, 0, 0); 2201 }; 2202 2203 var tmpTransform = []; 2204 2205 /** 2206 * éåÐ`transform`éâ æ¨éçposition`, `rotation`, `scale` 2207 */ 2208 transformableProto.decomposeTransform = function () { 2209 if (!this.transform) { 2210 return; 2211 } 2212 var parent = this.parent; 2213 var m = this.transform; 2214 if (parent && parent.transform) { 2215 // Get local transform and decompose them to position, scale, rotation 2216 mul$1(tmpTransform, parent.invTransform, m); 2217 m = tmpTransform; 2218 } 2219 var sx = m[0] * m[0] + m[1] * m[1]; 2220 var sy = m[2] * m[2] + m[3] * m[3]; 2221 var position = this.position; 2222 var scale$$1 = this.scale; 2223 if (isNotAroundZero(sx - 1)) { 2224 sx = Math.sqrt(sx); 2225 } 2226 if (isNotAroundZero(sy - 1)) { 2227 sy = Math.sqrt(sy); 2228 } 2229 if (m[0] < 0) { 2230 sx = -sx; 2231 } 2232 if (m[3] < 0) { 2233 sy = -sy; 2234 } 2235 position[0] = m[4]; 2236 position[1] = m[5]; 2237 scale$$1[0] = sx; 2238 scale$$1[1] = sy; 2239 this.rotation = Math.atan2(-m[1] / sy, m[0] / sx); 2240 }; 2241 2242 /** 2243 * Get global scale 2244 * @return {Array.<number>} 2245 */ 2246 transformableProto.getGlobalScale = function () { 2247 var m = this.transform; 2248 if (!m) { 2249 return [1, 1]; 2250 } 2251 var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]); 2252 var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]); 2253 if (m[0] < 0) { 2254 sx = -sx; 2255 } 2256 if (m[3] < 0) { 2257 sy = -sy; 2258 } 2259 return [sx, sy]; 2260 }; 2261 /** 2262 * 鿍»å´²é§æç£æµ£å¶çé� shape é¨å«ç¬é®ã¥æ½éå©âé� 2263 * @method 2264 * @param {number} x 2265 * @param {number} y 2266 * @return {Array.<number>} 2267 */ 2268 transformableProto.transformCoordToLocal = function (x, y) { 2269 var v2 = [x, y]; 2270 var invTransform = this.invTransform; 2271 if (invTransform) { 2272 applyTransform(v2, v2, invTransform); 2273 } 2274 return v2; 2275 }; 2276 2277 /** 2278 * 鿍»å´²çâ¬é®ã¥æ½éå¦ç¶ ç¼î¼åéã¥ç¬é§æç£ç»æ´ªæ£¿ 2279 * @method 2280 * @param {number} x 2281 * @param {number} y 2282 * @return {Array.<number>} 2283 */ 2284 transformableProto.transformCoordToGlobal = function (x, y) { 2285 var v2 = [x, y]; 2286 var transform = this.transform; 2287 if (transform) { 2288 applyTransform(v2, v2, transform); 2289 } 2290 return v2; 2291 }; 2292 2293 /** 2294 * @static 2295 * @param {Object} target 2296 * @param {Array.<number>} target.origin 2297 * @param {number} target.rotation 2298 * @param {Array.<number>} target.position 2299 * @param {Array.<number>} [m] 2300 */ 2301 Transformable.getLocalTransform = function (target, m) { 2302 m = m || []; 2303 mIdentity(m); 2304 2305 var origin = target.origin; 2306 var scale$$1 = target.scale || [1, 1]; 2307 var rotation = target.rotation || 0; 2308 var position = target.position || [0, 0]; 2309 2310 if (origin) { 2311 // Translate to origin 2312 m[4] -= origin[0]; 2313 m[5] -= origin[1]; 2314 } 2315 scale$1(m, m, scale$$1); 2316 if (rotation) { 2317 rotate(m, m, rotation); 2318 } 2319 if (origin) { 2320 // Translate back from origin 2321 m[4] += origin[0]; 2322 m[5] += origin[1]; 2323 } 2324 2325 m[4] += position[0]; 2326 m[5] += position[1]; 2327 2328 return m; 2329 }; 2330 2331 /** 2332 * ç¼æ³å§©æµ ï½çéã¨å https://github.com/sole/tween.js/blob/master/src/Tween.js 2333 * @see http://sole.github.io/tween.js/examples/03_graphs.html 2334 * @exports zrender/animation/easing 2335 */ 2336 var easing = { 2337 /** 2338 * @param {number} k 2339 * @return {number} 2340 */ 2341 linear: function (k) { 2342 return k; 2343 }, 2344 2345 /** 2346 * @param {number} k 2347 * @return {number} 2348 */ 2349 quadraticIn: function (k) { 2350 return k * k; 2351 }, 2352 /** 2353 * @param {number} k 2354 * @return {number} 2355 */ 2356 quadraticOut: function (k) { 2357 return k * (2 - k); 2358 }, 2359 /** 2360 * @param {number} k 2361 * @return {number} 2362 */ 2363 quadraticInOut: function (k) { 2364 if ((k *= 2) < 1) { 2365 return 0.5 * k * k; 2366 } 2367 return -0.5 * (--k * (k - 2) - 1); 2368 }, 2369
vendor: 9,182 bytes, lines 2370-2693
2370 // æ¶å¤î¼éåæ®ç¼æ³å§©éå±^3é� 2371 /** 2372 * @param {number} k 2373 * @return {number} 2374 */ 2375 cubicIn: function (k) { 2376 return k * k * k; 2377 }, 2378 /** 2379 * @param {number} k 2380 * @return {number} 2381 */ 2382 cubicOut: function (k) { 2383 return --k * k * k + 1; 2384 }, 2385 /** 2386 * @param {number} k 2387 * @return {number} 2388 */ 2389 cubicInOut: function (k) { 2390 if ((k *= 2) < 1) { 2391 return 0.5 * k * k * k; 2392 } 2393 return 0.5 * ((k -= 2) * k * k + 2); 2394 }, 2395 2396 // 饿¶î¼éåæ®ç¼æ³å§©éå±^4é� 2397 /** 2398 * @param {number} k 2399 * @return {number} 2400 */ 2401 quarticIn: function (k) { 2402 return k * k * k * k; 2403 }, 2404 /** 2405 * @param {number} k 2406 * @return {number} 2407 */ 2408 quarticOut: function (k) { 2409 return 1 - (--k * k * k * k); 2410 }, 2411 /** 2412 * @param {number} k 2413 * @return {number} 2414 */ 2415 quarticInOut: function (k) { 2416 if ((k *= 2) < 1) { 2417 return 0.5 * k * k * k * k; 2418 } 2419 return -0.5 * ((k -= 2) * k * k * k - 2); 2420 }, 2421 2422 // æµæî¼éåæ®ç¼æ³å§©éå±^5é� 2423 /** 2424 * @param {number} k 2425 * @return {number} 2426 */ 2427 quinticIn: function (k) { 2428 return k * k * k * k * k; 2429 }, 2430 /** 2431 * @param {number} k 2432 * @return {number} 2433 */ 2434 quinticOut: function (k) { 2435 return --k * k * k * k * k + 1; 2436 }, 2437 /** 2438 * @param {number} k 2439 * @return {number} 2440 */ 2441 quinticInOut: function (k) { 2442 if ((k *= 2) < 1) { 2443 return 0.5 * k * k * k * k * k; 2444 } 2445 return 0.5 * ((k -= 2) * k * k * k * k + 2); 2446 }, 2447 2448 // å§ï½ 鸡éè¬åé¨åç´¦éîç´sin(t)é� 2449 /** 2450 * @param {number} k 2451 * @return {number} 2452 */ 2453 sinusoidalIn: function (k) { 2454 return 1 - Math.cos(k * Math.PI / 2); 2455 }, 2456 /** 2457 * @param {number} k 2458 * @return {number} 2459 */ 2460 sinusoidalOut: function (k) { 2461 return Math.sin(k * Math.PI / 2); 2462 }, 2463 /** 2464 * @param {number} k 2465 * @return {number} 2466 */ 2467 sinusoidalInOut: function (k) { 2468 return 0.5 * (1 - Math.cos(Math.PI * k)); 2469 }, 2470 2471 // é¸å¨æéè¬åé¨åç´¦éîç´2^té� 2472 /** 2473 * @param {number} k 2474 * @return {number} 2475 */ 2476 exponentialIn: function (k) { 2477 return k === 0 ? 0 : Math.pow(1024, k - 1); 2478 }, 2479 /** 2480 * @param {number} k 2481 * @return {number} 2482 */ 2483 exponentialOut: function (k) { 2484 return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); 2485 }, 2486 /** 2487 * @param {number} k 2488 * @return {number} 2489 */ 2490 exponentialInOut: function (k) { 2491 if (k === 0) { 2492 return 0; 2493 } 2494 if (k === 1) { 2495 return 1; 2496 } 2497 if ((k *= 2) < 1) { 2498 return 0.5 * Math.pow(1024, k - 1); 2499 } 2500 return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2); 2501 }, 2502 2503 // é¦åè°éè¬åé¨åç´¦éîç´sqrt(1-t^2)é� 2504 /** 2505 * @param {number} k 2506 * @return {number} 2507 */ 2508 circularIn: function (k) { 2509 return 1 - Math.sqrt(1 - k * k); 2510 }, 2511 /** 2512 * @param {number} k 2513 * @return {number} 2514 */ 2515 circularOut: function (k) { 2516 return Math.sqrt(1 - (--k * k)); 2517 }, 2518 /** 2519 * @param {number} k 2520 * @return {number} 2521 */ 2522 circularInOut: function (k) { 2523 if ((k *= 2) < 1) { 2524 return -0.5 * (Math.sqrt(1 - k * k) - 1); 2525 } 2526 return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); 2527 }, 2528 2529 // éæ¶ç¼ç»«è®³ææµåº¡èç»¨Ñæ¹ªéæ»îéå¶æ½µé¥ç´å°é½ï¼æ®éã§æ¾ 2530 /** 2531 * @param {number} k 2532 * @return {number} 2533 */ 2534 elasticIn: function (k) { 2535 var s; 2536 var a = 0.1; 2537 var p = 0.4; 2538 if (k === 0) { 2539 return 0; 2540 } 2541 if (k === 1) { 2542 return 1; 2543 } 2544 if (!a || a < 1) { 2545 a = 1; s = p / 4; 2546 } 2547 else { 2548 s = p * Math.asin(1 / a) / (2 * Math.PI); 2549 } 2550 return -(a * Math.pow(2, 10 * (k -= 1)) * 2551 Math.sin((k - s) * (2 * Math.PI) / p)); 2552 }, 2553 /** 2554 * @param {number} k 2555 * @return {number} 2556 */ 2557 elasticOut: function (k) { 2558 var s; 2559 var a = 0.1; 2560 var p = 0.4; 2561 if (k === 0) { 2562 return 0; 2563 } 2564 if (k === 1) { 2565 return 1; 2566 } 2567 if (!a || a < 1) { 2568 a = 1; s = p / 4; 2569 } 2570 else { 2571 s = p * Math.asin(1 / a) / (2 * Math.PI); 2572 } 2573 return (a * Math.pow(2, -10 * k) * 2574 Math.sin((k - s) * (2 * Math.PI) / p) + 1); 2575 }, 2576 /** 2577 * @param {number} k 2578 * @return {number} 2579 */ 2580 elasticInOut: function (k) { 2581 var s; 2582 var a = 0.1; 2583 var p = 0.4; 2584 if (k === 0) { 2585 return 0; 2586 } 2587 if (k === 1) { 2588 return 1; 2589 } 2590 if (!a || a < 1) { 2591 a = 1; s = p / 4; 2592 } 2593 else { 2594 s = p * Math.asin(1 / a) / (2 * Math.PI); 2595 } 2596 if ((k *= 2) < 1) { 2597 return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) 2598 * Math.sin((k - s) * (2 * Math.PI) / p)); 2599 } 2600 return a * Math.pow(2, -10 * (k -= 1)) 2601 * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; 2602 2603 }, 2604 2605 // é¦ã¦ç æ¶â¬éã§æ¾å¯®â¬æ¿®å¬«é¨é¸å©ãé¨å®ç¾å¯°å®ç¹ç屽姩é¢è¯²î©éåå¢ ç»å¶â¢éè·ºæ´çã¥å§©é¢è¤æ®ç»è¯²å§© 2606 /** 2607 * @param {number} k 2608 * @return {number} 2609 */ 2610 backIn: function (k) { 2611 var s = 1.70158; 2612 return k * k * ((s + 1) * k - s); 2613 }, 2614 /** 2615 * @param {number} k 2616 * @return {number} 2617 */ 2618 backOut: function (k) { 2619 var s = 1.70158; 2620 return --k * k * ((s + 1) * k + s) + 1; 2621 }, 2622 /** 2623 * @param {number} k 2624 * @return {number} 2625 */ 2626 backInOut: function (k) { 2627 var s = 1.70158 * 1.525; 2628 if ((k *= 2) < 1) { 2629 return 0.5 * (k * k * ((s + 1) * k - s)); 2630 } 2631 return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); 2632 }, 2633 2634 // éæ¶ç¼å¯®ç¡ ç¦éå ç 2635 /** 2636 * @param {number} k 2637 * @return {number} 2638 */ 2639 bounceIn: function (k) { 2640 return 1 - easing.bounceOut(1 - k); 2641 }, 2642 /** 2643 * @param {number} k 2644 * @return {number} 2645 */ 2646 bounceOut: function (k) { 2647 if (k < (1 / 2.75)) { 2648 return 7.5625 * k * k; 2649 } 2650 else if (k < (2 / 2.75)) { 2651 return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; 2652 } 2653 else if (k < (2.5 / 2.75)) { 2654 return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; 2655 } 2656 else { 2657 return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; 2658 } 2659 }, 2660 /** 2661 * @param {number} k 2662 * @return {number} 2663 */ 2664 bounceInOut: function (k) { 2665 if (k < 0.5) { 2666 return easing.bounceIn(k * 2) * 0.5; 2667 } 2668 return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5; 2669 } 2670 }; 2671 2672 /** 2673 * éã§æ¾æ¶ç»å¸¶éè·ºæ« 2674 * @config target éã§æ¾çµç¡ èéå±½å½²æµ ã¦æ§¸éæ®ç²éå±½î§éæ»æ§¸éæ®ç²é¨å®ç½æµ¼æ°å£é²å¿åéænframeç»å¤ç°¨æµ � 2675 * @config life(1000) éã§æ¾éå æ± 2676 * @config delay(0) éã§æ¾å¯¤æ°ç¹éå æ£¿ 2677 * @config loop(true) 2678 * @config gap(0) 寰îå¹é¨å¯æ£¿é ææ¤é� 2679 * @config onframe 2680 * @config easing(optional) 2681 * @config ondestroy(optional) 2682 * @config onrestart(optional) 2683 * 2684 * TODO pause 2685 */ 2686 2687 function Clip(options) { 2688 2689 this._target = options.target; 2690 2691 // é¢ç·æ¡éã¦æ¹¡ 2692 this._life = options.life || 1000; 2693 // å¯¤èµæ¤
2694 this._delay = options.delay || 0; 2695 // å¯®â¬æ¿®å¬«æ¤é� 2696 // this._startTime = new Date().getTime() + this._delay;// éæç¶ å§£î¤î 2697 this._initialized = false; 2698 2699 // éîæå¯°îå¹ 2700 this.loop = options.loop == null ? false : options.loop; 2701 2702 this.gap = options.gap || 0; 2703 2704 this.easing = options.easing || 'Linear'; 2705 2706 this.onframe = options.onframe; 2707 this.ondestroy = options.ondestroy; 2708 this.onrestart = options.onrestart; 2709 2710 this._pausedTime = 0; 2711 this._paused = false; 2712 } 2713 2714 Clip.prototype = { 2715 2716 constructor: Clip, 2717 2718 step: function (globalTime, deltaTime) { 2719 // Set startTime on first step, or _startTime may has milleseconds different between clips 2720 // PENDING 2721 if (!this._initialized) { 2722 this._startTime = globalTime + this._delay; 2723 this._initialized = true; 2724 } 2725 2726 if (this._paused) { 2727 this._pausedTime += deltaTime; 2728 return; 2729 } 2730 2731 var percent = (globalTime - this._startTime - this._pausedTime) / this._life; 2732 2733 // æ©æ¨»ç å¯®â¬æ¿®ï¿½ 2734 if (percent < 0) { 2735 return; 2736 } 2737 2738 percent = Math.min(percent, 1); 2739 2740 var easing$$1 = this.easing; 2741 var easingFunc = typeof easing$$1 == 'string' ? easing[easing$$1] : easing$$1; 2742 var schedule = typeof easingFunc === 'function' 2743 ? easingFunc(percent) 2744 : percent; 2745 2746 this.fire('frame', schedule); 2747 2748 // ç¼æ´æ½« 2749 if (percent == 1) { 2750 if (this.loop) { 2751 this.restart (globalTime); 2752 // é²å¶æå¯®â¬æ¿®å¬ªæé� 2753 // é¶æ¶åé°å±¼ç¬éîæ´¿éºã¨çé¢ã¤ç°¨æµ åæ´¿é� stage.update é庡åç¼ç¶ç«´çåªæ¤æ©æ¬ç°ºæµå¬©æ¬¢ 2754 return 'restart'; 2755 } 2756 2757 // éã§æ¾ç¹å±¾åçåç¹æ¶î 帶éè·ºæ«éåªçæ¶åç·éç»æ« 2758 // é¦Ënimation.updateæ¶î ¡ç¹çå±¾å£é²å¿å¹é� 2759 this._needsRemove = true; 2760 return 'destroy'; 2761 } 2762 2763 return null; 2764 }, 2765 2766 restart: function (globalTime) { 2767 var remainder = (globalTime - this._startTime - this._pausedTime) % this._life; 2768 this._startTime = globalTime - remainder + this.gap; 2769 this._pausedTime = 0; 2770 2771 this._needsRemove = false; 2772 }, 2773 2774 fire: function (eventType, arg) { 2775 eventType = 'on' + eventType; 2776 if (this[eventType]) { 2777 this[eventType](this._target, arg); 2778 } 2779 }, 2780 2781 pause: function () { 2782 this._paused = true; 2783 }, 2784 2785 resume: function () { 2786 this._paused = false; 2787 } 2788 }; 2789 2790// Simple LRU cache use doubly linked list 2791// @module zrender/core/LRU 2792 2793 /** 2794 * Simple double linked list. Compared with array, it has O(1) remove operation. 2795 * @constructor 2796 */ 2797 var LinkedList = function () { 2798 2799 /** 2800 * @type {module:zrender/core/LRU~Entry} 2801 */ 2802 this.head = null; 2803 2804 /** 2805 * @type {module:zrender/core/LRU~Entry} 2806 */ 2807 this.tail = null; 2808 2809 this._len = 0; 2810 }; 2811 2812 var linkedListProto = LinkedList.prototype; 2813 /** 2814 * Insert a new value at the tail 2815 * @param {} val 2816 * @return {module:zrender/core/LRU~Entry} 2817 */ 2818 linkedListProto.insert = function (val) { 2819 var entry = new Entry(val); 2820 this.insertEntry(entry); 2821 return entry; 2822 }; 2823 2824 /** 2825 * Insert an entry at the tail 2826 * @param {module:zrender/core/LRU~Entry} entry 2827 */ 2828 linkedListProto.insertEntry = function (entry) { 2829 if (!this.head) { 2830 this.head = this.tail = entry; 2831 } 2832 else { 2833 this.tail.next = entry; 2834 entry.prev = this.tail; 2835 entry.next = null; 2836 this.tail = entry; 2837 } 2838 this._len++; 2839 }; 2840 2841 /** 2842 * Remove entry. 2843 * @param {module:zrender/core/LRU~Entry} entry 2844 */ 2845 linkedListProto.remove = function (entry) { 2846 var prev = entry.prev; 2847 var next = entry.next; 2848 if (prev) { 2849 prev.next = next; 2850 } 2851 else { 2852 // Is head 2853 this.head = next; 2854 } 2855 if (next) { 2856 next.prev = prev; 2857 } 2858 else { 2859 // Is tail 2860 this.tail = prev; 2861 } 2862 entry.next = entry.prev = null; 2863 this._len--; 2864 }; 2865 2866 /** 2867 * @return {number} 2868 */ 2869 linkedListProto.len = function () { 2870 return this._len; 2871 }; 2872 2873 /** 2874 * Clear list 2875 */ 2876 linkedListProto.clear = function () { 2877 this.head = this.tail = null; 2878 this._len = 0; 2879 }; 2880 2881 /** 2882 * @constructor 2883 * @param {} val 2884 */ 2885 var Entry = function (val) { 2886 /** 2887 * @type {} 2888 */ 2889 this.value = val; 2890 2891 /** 2892 * @type {module:zrender/core/LRU~Entry} 2893 */ 2894 this.next; 2895 2896 /** 2897 * @type {module:zrender/core/LRU~Entry} 2898 */ 2899 this.prev; 2900 }; 2901 2902 /** 2903 * LRU Cache 2904 * @constructor 2905 * @alias module:zrender/core/LRU 2906 */ 2907 var LRU = function (maxSize) { 2908 2909 this._list = new LinkedList(); 2910 2911 this._map = {}; 2912 2913 this._maxSize = maxSize || 10; 2914 2915 this._lastRemovedEntry = null; 2916 }; 2917 2918 var LRUProto = LRU.prototype; 2919 2920 /** 2921 * @param {string} key 2922 * @param {} value 2923 * @return {} Removed value 2924 */ 2925 LRUProto.put = function (key, value) { 2926 var list = this._list; 2927 var map = this._map; 2928 var removed = null; 2929 if (map[key] == null) { 2930 var len = list.len();
2931 // Reuse last removed entry 2932 var entry = this._lastRemovedEntry; 2933 2934 if (len >= this._maxSize && len > 0) { 2935 // Remove the least recently used 2936 var leastUsedEntry = list.head; 2937 list.remove(leastUsedEntry); 2938 delete map[leastUsedEntry.key]; 2939 2940 removed = leastUsedEntry.value; 2941 this._lastRemovedEntry = leastUsedEntry; 2942 } 2943 2944 if (entry) { 2945 entry.value = value; 2946 } 2947 else { 2948 entry = new Entry(value); 2949 } 2950 entry.key = key; 2951 list.insertEntry(entry); 2952 map[key] = entry; 2953 } 2954 2955 return removed; 2956 }; 2957 2958 /** 2959 * @param {string} key 2960 * @return {} 2961 */ 2962 LRUProto.get = function (key) { 2963 var entry = this._map[key]; 2964 var list = this._list; 2965 if (entry != null) { 2966 // Put the latest used entry in the tail 2967 if (entry !== list.tail) { 2968 list.remove(entry); 2969 list.insertEntry(entry); 2970 } 2971 2972 return entry.value; 2973 } 2974 }; 2975 2976 /** 2977 * Clear the cache 2978 */ 2979 LRUProto.clear = function () { 2980 this._list.clear(); 2981 this._map = {}; 2982 }; 2983 2984 var kCSSColorTable = { 2985 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], 2986 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], 2987 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], 2988 'beige': [245,245,220,1], 'bisque': [255,228,196,1],
2989 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], 2990 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], 2991 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], 2992 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], 2993 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], 2994 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], 2995 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], 2996 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], 2997 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], 2998 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], 2999 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], 3000 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], 3001 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], 3002 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], 3003 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], 3004 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], 3005 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], 3006 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], 3007 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], 3008 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], 3009 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], 3010 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], 3011 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], 3012 'gray': [128,128,128,1], 'green': [0,128,0,1], 3013 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], 3014 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], 3015 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], 3016 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], 3017 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], 3018 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], 3019 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], 3020 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], 3021 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], 3022 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], 3023 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], 3024 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], 3025 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], 3026 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], 3027 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], 3028 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], 3029 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], 3030 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], 3031 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], 3032 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], 3033 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], 3034 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], 3035 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], 3036 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], 3037 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], 3038 'orange': [255,165,0,1], 'orangered': [255,69,0,1], 3039 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], 3040 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], 3041 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], 3042 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], 3043 'pink': [255,192,203,1], 'plum': [221,160,221,1], 3044 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], 3045 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], 3046 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], 3047 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], 3048 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], 3049 'sienna': [160,82,45,1], 'silver': [192,192,192,1],
vendor: 6,228 bytes, lines 3050-3227
3050 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], 3051 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], 3052 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], 3053 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], 3054 'teal': [0,128,128,1], 'thistle': [216,191,216,1], 3055 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], 3056 'violet': [238,130,238,1], 'wheat': [245,222,179,1], 3057 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], 3058 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] 3059 }; 3060 3061 function clampCssByte(i) { // Clamp to integer 0 .. 255. 3062 i = Math.round(i); // Seems to be what Chrome does (vs truncation). 3063 return i < 0 ? 0 : i > 255 ? 255 : i; 3064 } 3065 3066 function clampCssAngle(i) { // Clamp to integer 0 .. 360. 3067 i = Math.round(i); // Seems to be what Chrome does (vs truncation). 3068 return i < 0 ? 0 : i > 360 ? 360 : i; 3069 } 3070 3071 function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. 3072 return f < 0 ? 0 : f > 1 ? 1 : f; 3073 } 3074 3075 function parseCssInt(str) { // int or percentage. 3076 if (str.length && str.charAt(str.length - 1) === '%') { 3077 return clampCssByte(parseFloat(str) / 100 * 255); 3078 } 3079 return clampCssByte(parseInt(str, 10)); 3080 } 3081 3082 function parseCssFloat(str) { // float or percentage. 3083 if (str.length && str.charAt(str.length - 1) === '%') { 3084 return clampCssFloat(parseFloat(str) / 100); 3085 } 3086 return clampCssFloat(parseFloat(str)); 3087 } 3088 3089 function cssHueToRgb(m1, m2, h) { 3090 if (h < 0) { 3091 h += 1; 3092 } 3093 else if (h > 1) { 3094 h -= 1; 3095 } 3096 3097 if (h * 6 < 1) { 3098 return m1 + (m2 - m1) * h * 6; 3099 } 3100 if (h * 2 < 1) { 3101 return m2; 3102 } 3103 if (h * 3 < 2) { 3104 return m1 + (m2 - m1) * (2/3 - h) * 6; 3105 } 3106 return m1; 3107 } 3108 3109 function lerpNumber(a, b, p) { 3110 return a + (b - a) * p; 3111 } 3112 3113 function setRgba(out, r, g, b, a) { 3114 out[0] = r; out[1] = g; out[2] = b; out[3] = a; 3115 return out; 3116 } 3117 function copyRgba(out, a) { 3118 out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; 3119 return out; 3120 } 3121 3122 var colorCache = new LRU(20); 3123 var lastRemovedArr = null; 3124 3125 function putToCache(colorStr, rgbaArr) { 3126 // Reuse removed array 3127 if (lastRemovedArr) { 3128 copyRgba(lastRemovedArr, rgbaArr); 3129 } 3130 lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); 3131 } 3132 3133 /** 3134 * @param {string} colorStr 3135 * @param {Array.<number>} out 3136 * @return {Array.<number>} 3137 * @memberOf module:zrender/util/color 3138 */ 3139 function parse(colorStr, rgbaArr) { 3140 if (!colorStr) { 3141 return; 3142 } 3143 rgbaArr = rgbaArr || []; 3144 3145 var cached = colorCache.get(colorStr); 3146 if (cached) { 3147 return copyRgba(rgbaArr, cached); 3148 } 3149 3150 // colorStr may be not string 3151 colorStr = colorStr + ''; 3152 // Remove all whitespace, not compliant, but should just be more accepting. 3153 var str = colorStr.replace(/ /g, '').toLowerCase(); 3154 3155 // Color keywords (and transparent) lookup. 3156 if (str in kCSSColorTable) { 3157 copyRgba(rgbaArr, kCSSColorTable[str]); 3158 putToCache(colorStr, rgbaArr); 3159 return rgbaArr; 3160 } 3161 3162 // #abc and #abc123 syntax. 3163 if (str.charAt(0) === '#') { 3164 if (str.length === 4) { 3165 var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. 3166 if (!(iv >= 0 && iv <= 0xfff)) { 3167 setRgba(rgbaArr, 0, 0, 0, 1); 3168 return; // Covers NaN. 3169 } 3170 setRgba(rgbaArr, 3171 ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), 3172 (iv & 0xf0) | ((iv & 0xf0) >> 4), 3173 (iv & 0xf) | ((iv & 0xf) << 4), 3174 1 3175 ); 3176 putToCache(colorStr, rgbaArr); 3177 return rgbaArr; 3178 } 3179 else if (str.length === 7) { 3180 var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. 3181 if (!(iv >= 0 && iv <= 0xffffff)) { 3182 setRgba(rgbaArr, 0, 0, 0, 1); 3183 return; // Covers NaN. 3184 } 3185 setRgba(rgbaArr, 3186 (iv & 0xff0000) >> 16, 3187 (iv & 0xff00) >> 8, 3188 iv & 0xff, 3189 1 3190 ); 3191 putToCache(colorStr, rgbaArr); 3192 return rgbaArr; 3193 } 3194 3195 return; 3196 } 3197 var op = str.indexOf('('), ep = str.indexOf(')'); 3198 if (op !== -1 && ep + 1 === str.length) { 3199 var fname = str.substr(0, op); 3200 var params = str.substr(op + 1, ep - (op + 1)).split(','); 3201 var alpha = 1; // To allow case fallthrough. 3202 switch (fname) { 3203 case 'rgba': 3204 if (params.length !== 4) { 3205 setRgba(rgbaArr, 0, 0, 0, 1); 3206 return; 3207 } 3208 alpha = parseCssFloat(params.pop()); // jshint ignore:line 3209 // Fall through. 3210 case 'rgb': 3211 if (params.length !== 3) { 3212 setRgba(rgbaArr, 0, 0, 0, 1); 3213 return; 3214 } 3215 setRgba(rgbaArr, 3216 parseCssInt(params[0]), 3217 parseCssInt(params[1]), 3218 parseCssInt(params[2]), 3219 alpha 3220 ); 3221 putToCache(colorStr, rgbaArr); 3222 return rgbaArr; 3223 case 'hsla': 3224 if (params.length !== 4) { 3225 setRgba(rgbaArr, 0, 0, 0, 1); 3226 return; 3227 }
vendor: 7,852 bytes, lines 3228-3478
3228 params[3] = parseCssFloat(params[3]); 3229 hsla2rgba(params, rgbaArr); 3230 putToCache(colorStr, rgbaArr); 3231 return rgbaArr; 3232 case 'hsl': 3233 if (params.length !== 3) { 3234 setRgba(rgbaArr, 0, 0, 0, 1); 3235 return; 3236 } 3237 hsla2rgba(params, rgbaArr); 3238 putToCache(colorStr, rgbaArr); 3239 return rgbaArr; 3240 default: 3241 return; 3242 } 3243 } 3244 3245 setRgba(rgbaArr, 0, 0, 0, 1); 3246 return; 3247 } 3248 3249 /** 3250 * @param {Array.<number>} hsla 3251 * @param {Array.<number>} rgba 3252 * @return {Array.<number>} rgba 3253 */ 3254 function hsla2rgba(hsla, rgba) { 3255 var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 3256 // NOTE(deanm): According to the CSS spec s/l should only be 3257 // percentages, but we don't bother and let float or percentage. 3258 var s = parseCssFloat(hsla[1]); 3259 var l = parseCssFloat(hsla[2]); 3260 var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; 3261 var m1 = l * 2 - m2; 3262 3263 rgba = rgba || []; 3264 setRgba(rgba, 3265 clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), 3266 clampCssByte(cssHueToRgb(m1, m2, h) * 255), 3267 clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 3268 1 3269 ); 3270 3271 if (hsla.length === 4) { 3272 rgba[3] = hsla[3]; 3273 } 3274 3275 return rgba; 3276 } 3277 3278 /** 3279 * @param {Array.<number>} rgba 3280 * @return {Array.<number>} hsla 3281 */ 3282 function rgba2hsla(rgba) { 3283 if (!rgba) { 3284 return; 3285 } 3286 3287 // RGB from 0 to 255 3288 var R = rgba[0] / 255; 3289 var G = rgba[1] / 255; 3290 var B = rgba[2] / 255; 3291 3292 var vMin = Math.min(R, G, B); // Min. value of RGB 3293 var vMax = Math.max(R, G, B); // Max. value of RGB 3294 var delta = vMax - vMin; // Delta RGB value 3295 3296 var L = (vMax + vMin) / 2; 3297 var H; 3298 var S; 3299 // HSL results from 0 to 1 3300 if (delta === 0) { 3301 H = 0; 3302 S = 0; 3303 } 3304 else { 3305 if (L < 0.5) { 3306 S = delta / (vMax + vMin); 3307 } 3308 else { 3309 S = delta / (2 - vMax - vMin); 3310 } 3311 3312 var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; 3313 var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; 3314 var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; 3315 3316 if (R === vMax) { 3317 H = deltaB - deltaG; 3318 } 3319 else if (G === vMax) { 3320 H = (1 / 3) + deltaR - deltaB; 3321 } 3322 else if (B === vMax) { 3323 H = (2 / 3) + deltaG - deltaR; 3324 } 3325 3326 if (H < 0) { 3327 H += 1; 3328 } 3329 3330 if (H > 1) { 3331 H -= 1; 3332 } 3333 } 3334 3335 var hsla = [H * 360, S, L]; 3336 3337 if (rgba[3] != null) { 3338 hsla.push(rgba[3]); 3339 } 3340 3341 return hsla; 3342 } 3343 3344 /** 3345 * @param {string} color 3346 * @param {number} level 3347 * @return {string} 3348 * @memberOf module:zrender/util/color 3349 */ 3350 function lift(color, level) { 3351 var colorArr = parse(color); 3352 if (colorArr) { 3353 for (var i = 0; i < 3; i++) { 3354 if (level < 0) { 3355 colorArr[i] = colorArr[i] * (1 - level) | 0; 3356 } 3357 else { 3358 colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; 3359 } 3360 if (colorArr[i] > 255) { 3361 colorArr[i] = 255; 3362 } 3363 else if (color[i] < 0) { 3364 colorArr[i] = 0; 3365 } 3366 } 3367 return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); 3368 } 3369 } 3370 3371 /** 3372 * @param {string} color 3373 * @return {string} 3374 * @memberOf module:zrender/util/color 3375 */ 3376 function toHex(color) { 3377 var colorArr = parse(color); 3378 if (colorArr) { 3379 return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); 3380 } 3381 } 3382 3383 /** 3384 * Map value to color. Faster than lerp methods because color is represented by rgba array. 3385 * @param {number} normalizedValue A float between 0 and 1. 3386 * @param {Array.<Array.<number>>} colors List of rgba color array 3387 * @param {Array.<number>} [out] Mapped gba color array 3388 * @return {Array.<number>} will be null/undefined if input illegal. 3389 */ 3390 function fastLerp(normalizedValue, colors, out) { 3391 if (!(colors && colors.length) 3392 || !(normalizedValue >= 0 && normalizedValue <= 1) 3393 ) { 3394 return; 3395 } 3396 3397 out = out || []; 3398 3399 var value = normalizedValue * (colors.length - 1); 3400 var leftIndex = Math.floor(value); 3401 var rightIndex = Math.ceil(value); 3402 var leftColor = colors[leftIndex]; 3403 var rightColor = colors[rightIndex]; 3404 var dv = value - leftIndex; 3405 out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); 3406 out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); 3407 out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); 3408 out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); 3409 3410 return out; 3411 } 3412 3413 /** 3414 * @deprecated 3415 */ 3416 var fastMapToColor = fastLerp; 3417 3418 /** 3419 * @param {number} normalizedValue A float between 0 and 1. 3420 * @param {Array.<string>} colors Color list. 3421 * @param {boolean=} fullOutput Default false. 3422 * @return {(string|Object)} Result color. If fullOutput, 3423 * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, 3424 * @memberOf module:zrender/util/color 3425 */ 3426 function lerp$1(normalizedValue, colors, fullOutput) { 3427 if (!(colors && colors.length) 3428 || !(normalizedValue >= 0 && normalizedValue <= 1) 3429 ) { 3430 return; 3431 } 3432 3433 var value = normalizedValue * (colors.length - 1); 3434 var leftIndex = Math.floor(value); 3435 var rightIndex = Math.ceil(value); 3436 var leftColor = parse(colors[leftIndex]); 3437 var rightColor = parse(colors[rightIndex]); 3438 var dv = value - leftIndex; 3439 3440 var color = stringify( 3441 [ 3442 clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), 3443 clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), 3444 clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), 3445 clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) 3446 ], 3447 'rgba' 3448 ); 3449 3450 return fullOutput 3451 ? { 3452 color: color, 3453 leftIndex: leftIndex, 3454 rightIndex: rightIndex, 3455 value: value 3456 } 3457 : color; 3458 } 3459 3460 /** 3461 * @deprecated 3462 */ 3463 var mapToColor = lerp$1; 3464 3465 /** 3466 * @param {string} color 3467 * @param {number=} h 0 ~ 360, ignore when null. 3468 * @param {number=} s 0 ~ 1, ignore when null. 3469 * @param {number=} l 0 ~ 1, ignore when null. 3470 * @return {string} Color string in rgba format. 3471 * @memberOf module:zrender/util/color 3472 */ 3473 function modifyHSL(color, h, s, l) { 3474 color = parse(color); 3475 3476 if (color) { 3477 color = rgba2hsla(color); 3478 h != null && (color[0] = clampCssAngle(h));
3479 s != null && (color[1] = parseCssFloat(s)); 3480 l != null && (color[2] = parseCssFloat(l)); 3481 3482 return stringify(hsla2rgba(color), 'rgba'); 3483 } 3484 } 3485 3486 /** 3487 * @param {string} color 3488 * @param {number=} alpha 0 ~ 1 3489 * @return {string} Color string in rgba format. 3490 * @memberOf module:zrender/util/color 3491 */ 3492 function modifyAlpha(color, alpha) { 3493 color = parse(color); 3494 3495 if (color && alpha != null) { 3496 color[3] = clampCssFloat(alpha); 3497 return stringify(color, 'rgba'); 3498 } 3499 } 3500 3501 /** 3502 * @param {Array.<number>} arrColor like [12,33,44,0.4] 3503 * @param {string} type 'rgba', 'hsva', ... 3504 * @return {string} Result color. (If input illegal, return undefined). 3505 */ 3506 function stringify(arrColor, type) { 3507 if (!arrColor || !arrColor.length) { 3508 return; 3509 } 3510 var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; 3511 if (type === 'rgba' || type === 'hsva' || type === 'hsla') { 3512 colorStr += ',' + arrColor[3]; 3513 } 3514 return type + '(' + colorStr + ')'; 3515 } 3516 3517 3518 var color = (Object.freeze || Object)({ 3519 parse: parse, 3520 lift: lift, 3521 toHex: toHex, 3522 fastLerp: fastLerp, 3523 fastMapToColor: fastMapToColor, 3524 lerp: lerp$1, 3525 mapToColor: mapToColor, 3526 modifyHSL: modifyHSL, 3527 modifyAlpha: modifyAlpha, 3528 stringify: stringify 3529 }); 3530 3531 /** 3532 * @module echarts/animation/Animator 3533 */ 3534 3535 var arraySlice = Array.prototype.slice; 3536 3537 function defaultGetter(target, key) { 3538 return target[key]; 3539 } 3540 3541 function defaultSetter(target, key, value) { 3542 target[key] = value; 3543 } 3544 3545 /** 3546 * @param {number} p0 3547 * @param {number} p1 3548 * @param {number} percent 3549 * @return {number} 3550 */ 3551 function interpolateNumber(p0, p1, percent) { 3552 return (p1 - p0) * percent + p0; 3553 } 3554 3555 /** 3556 * @param {string} p0 3557 * @param {string} p1 3558 * @param {number} percent 3559 * @return {string} 3560 */ 3561 function interpolateString(p0, p1, percent) { 3562 return percent > 0.5 ? p1 : p0; 3563 } 3564 3565 /** 3566 * @param {Array} p0 3567 * @param {Array} p1 3568 * @param {number} percent 3569 * @param {Array} out 3570 * @param {number} arrDim 3571 */ 3572 function interpolateArray(p0, p1, percent, out, arrDim) { 3573 var len = p0.length; 3574 if (arrDim == 1) { 3575 for (var i = 0; i < len; i++) { 3576 out[i] = interpolateNumber(p0[i], p1[i], percent); 3577 } 3578 } 3579 else { 3580 var len2 = len && p0[0].length; 3581 for (var i = 0; i < len; i++) { 3582 for (var j = 0; j < len2; j++) { 3583 out[i][j] = interpolateNumber( 3584 p0[i][j], p1[i][j], percent 3585 ); 3586 } 3587 } 3588 } 3589 } 3590 3591// arr0 is source array, arr1 is target array. 3592// Do some preprocess to avoid error happened when interpolating from arr0 to arr1 3593 function fillArr(arr0, arr1, arrDim) { 3594 var arr0Len = arr0.length; 3595 var arr1Len = arr1.length; 3596 if (arr0Len !== arr1Len) { 3597 // FIXME Not work for TypedArray 3598 var isPreviousLarger = arr0Len > arr1Len; 3599 if (isPreviousLarger) { 3600 // Cut the previous 3601 arr0.length = arr1Len; 3602 } 3603 else { 3604 // Fill the previous 3605 for (var i = arr0Len; i < arr1Len; i++) { 3606 arr0.push( 3607 arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]) 3608 ); 3609 } 3610 } 3611 } 3612 // Handling NaN value 3613 var len2 = arr0[0] && arr0[0].length; 3614 for (var i = 0; i < arr0.length; i++) { 3615 if (arrDim === 1) { 3616 if (isNaN(arr0[i])) { 3617 arr0[i] = arr1[i]; 3618 } 3619 } 3620 else { 3621 for (var j = 0; j < len2; j++) {
3622 if (isNaN(arr0[i][j])) { 3623 arr0[i][j] = arr1[i][j]; 3624 } 3625 } 3626 } 3627 } 3628 } 3629 3630 /** 3631 * @param {Array} arr0 3632 * @param {Array} arr1 3633 * @param {number} arrDim 3634 * @return {boolean} 3635 */ 3636 function isArraySame(arr0, arr1, arrDim) { 3637 if (arr0 === arr1) { 3638 return true; 3639 } 3640 var len = arr0.length; 3641 if (len !== arr1.length) { 3642 return false; 3643 } 3644 if (arrDim === 1) { 3645 for (var i = 0; i < len; i++) { 3646 if (arr0[i] !== arr1[i]) { 3647 return false; 3648 } 3649 } 3650 } 3651 else { 3652 var len2 = arr0[0].length; 3653 for (var i = 0; i < len; i++) { 3654 for (var j = 0; j < len2; j++) { 3655 if (arr0[i][j] !== arr1[i][j]) { 3656 return false; 3657 } 3658 } 3659 } 3660 } 3661 return true; 3662 } 3663 3664 /** 3665 * Catmull Rom interpolate array 3666 * @param {Array} p0 3667 * @param {Array} p1 3668 * @param {Array} p2 3669 * @param {Array} p3 3670 * @param {number} t 3671 * @param {number} t2 3672 * @param {number} t3 3673 * @param {Array} out 3674 * @param {number} arrDim 3675 */ 3676 function catmullRomInterpolateArray( 3677 p0, p1, p2, p3, t, t2, t3, out, arrDim 3678 ) { 3679 var len = p0.length; 3680 if (arrDim == 1) { 3681 for (var i = 0; i < len; i++) { 3682 out[i] = catmullRomInterpolate( 3683 p0[i], p1[i], p2[i], p3[i], t, t2, t3 3684 ); 3685 } 3686 } 3687 else { 3688 var len2 = p0[0].length; 3689 for (var i = 0; i < len; i++) { 3690 for (var j = 0; j < len2; j++) { 3691 out[i][j] = catmullRomInterpolate( 3692 p0[i][j], p1[i][j], p2[i][j], p3[i][j], 3693 t, t2, t3 3694 ); 3695 } 3696 } 3697 } 3698 } 3699 3700 /** 3701 * Catmull Rom interpolate number 3702 * @param {number} p0 3703 * @param {number} p1 3704 * @param {number} p2 3705 * @param {number} p3 3706 * @param {number} t 3707 * @param {number} t2 3708 * @param {number} t3 3709 * @return {number} 3710 */ 3711 function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { 3712 var v0 = (p2 - p0) * 0.5; 3713 var v1 = (p3 - p1) * 0.5; 3714 return (2 * (p1 - p2) + v0 + v1) * t3 3715 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 3716 + v0 * t + p1; 3717 } 3718 3719 function cloneValue(value) { 3720 if (isArrayLike(value)) { 3721 var len = value.length; 3722 if (isArrayLike(value[0])) { 3723 var ret = []; 3724 for (var i = 0; i < len; i++) { 3725 ret.push(arraySlice.call(value[i])); 3726 } 3727 return ret; 3728 } 3729 3730 return arraySlice.call(value); 3731 } 3732 3733 return value; 3734 } 3735 3736 function rgba2String(rgba) { 3737 rgba[0] = Math.floor(rgba[0]); 3738 rgba[1] = Math.floor(rgba[1]); 3739 rgba[2] = Math.floor(rgba[2]); 3740 3741 return 'rgba(' + rgba.join(',') + ')'; 3742 } 3743 3744 function getArrayDim(keyframes) { 3745 var lastValue = keyframes[keyframes.length - 1].value; 3746 return isArrayLike(lastValue && lastValue[0]) ? 2 : 1; 3747 } 3748 3749 function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) { 3750 var getter = animator._getter; 3751 var setter = animator._setter; 3752 var useSpline = easing === 'spline'; 3753 3754 var trackLen = keyframes.length; 3755 if (!trackLen) { 3756 return; 3757 } 3758 // Guess data type 3759 var firstVal = keyframes[0].value; 3760 var isValueArray = isArrayLike(firstVal); 3761 var isValueColor = false; 3762 var isValueString = false; 3763 3764 // For vertices morphing 3765 var arrDim = isValueArray ? getArrayDim(keyframes) : 0; 3766 3767 var trackMaxTime; 3768 // Sort keyframe as ascending 3769 keyframes.sort(function(a, b) { 3770 return a.time - b.time; 3771 }); 3772 3773 trackMaxTime = keyframes[trackLen - 1].time; 3774 // Percents of each keyframe 3775 var kfPercents = []; 3776 // Value of each keyframe 3777 var kfValues = []; 3778 var prevValue = keyframes[0].value; 3779 var isAllValueEqual = true; 3780 for (var i = 0; i < trackLen; i++) { 3781 kfPercents.push(keyframes[i].time / trackMaxTime); 3782 // Assume value is a color when it is a string 3783 var value = keyframes[i].value; 3784 3785 // Check if value is equal, deep check if value is array 3786 if (!((isValueArray && isArraySame(value, prevValue, arrDim)) 3787 || (!isValueArray && value === prevValue))) { 3788 isAllValueEqual = false; 3789 } 3790 prevValue = value; 3791
3792 // Try converting a string to a color array 3793 if (typeof value == 'string') { 3794 var colorArray = parse(value); 3795 if (colorArray) { 3796 value = colorArray; 3797 isValueColor = true; 3798 } 3799 else { 3800 isValueString = true; 3801 } 3802 } 3803 kfValues.push(value); 3804 } 3805 if (!forceAnimate && isAllValueEqual) { 3806 return; 3807 } 3808 3809 var lastValue = kfValues[trackLen - 1]; 3810 // Polyfill array and NaN value 3811 for (var i = 0; i < trackLen - 1; i++) { 3812 if (isValueArray) { 3813 fillArr(kfValues[i], lastValue, arrDim); 3814 } 3815 else { 3816 if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) { 3817 kfValues[i] = lastValue; 3818 } 3819 } 3820 } 3821 isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim); 3822 3823 // Cache the key of last frame to speed up when 3824 // animation playback is sequency 3825 var lastFrame = 0; 3826 var lastFramePercent = 0; 3827 var start; 3828 var w; 3829 var p0; 3830 var p1; 3831 var p2; 3832 var p3; 3833 3834 if (isValueColor) { 3835 var rgba = [0, 0, 0, 0]; 3836 } 3837 3838 var onframe = function (target, percent) { 3839 // Find the range keyframes 3840 // kf1-----kf2---------current--------kf3 3841 // find kf2 and kf3 and do interpolation 3842 var frame; 3843 // In the easing function like elasticOut, percent may less than 0 3844 if (percent < 0) { 3845 frame = 0; 3846 } 3847 else if (percent < lastFramePercent) { 3848 // Start from next key 3849 // PENDING start from lastFrame ? 3850 start = Math.min(lastFrame + 1, trackLen - 1); 3851 for (frame = start; frame >= 0; frame--) { 3852 if (kfPercents[frame] <= percent) { 3853 break; 3854 } 3855 } 3856 // PENDING really need to do this ? 3857 frame = Math.min(frame, trackLen - 2); 3858 } 3859 else { 3860 for (frame = lastFrame; frame < trackLen; frame++) { 3861 if (kfPercents[frame] > percent) { 3862 break; 3863 } 3864 } 3865 frame = Math.min(frame - 1, trackLen - 2); 3866 } 3867 lastFrame = frame; 3868 lastFramePercent = percent; 3869 3870 var range = (kfPercents[frame + 1] - kfPercents[frame]); 3871 if (range === 0) { 3872 return; 3873 } 3874 else { 3875 w = (percent - kfPercents[frame]) / range; 3876 } 3877 if (useSpline) { 3878 p1 = kfValues[frame]; 3879 p0 = kfValues[frame === 0 ? frame : frame - 1]; 3880 p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1]; 3881 p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2]; 3882 if (isValueArray) { 3883 catmullRomInterpolateArray( 3884 p0, p1, p2, p3, w, w * w, w * w * w, 3885 getter(target, propName), 3886 arrDim 3887 ); 3888 } 3889 else { 3890 var value; 3891 if (isValueColor) { 3892 value = catmullRomInterpolateArray( 3893 p0, p1, p2, p3, w, w * w, w * w * w, 3894 rgba, 1 3895 ); 3896 value = rgba2String(rgba); 3897 } 3898 else if (isValueString) { 3899 // String is step(0.5) 3900 return interpolateString(p1, p2, w); 3901 } 3902 else { 3903 value = catmullRomInterpolate( 3904 p0, p1, p2, p3, w, w * w, w * w * w 3905 ); 3906 } 3907 setter( 3908 target, 3909 propName, 3910 value 3911 ); 3912 } 3913 } 3914 else { 3915 if (isValueArray) { 3916 interpolateArray( 3917 kfValues[frame], kfValues[frame + 1], w, 3918 getter(target, propName), 3919 arrDim 3920 ); 3921 } 3922 else { 3923 var value; 3924 if (isValueColor) { 3925 interpolateArray( 3926 kfValues[frame], kfValues[frame + 1], w, 3927 rgba, 1 3928 ); 3929 value = rgba2String(rgba); 3930 } 3931 else if (isValueString) { 3932 // String is step(0.5) 3933 return interpolateString(kfValues[frame], kfValues[frame + 1], w); 3934 } 3935 else { 3936 value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w); 3937 } 3938 setter( 3939 target, 3940 propName, 3941 value 3942 ); 3943 } 3944 } 3945 }; 3946 3947 var clip = new Clip({ 3948 target: animator._target, 3949 life: trackMaxTime, 3950 loop: animator._loop, 3951 delay: animator._delay, 3952 onframe: onframe, 3953 ondestroy: oneTrackDone 3954 }); 3955 3956 if (easing && easing !== 'spline') { 3957 clip.easing = easing; 3958 } 3959 3960 return clip; 3961 } 3962 3963 /** 3964 * @alias module:zrender/animation/Animator 3965 * @constructor 3966 * @param {Object} target 3967 * @param {boolean} loop 3968 * @param {Function} getter 3969 * @param {Function} setter 3970 */ 3971 var Animator = function(target, loop, getter, setter) { 3972 this._tracks = {}; 3973 this._target = target; 3974 3975 this._loop = loop || false; 3976 3977 this._getter = getter || defaultGetter; 3978 this._setter = setter || defaultSetter; 3979 3980 this._clipCount = 0; 3981
3982 this._delay = 0; 3983 3984 this._doneList = []; 3985 3986 this._onframeList = []; 3987 3988 this._clipList = []; 3989 }; 3990 3991 Animator.prototype = { 3992 /** 3993 * çå§çéã§æ¾éæ½æç¯ï¿½ 3994 * @param {number} time éæ½æç¯Ñæ¤éè¾¾ç´éæç¶ éç¬s 3995 * @param {Object} props éæ½æç¯Ñ æ®çç´â¬Ñâ¬ç¡·ç´key-valueçã§ã 3996 * @return {module:zrender/animation/Animator} 3997 */ 3998 when: function(time /* ms */, props) { 3999 var tracks = this._tracks; 4000 for (var propName in props) { 4001 if (!props.hasOwnProperty(propName)) { 4002 continue; 4003 } 4004 4005 if (!tracks[propName]) { 4006 tracks[propName] = []; 4007 // Invalid value 4008 var value = this._getter(this._target, propName); 4009 if (value == null) { 4010 // zrLog('Invalid property ' + propName); 4011 continue; 4012 } 4013 // If time is 0 4014 // Then props is given initialize value 4015 // Else 4016 // Initialize value from current prop value 4017 if (time !== 0) { 4018 tracks[propName].push({ 4019 time: 0, 4020 value: cloneValue(value) 4021 }); 4022 } 4023 } 4024 tracks[propName].push({ 4025 time: time, 4026 value: props[propName] 4027 }); 4028 } 4029 return this; 4030 }, 4031 /** 4032 * 娣诲å§éã§æ¾å§£å¿ç«´ç¯Ñ æ®é¥ç¶çéè¥æ 4033 * @param {Function} callback 4034 * @return {module:zrender/animation/Animator} 4035 */ 4036 during: function (callback) { 4037 this._onframeList.push(callback); 4038 return this; 4039 }, 4040 4041 pause: function () { 4042 for (var i = 0; i < this._clipList.length; i++) { 4043 this._clipList[i].pause(); 4044 } 4045 this._paused = true; 4046 }, 4047 4048 resume: function () { 4049 for (var i = 0; i < this._clipList.length; i++) { 4050 this._clipList[i].resume(); 4051 } 4052 this._paused = false; 4053 }, 4054 4055 isPaused: function () { 4056 return !!this._paused; 4057 }, 4058 4059 _doneCallback: function () { 4060 // Clear all tracks 4061 this._tracks = {}; 4062 // Clear all clips 4063 this._clipList.length = 0; 4064 4065 var doneList = this._doneList; 4066 var len = doneList.length; 4067 for (var i = 0; i < len; i++) { 4068 doneList[i].call(this); 4069 } 4070 }, 4071 /** 4072 * å¯®â¬æ¿®å¬«å¢½ç屽姩é¢ï¿½ 4073 * @param {string|Function} [easing] 4074 * éã§æ¾ç¼æ³å§©éè¥æéå²îçäº@link module:zrender/animation/easing} 4075 * @param {boolean} forceAnimate 4076 * @return {module:zrender/animation/Animator} 4077 */ 4078 start: function (easing, forceAnimate) { 4079 4080 var self = this; 4081 var clipCount = 0; 4082 4083 var oneTrackDone = function() { 4084 clipCount--; 4085 if (!clipCount) { 4086 self._doneCallback(); 4087 } 4088 }; 4089 4090 var lastClip; 4091 for (var propName in this._tracks) { 4092 if (!this._tracks.hasOwnProperty(propName)) { 4093 continue; 4094 } 4095 var clip = createTrackClip( 4096 this, easing, oneTrackDone, 4097 this._tracks[propName], propName, forceAnimate 4098 ); 4099 if (clip) { 4100 this._clipList.push(clip); 4101 clipCount++; 4102 4103 // If start after added to animation 4104 if (this.animation) { 4105 this.animation.addClip(clip); 4106 } 4107 4108 lastClip = clip; 4109 } 4110 } 4111 4112 // Add during callback on the last clip 4113 if (lastClip) {
4114 var oldOnFrame = lastClip.onframe; 4115 lastClip.onframe = function (target, percent) { 4116 oldOnFrame(target, percent); 4117 4118 for (var i = 0; i < self._onframeList.length; i++) { 4119 self._onframeList[i](target, percent); 4120 } 4121 }; 4122 } 4123 4124 // This optimization will help the case that in the upper application 4125 // the view may be refreshed frequently, where animation will be 4126 // called repeatly but nothing changed. 4127 if (!clipCount) { 4128 this._doneCallback(); 4129 } 4130 return this; 4131 }, 4132 /** 4133 * éæ»îéã§æ¾ 4134 * @param {boolean} forwardToLast If move to last frame before stop 4135 */ 4136 stop: function (forwardToLast) { 4137 var clipList = this._clipList; 4138 var animation = this.animation; 4139 for (var i = 0; i < clipList.length; i++) { 4140 var clip = clipList[i]; 4141 if (forwardToLast) { 4142 // Move to last frame before stop 4143 clip.onframe(this._target, 1); 4144 } 4145 animation && animation.removeClip(clip); 4146 } 4147 clipList.length = 0; 4148 }, 4149 /** 4150 * çå§çéã§æ¾å¯¤æ°ç¹å¯®â¬æ¿®å¬¬æ®éå æ£¿ 4151 * @param {number} time éæç¶ ms 4152 * @return {module:zrender/animation/Animator} 4153 */ 4154 delay: function (time) { 4155 this._delay = time; 4156 return this; 4157 }, 4158 /** 4159 * 娣诲å§éã§æ¾ç¼æ´æ½«é¨å«æ´ç� 4160 * @param {Function} cb 4161 * @return {module:zrender/animation/Animator} 4162 */ 4163 done: function(cb) { 4164 if (cb) { 4165 this._doneList.push(cb); 4166 } 4167 return this; 4168 }, 4169 4170 /** 4171 * @return {Array.<module:zrender/animation/Clip>} 4172 */ 4173 getClips: function () { 4174 return this._clipList; 4175 } 4176 }; 4177 4178 var dpr = 1; 4179 4180// If in browser environment 4181 if (typeof window !== 'undefined') { 4182 dpr = Math.max(window.devicePixelRatio || 1, 1); 4183 } 4184 4185 /** 4186 * configæ¦æ¨¿î »é°å¶ç椤� 4187 * @exports zrender/config 4188 * @author Kener (@Kener-鿥å², [email protected]) 4189 */ 4190 4191 /** 4192 * debugéã¥ç¹é«å¤ãéæatchBrushExceptionæ¶ç°rueæ¶å¬«æ¹é� 4193 * 0 : æ¶å¶æé´æebugéçåµéå±½å½ç¯åªæ¤ 4194 * 1 : 寮åç¶é¶æ¶åéå²ççæ æ¤ 4195 * 2 : éºÑåéæç·éç´ç´çåç¯é¢ï¿½ 4196 */ 4197 var debugMode = 0; 4198 4199// retina çå¿ç®·æµ¼æ¨ºå¯² 4200 var devicePixelRatio = dpr; 4201 4202 var log = function () { 4203 }; 4204 4205 if (debugMode === 1) { 4206 log = function () { 4207 for (var k in arguments) { 4208 throw new Error(arguments[k]); 4209 } 4210 }; 4211 } 4212 else if (debugMode > 1) { 4213 log = function () { 4214 for (var k in arguments) { 4215 console.log(arguments[k]); 4216 } 4217 }; 4218 } 4219 4220 var zrLog = log; 4221 4222 /** 4223 * @alias modue:zrender/mixin/Animatable 4224 * @constructor 4225 */ 4226 var Animatable = function () { 4227 4228 /** 4229 * @type {Array.<module:zrender/animation/Animator>} 4230 * @readOnly 4231 */ 4232 this.animators = []; 4233 }; 4234 4235 Animatable.prototype = { 4236 4237 constructor: Animatable, 4238 4239 /** 4240 * éã§æ¾ 4241 * 4242 * @param {string} path The path to fetch value from object, like 'a.b.c'. 4243 * @param {boolean} [loop] Whether to loop animation. 4244 * @return {module:zrender/animation/Animator} 4245 * @example: 4246 * el.animate('style', false) 4247 * .when(1000, {x: 10} ) 4248 * .done(function(){ // Animation done }) 4249 * .start() 4250 */ 4251 animate: function (path, loop) { 4252 var target; 4253 var animatingShape = false; 4254 var el = this; 4255 var zr = this.__zr; 4256 if (path) { 4257 var pathSplitted = path.split('.'); 4258 var prop = el; 4259 // If animating shape 4260 animatingShape = pathSplitted[0] === 'shape'; 4261 for (var i = 0, l = pathSplitted.length; i < l; i++) { 4262 if (!prop) { 4263 continue; 4264 } 4265 prop = prop[pathSplitted[i]]; 4266 } 4267 if (prop) { 4268 target = prop; 4269 } 4270 } 4271 else { 4272 target = el; 4273 } 4274 4275 if (!target) {
4276 zrLog( 4277 'Property "' 4278 + path 4279 + '" is not existed in element ' 4280 + el.id 4281 ); 4282 return; 4283 } 4284 4285 var animators = el.animators; 4286 4287 var animator = new Animator(target, loop); 4288 4289 animator.during(function (target) { 4290 el.dirty(animatingShape); 4291 }) 4292 .done(function () { 4293 // FIXME Animator will not be removed if use `Animator#stop` to stop animation 4294 animators.splice(indexOf(animators, animator), 1); 4295 }); 4296 4297 animators.push(animator); 4298 4299 // If animate after added to the zrender 4300 if (zr) { 4301 zr.animation.addAnimator(animator); 4302 } 4303 4304 return animator; 4305 }, 4306 4307 /** 4308 * éæ»îéã§æ¾ 4309 * @param {boolean} forwardToLast If move to last frame before stop 4310 */ 4311 stopAnimation: function (forwardToLast) { 4312 var animators = this.animators; 4313 var len = animators.length; 4314 for (var i = 0; i < len; i++) { 4315 animators[i].stop(forwardToLast); 4316 } 4317 animators.length = 0; 4318 4319 return this; 4320 }, 4321 4322 /** 4323 * Caution: this method will stop previous animation. 4324 * So do not use this method to one element twice before 4325 * animation starts, unless you know what you are doing. 4326 * @param {Object} target 4327 * @param {number} [time=500] Time in ms 4328 * @param {string} [easing='linear'] 4329 * @param {number} [delay=0] 4330 * @param {Function} [callback] 4331 * @param {Function} [forceAnimate] Prevent stop animation and callback 4332 * immediently when target values are the same as current values. 4333 * 4334 * @example 4335 * // Animate position 4336 * el.animateTo({ 4337 * position: [10, 10] 4338 * }, function () { // done }) 4339 * 4340 * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing 4341 * el.animateTo({ 4342 * shape: { 4343 * width: 500 4344 * }, 4345 * style: { 4346 * fill: 'red' 4347 * } 4348 * position: [10, 10] 4349 * }, 100, 100, 'cubicOut', function () { // done }) 4350 */ 4351 // TODO Return animation key 4352 animateTo: function (target, time, delay, easing, callback, forceAnimate) { 4353 // animateTo(target, time, easing, callback); 4354 if (isString(delay)) { 4355 callback = easing; 4356 easing = delay; 4357 delay = 0; 4358 } 4359 // animateTo(target, time, delay, callback); 4360 else if (isFunction$1(easing)) { 4361 callback = easing; 4362 easing = 'linear'; 4363 delay = 0; 4364 } 4365 // animateTo(target, time, callback); 4366 else if (isFunction$1(delay)) { 4367 callback = delay; 4368 delay = 0; 4369 } 4370 // animateTo(target, callback) 4371 else if (isFunction$1(time)) { 4372 callback = time; 4373 time = 500; 4374 } 4375 // animateTo(target) 4376 else if (!time) { 4377 time = 500; 4378 } 4379 // Stop all previous animations 4380 this.stopAnimation(); 4381 this._animateToShallow('', this, target, time, delay); 4382 4383 // Animators may be removed immediately after start 4384 // if there is nothing to animate 4385 var animators = this.animators.slice(); 4386 var count = animators.length; 4387 function done() { 4388 count--; 4389 if (!count) { 4390 callback && callback(); 4391 } 4392 } 4393 4394 // No animators. This should be checked before animators[i].start(), 4395 // because 'done' may be executed immediately if no need to animate. 4396 if (!count) { 4397 callback && callback(); 4398 } 4399 // Start after all animators created 4400 // Incase any animator is done immediately when all animation properties are not changed 4401 for (var i = 0; i < animators.length; i++) { 4402 animators[i] 4403 .done(done) 4404 .start(easing, forceAnimate); 4405 } 4406 }, 4407 4408 /** 4409 * @private 4410 * @param {string} path='' 4411 * @param {Object} source=this 4412 * @param {Object} target 4413 * @param {number} [time=500] 4414 * @param {number} [delay=0] 4415 * 4416 * @example 4417 * // Animate position 4418 * el._animateToShallow({ 4419 * position: [10, 10] 4420 * }) 4421 * 4422 * // Animate shape, style and position in 100ms, delayed 100ms 4423 * el._animateToShallow({ 4424 * shape: { 4425 * width: 500 4426 * }, 4427 * style: { 4428 * fill: 'red' 4429 * } 4430 * position: [10, 10] 4431 * }, 100, 100) 4432 */ 4433 _animateToShallow: function (path, source, target, time, delay) { 4434 var objShallow = {}; 4435 var propertyCount = 0; 4436 for (var name in target) { 4437 if (!target.hasOwnProperty(name)) { 4438 continue; 4439 } 4440 4441 if (source[name] != null) { 4442 if (isObject$1(target[name]) && !isArrayLike(target[name])) { 4443 this._animateToShallow( 4444 path ? path + '.' + name : name, 4445 source[name], 4446 target[name], 4447 time, 4448 delay 4449 ); 4450 } 4451 else { 4452 objShallow[name] = target[name]; 4453 propertyCount++; 4454 } 4455 } 4456 else if (target[name] != null) { 4457 // Attr directly if not has property 4458 // FIXME, if some property not needed for element ? 4459 if (!path) { 4460 this.attr(name, target[name]); 4461 } 4462 else { // Shape or style 4463 var props = {}; 4464 props[path] = {}; 4465 props[path][name] = target[name]; 4466 this.attr(props); 4467 } 4468 } 4469 } 4470 4471 if (propertyCount > 0) { 4472 this.animate(path, false) 4473 .when(time == null ? 500 : time, objShallow) 4474 .delay(delay || 0); 4475 } 4476 4477 return this; 4478 } 4479 }; 4480 4481 /** 4482 * @alias module:zrender/Element 4483 * @constructor 4484 * @extends {module:zrender/mixin/Animatable} 4485 * @extends {module:zrender/mixin/Transformable} 4486 * @extends {module:zrender/mixin/Eventful} 4487 */ 4488 var Element = function (opts) { // jshint ignore:line 4489 4490 Transformable.call(this, opts); 4491 Eventful.call(this, opts); 4492 Animatable.call(this, opts); 4493 4494 /** 4495 * é¢è¯²ç«·éåªç¤ID 4496 * @type {string} 4497 */ 4498 this.id = opts.id || guid(); 4499 }; 4500 4501 Element.prototype = { 4502 4503 /** 4504 * éåªç¤ç»«è¯²ç· 4505 * Element type 4506 * @type {string} 4507 */ 4508 type: 'element', 4509 4510 /** 4511 * éåªç¤éå¶ ç§ 4512 * Element name 4513 * @type {string} 4514 */ 4515 name: '', 4516 4517 /** 4518 * ZRender ç¹ç°ç·¥çµç¡ èé屼細é¦ï¿½ element 娣诲å§é� zrender ç¹ç°ç·¥æ¶î æé·îå§©ç§å¬ªâ¬ï¿½ 4519 * ZRender instance will be assigned when element is associated with zrender
4520 * @name module:/zrender/Element#__zr 4521 * @type {module:zrender/ZRender} 4522 */ 4523 __zr: null, 4524 4525 /** 4526 * 饿§è°éîæè¹çæéå±¼è´trueéè·ºæ·é£ã¥æµè¤°ã¢æ®ç¼æ¨ºåæµ ã¥å¼·æµå¬©æ¬¢çï¹å½ 4527 * If ignore drawing and events of the element object 4528 * @name module:/zrender/Element#ignore 4529 * @type {boolean} 4530 * @default false 4531 */ 4532 ignore: false, 4533 4534 /** 4535 * é¢ã¤ç°¬ç佸å£é¨å®ç¾å¯°ï¿½(shape)éå±¾å¢é� Group é坿®çºîç·é¦ã§ç²¯éèµæ¤é®æç´°çî¥ç¹æ¶îç¾å¯°å®îé� 4536 * çã¨ç¾å¯°åªç´°ç¼Ñå£çî¥îéå¿î®çï¼æ®é樻崲 4537 * @type {module:zrender/graphic/Path} 4538 * @see http://www.w3.org/TR/2dcontext/#clipping-region 4539 * @readOnly 4540 */ 4541 clipPath: null, 4542 4543 /** 4544 * éîæé� Group 4545 * @type {boolean} 4546 */ 4547 isGroup: false, 4548 4549 /** 4550 * Drift element 4551 * @param {number} dx dx on the global space 4552 * @param {number} dy dy on the global space 4553 */ 4554 drift: function (dx, dy) { 4555 switch (this.draggable) { 4556 case 'horizontal': 4557 dy = 0; 4558 break; 4559 case 'vertical': 4560 dx = 0; 4561 break; 4562 } 4563 4564 var m = this.transform; 4565 if (!m) { 4566 m = this.transform = [1, 0, 0, 1, 0, 0]; 4567 } 4568 m[4] += dx; 4569 m[5] += dy; 4570 4571 this.decomposeTransform(); 4572 this.dirty(false); 4573 }, 4574 4575 /** 4576 * Hook before update 4577 */ 4578 beforeUpdate: function () {}, 4579 /** 4580 * Hook after update 4581 */ 4582 afterUpdate: function () {}, 4583 /** 4584 * Update each frame 4585 */ 4586 update: function () { 4587 this.updateTransform(); 4588 }, 4589 4590 /** 4591 * @param {Function} cb 4592 * @param {} context 4593 */ 4594 traverse: function (cb, context) {}, 4595 4596 /** 4597 * @protected 4598 */ 4599 attrKV: function (key, value) { 4600 if (key === 'position' || key === 'scale' || key === 'origin') { 4601 // Copy the array 4602 if (value) { 4603 var target = this[key]; 4604 if (!target) { 4605 target = this[key] = []; 4606 } 4607 target[0] = value[0]; 4608 target[1] = value[1]; 4609 } 4610 } 4611 else { 4612 this[key] = value; 4613 } 4614 }, 4615 4616 /** 4617 * Hide the element 4618 */ 4619 hide: function () { 4620 this.ignore = true; 4621 this.__zr && this.__zr.refresh(); 4622 }, 4623 4624 /** 4625 * Show the element 4626 */ 4627 show: function () { 4628 this.ignore = false; 4629 this.__zr && this.__zr.refresh(); 4630 }, 4631 4632 /** 4633 * @param {string|Object} key 4634 * @param {*} value 4635 */ 4636 attr: function (key, value) { 4637 if (typeof key === 'string') { 4638 this.attrKV(key, value); 4639 } 4640 else if (isObject$1(key)) { 4641 for (var name in key) { 4642 if (key.hasOwnProperty(name)) { 4643 this.attrKV(name, key[name]); 4644 } 4645 } 4646 } 4647 4648 this.dirty(false); 4649 4650 return this; 4651 }, 4652 4653 /** 4654 * @param {module:zrender/graphic/Path} clipPath 4655 */ 4656 setClipPath: function (clipPath) { 4657 var zr = this.__zr; 4658 if (zr) { 4659 clipPath.addSelfToZr(zr); 4660 } 4661 4662 // Remove previous clip path 4663 if (this.clipPath && this.clipPath !== clipPath) { 4664 this.removeClipPath(); 4665 } 4666 4667 this.clipPath = clipPath; 4668 clipPath.__zr = zr; 4669 clipPath.__clipTarget = this; 4670 4671 this.dirty(false); 4672 }, 4673 4674 /** 4675 */ 4676 removeClipPath: function () { 4677 var clipPath = this.clipPath; 4678 if (clipPath) { 4679 if (clipPath.__zr) { 4680 clipPath.removeSelfFromZr(clipPath.__zr); 4681 } 4682 4683 clipPath.__zr = null; 4684 clipPath.__clipTarget = null; 4685 this.clipPath = null; 4686 4687 this.dirty(false); 4688 } 4689 }, 4690 4691 /** 4692 * Add self from zrender instance. 4693 * Not recursively because it will be invoked when element added to storage. 4694 * @param {module:zrender/ZRender} zr 4695 */ 4696 addSelfToZr: function (zr) { 4697 this.__zr = zr; 4698 // 娣诲å§éã§æ¾ 4699 var animators = this.animators; 4700 if (animators) { 4701 for (var i = 0; i < animators.length; i++) { 4702 zr.animation.addAnimator(animators[i]); 4703 } 4704 } 4705 4706 if (this.clipPath) { 4707 this.clipPath.addSelfToZr(zr); 4708 } 4709 }, 4710 4711 /** 4712 * Remove self from zrender instance. 4713 * Not recursively because it will be invoked when element added to storage. 4714 * @param {module:zrender/ZRender} zr 4715 */ 4716 removeSelfFromZr: function (zr) { 4717 this.__zr = null; 4718 // ç»å©æ«éã§æ¾ 4719 var animators = this.animators; 4720 if (animators) { 4721 for (var i = 0; i < animators.length; i++) { 4722 zr.animation.removeAnimator(animators[i]); 4723 } 4724 } 4725 4726 if (this.clipPath) { 4727 this.clipPath.removeSelfFromZr(zr); 4728 } 4729 } 4730 }; 4731 4732 mixin(Element, Animatable); 4733 mixin(Element, Transformable); 4734 mixin(Element, Eventful); 4735 4736 /** 4737 * @module echarts/core/BoundingRect 4738 */ 4739 4740 var v2ApplyTransform = applyTransform; 4741 var mathMin = Math.min; 4742 var mathMax = Math.max; 4743 4744 /** 4745 * @alias module:echarts/core/BoundingRect 4746 */
4747 function BoundingRect(x, y, width, height) { 4748 4749 if (width < 0) { 4750 x = x + width; 4751 width = -width; 4752 } 4753 if (height < 0) { 4754 y = y + height; 4755 height = -height; 4756 } 4757 4758 /** 4759 * @type {number} 4760 */ 4761 this.x = x; 4762 /** 4763 * @type {number} 4764 */ 4765 this.y = y; 4766 /** 4767 * @type {number} 4768 */ 4769 this.width = width; 4770 /** 4771 * @type {number} 4772 */ 4773 this.height = height; 4774 } 4775 4776 BoundingRect.prototype = { 4777 4778 constructor: BoundingRect, 4779 4780 /** 4781 * @param {module:echarts/core/BoundingRect} other 4782 */ 4783 union: function (other) { 4784 var x = mathMin(other.x, this.x); 4785 var y = mathMin(other.y, this.y); 4786 4787 this.width = mathMax( 4788 other.x + other.width, 4789 this.x + this.width 4790 ) - x; 4791 this.height = mathMax( 4792 other.y + other.height, 4793 this.y + this.height 4794 ) - y; 4795 this.x = x; 4796 this.y = y; 4797 }, 4798 4799 /** 4800 * @param {Array.<number>} m 4801 * @methods 4802 */ 4803 applyTransform: (function () { 4804 var lt = []; 4805 var rb = []; 4806 var lb = []; 4807 var rt = []; 4808 return function (m) { 4809 // In case usage like this 4810 // el.getBoundingRect().applyTransform(el.transform) 4811 // And element has no transform 4812 if (!m) { 4813 return; 4814 } 4815 lt[0] = lb[0] = this.x; 4816 lt[1] = rt[1] = this.y; 4817 rb[0] = rt[0] = this.x + this.width; 4818 rb[1] = lb[1] = this.y + this.height; 4819 4820 v2ApplyTransform(lt, lt, m); 4821 v2ApplyTransform(rb, rb, m); 4822 v2ApplyTransform(lb, lb, m); 4823 v2ApplyTransform(rt, rt, m); 4824 4825 this.x = mathMin(lt[0], rb[0], lb[0], rt[0]); 4826 this.y = mathMin(lt[1], rb[1], lb[1], rt[1]); 4827 var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]); 4828 var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]); 4829 this.width = maxX - this.x; 4830 this.height = maxY - this.y; 4831 }; 4832 })(), 4833 4834 /** 4835 * Calculate matrix of transforming from self to target rect 4836 * @param {module:zrender/core/BoundingRect} b 4837 * @return {Array.<number>} 4838 */ 4839 calculateTransform: function (b) { 4840 var a = this; 4841 var sx = b.width / a.width; 4842 var sy = b.height / a.height; 4843 4844 var m = create$1(); 4845 4846 // éâ æ¨éå ç®» 4847 translate(m, m, [-a.x, -a.y]); 4848 scale$1(m, m, [sx, sy]); 4849 translate(m, m, [b.x, b.y]); 4850 4851 return m; 4852 }, 4853 4854 /** 4855 * @param {(module:echarts/core/BoundingRect|Object)} b 4856 * @return {boolean} 4857 */ 4858 intersect: function (b) { 4859 if (!b) { 4860 return false; 4861 } 4862 4863 if (!(b instanceof BoundingRect)) { 4864 // Normalize negative width/height. 4865 b = BoundingRect.create(b); 4866 } 4867 4868 var a = this; 4869 var ax0 = a.x; 4870 var ax1 = a.x + a.width; 4871 var ay0 = a.y; 4872 var ay1 = a.y + a.height; 4873 4874 var bx0 = b.x; 4875 var bx1 = b.x + b.width; 4876 var by0 = b.y; 4877 var by1 = b.y + b.height; 4878 4879 return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0); 4880 }, 4881 4882 contain: function (x, y) { 4883 var rect = this; 4884 return x >= rect.x 4885 && x <= (rect.x + rect.width) 4886 && y >= rect.y 4887 && y <= (rect.y + rect.height); 4888 }, 4889 4890 /** 4891 * @return {module:echarts/core/BoundingRect} 4892 */ 4893 clone: function () {
4894 return new BoundingRect(this.x, this.y, this.width, this.height); 4895 }, 4896 4897 /** 4898 * Copy from another rect 4899 */ 4900 copy: function (other) { 4901 this.x = other.x; 4902 this.y = other.y; 4903 this.width = other.width; 4904 this.height = other.height; 4905 }, 4906 4907 plain: function () { 4908 return { 4909 x: this.x, 4910 y: this.y, 4911 width: this.width, 4912 height: this.height 4913 }; 4914 } 4915 }; 4916 4917 /** 4918 * @param {Object|module:zrender/core/BoundingRect} rect 4919 * @param {number} rect.x 4920 * @param {number} rect.y 4921 * @param {number} rect.width 4922 * @param {number} rect.height 4923 * @return {module:zrender/core/BoundingRect} 4924 */ 4925 BoundingRect.create = function (rect) { 4926 return new BoundingRect(rect.x, rect.y, rect.width, rect.height); 4927 }; 4928 4929 /** 4930 * Groupéîç«´æ¶îîé£îç´éîäºé»æåçæ¯å¦éç¸ç´Groupé¨å«å½é¹îç¯æµ¼æ°³î¦æ´ææ¤éæ¿çéºå壿¶ï¿½ 4931 * @module zrender/graphic/Group 4932 * @example 4933 * var Group = require('zrender/container/Group'); 4934 * var Circle = require('zrender/graphic/shape/Circle'); 4935 * var g = new Group(); 4936 * g.position[0] = 100; 4937 * g.position[1] = 100; 4938 * g.add(new Circle({ 4939 * style: { 4940 * x: 100, 4941 * y: 100, 4942 * r: 20, 4943 * } 4944 * })); 4945 * zr.add(g); 4946 */ 4947 4948 /** 4949 * @alias module:zrender/graphic/Group 4950 * @constructor 4951 * @extends module:zrender/mixin/Transformable 4952 * @extends module:zrender/mixin/Eventful 4953 */ 4954 var Group = function (opts) { 4955 4956 opts = opts || {}; 4957 4958 Element.call(this, opts); 4959 4960 for (var key in opts) { 4961 if (opts.hasOwnProperty(key)) { 4962 this[key] = opts[key]; 4963 } 4964 } 4965 4966 this._children = []; 4967 4968 this.__storage = null; 4969 4970 this.__dirty = true; 4971 }; 4972 4973 Group.prototype = { 4974 4975 constructor: Group, 4976 4977 isGroup: true, 4978 4979 /** 4980 * @type {string} 4981 */ 4982 type: 'group', 4983 4984 /** 4985 * éµâ¬éå¤ççæ¬åç»±ç³æ§¸éï¹æ·æ´æç´¶éå¦ç°¨æµ � 4986 * @name module:/zrender/container/Group#silent 4987 * @type {boolean} 4988 * @default false 4989 */ 4990 silent: false, 4991 4992 /** 4993 * @return {Array.<module:zrender/Element>} 4994 */ 4995 children: function () { 4996 return this._children.slice(); 4997 }, 4998 4999 /** 5000 * é¾å³°å½é¸å§ç¾ index é¨å«å¹çæ¯å¦é� 5001 * @param {number} idx 5002 * @return {module:zrender/Element} 5003 */ 5004 childAt: function (idx) { 5005 return this._children[idx]; 5006 }, 5007 5008 /** 5009 * é¾å³°å½é¸å§ç¾éå¶ ç§é¨å«å¹çæ¯å¦é� 5010 * @param {string} name 5011 * @return {module:zrender/Element} 5012 */ 5013 childOfName: function (name) { 5014 var children = this._children; 5015 for (var i = 0; i < children.length; i++) { 5016 if (children[i].name === name) { 5017 return children[i]; 5018 } 5019 } 5020 }, 5021 5022 /** 5023 * @return {number} 5024 */ 5025 childCount: function () { 5026 return this._children.length; 5027 }, 5028 5029 /** 5030 * 娣诲å§çæ¯å¦éç°åéâ¬é� 5031 * @param {module:zrender/Element} child 5032 */ 5033 add: function (child) { 5034 if (child && child !== this && child.parent !== this) { 5035 5036 this._children.push(child); 5037 5038 this._doAdd(child); 5039 } 5040 5041 return this; 5042 }, 5043 5044 /** 5045 * 娣诲å§çæ¯å¦éç°æ¹ª nextSibling æ¶å¬ªå¢ 5046 * @param {module:zrender/Element} child 5047 * @param {module:zrender/Element} nextSibling 5048 */ 5049 addBefore: function (child, nextSibling) { 5050 if (child && child !== this && child.parent !== this 5051 && nextSibling && nextSibling.parent === this) { 5052 5053 var children = this._children; 5054 var idx = children.indexOf(nextSibling); 5055 5056 if (idx >= 0) { 5057 children.splice(idx, 0, child); 5058 this._doAdd(child); 5059 } 5060 } 5061 5062 return this; 5063 }, 5064 5065 _doAdd: function (child) { 5066 if (child.parent) { 5067 child.parent.remove(child); 5068 } 5069 5070 child.parent = this; 5071 5072 var storage = this.__storage; 5073 var zr = this.__zr; 5074 if (storage && storage !== child.__storage) { 5075
5076 storage.addToStorage(child); 5077 5078 if (child instanceof Group) { 5079 child.addChildrenToStorage(storage); 5080 } 5081 } 5082 5083 zr && zr.refresh(); 5084 }, 5085 5086 /** 5087 * ç»å©æ«çæ¯å¦é� 5088 * @param {module:zrender/Element} child 5089 */ 5090 remove: function (child) { 5091 var zr = this.__zr; 5092 var storage = this.__storage; 5093 var children = this._children; 5094 5095 var idx = indexOf(children, child); 5096 if (idx < 0) { 5097 return this; 5098 } 5099 children.splice(idx, 1); 5100 5101 child.parent = null; 5102 5103 if (storage) { 5104 5105 storage.delFromStorage(child); 5106 5107 if (child instanceof Group) { 5108 child.delChildrenFromStorage(storage); 5109 } 5110 } 5111 5112 zr && zr.refresh(); 5113 5114 return this; 5115 }, 5116 5117 /** 5118 * ç»å©æ«éµâ¬éå¤çéºåå£ 5119 */ 5120 removeAll: function () { 5121 var children = this._children; 5122 var storage = this.__storage; 5123 var child; 5124 var i; 5125 for (i = 0; i < children.length; i++) { 5126 child = children[i]; 5127 if (storage) { 5128 storage.delFromStorage(child); 5129 if (child instanceof Group) { 5130 child.delChildrenFromStorage(storage); 5131 } 5132 } 5133 child.parent = null; 5134 } 5135 children.length = 0; 5136 5137 return this; 5138 }, 5139 5140 /** 5141 * é¬å¶ å·»éµâ¬éå¤çéºåå£ 5142 * @param {Function} cb 5143 * @param {} context 5144 */ 5145 eachChild: function (cb, context) { 5146 var children = this._children; 5147 for (var i = 0; i < children.length; i++) { 5148 var child = children[i]; 5149 cb.call(context, child, i); 5150 } 5151 return this; 5152 }, 5153 5154 /** 5155 * 娣å害浼樺åé¬å¶ å·»éµâ¬éå¤ççæ¬å¦é� 5156 * @param {Function} cb 5157 * @param {} context 5158 */ 5159 traverse: function (cb, context) { 5160 for (var i = 0; i < this._children.length; i++) { 5161 var child = this._children[i]; 5162 cb.call(context, child); 5163 5164 if (child.type === 'group') { 5165 child.traverse(cb, context); 5166 } 5167 } 5168 return this; 5169 }, 5170 5171 addChildrenToStorage: function (storage) { 5172 for (var i = 0; i < this._children.length; i++) { 5173 var child = this._children[i]; 5174 storage.addToStorage(child); 5175 if (child instanceof Group) { 5176 child.addChildrenToStorage(storage); 5177 } 5178 } 5179 }, 5180 5181 delChildrenFromStorage: function (storage) { 5182 for (var i = 0; i < this._children.length; i++) { 5183 var child = this._children[i]; 5184 storage.delFromStorage(child); 5185 if (child instanceof Group) { 5186 child.delChildrenFromStorage(storage); 5187 } 5188 } 5189 }, 5190 5191 dirty: function () { 5192 this.__dirty = true; 5193 this.__zr && this.__zr.refresh(); 5194 return this; 5195 }, 5196 5197 /** 5198 * @return {module:zrender/core/BoundingRect} 5199 */ 5200 getBoundingRect: function (includeChildren) { 5201 // TODO Caching 5202 var rect = null; 5203 var tmpRect = new BoundingRect(0, 0, 0, 0); 5204 var children = includeChildren || this._children; 5205 var tmpMat = []; 5206 5207 for (var i = 0; i < children.length; i++) { 5208 var child = children[i]; 5209 if (child.ignore || child.invisible) { 5210 continue; 5211 } 5212
vendor: 18,107 bytes, lines 5213-5825
5213 var childRect = child.getBoundingRect(); 5214 var transform = child.getLocalTransform(tmpMat); 5215 // TODO 5216 // The boundingRect cacluated by transforming original 5217 // rect may be bigger than the actual bundingRect when rotation 5218 // is used. (Consider a circle rotated aginst its center, where 5219 // the actual boundingRect should be the same as that not be 5220 // rotated.) But we can not find better approach to calculate 5221 // actual boundingRect yet, considering performance. 5222 if (transform) { 5223 tmpRect.copy(childRect); 5224 tmpRect.applyTransform(transform); 5225 rect = rect || tmpRect.clone(); 5226 rect.union(tmpRect); 5227 } 5228 else { 5229 rect = rect || childRect.clone(); 5230 rect.union(childRect); 5231 } 5232 } 5233 return rect || tmpRect; 5234 } 5235 }; 5236 5237 inherits(Group, Element); 5238 5239// https://github.com/mziccard/node-timsort 5240 var DEFAULT_MIN_MERGE = 32; 5241 5242 var DEFAULT_MIN_GALLOPING = 7; 5243 5244 function minRunLength(n) { 5245 var r = 0; 5246 5247 while (n >= DEFAULT_MIN_MERGE) { 5248 r |= n & 1; 5249 n >>= 1; 5250 } 5251 5252 return n + r; 5253 } 5254 5255 function makeAscendingRun(array, lo, hi, compare) { 5256 var runHi = lo + 1; 5257 5258 if (runHi === hi) { 5259 return 1; 5260 } 5261 5262 if (compare(array[runHi++], array[lo]) < 0) { 5263 while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) { 5264 runHi++; 5265 } 5266 5267 reverseRun(array, lo, runHi); 5268 } 5269 else { 5270 while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) { 5271 runHi++; 5272 } 5273 } 5274 5275 return runHi - lo; 5276 } 5277 5278 function reverseRun(array, lo, hi) { 5279 hi--; 5280 5281 while (lo < hi) { 5282 var t = array[lo]; 5283 array[lo++] = array[hi]; 5284 array[hi--] = t; 5285 } 5286 } 5287 5288 function binaryInsertionSort(array, lo, hi, start, compare) { 5289 if (start === lo) { 5290 start++; 5291 } 5292 5293 for (; start < hi; start++) { 5294 var pivot = array[start]; 5295 5296 var left = lo; 5297 var right = start; 5298 var mid; 5299 5300 while (left < right) { 5301 mid = left + right >>> 1; 5302 5303 if (compare(pivot, array[mid]) < 0) { 5304 right = mid; 5305 } 5306 else { 5307 left = mid + 1; 5308 } 5309 } 5310 5311 var n = start - left; 5312 5313 switch (n) { 5314 case 3: 5315 array[left + 3] = array[left + 2]; 5316 5317 case 2: 5318 array[left + 2] = array[left + 1]; 5319 5320 case 1: 5321 array[left + 1] = array[left]; 5322 break; 5323 default: 5324 while (n > 0) { 5325 array[left + n] = array[left + n - 1]; 5326 n--; 5327 } 5328 } 5329 5330 array[left] = pivot; 5331 } 5332 } 5333 5334 function gallopLeft(value, array, start, length, hint, compare) { 5335 var lastOffset = 0; 5336 var maxOffset = 0; 5337 var offset = 1; 5338 5339 if (compare(value, array[start + hint]) > 0) { 5340 maxOffset = length - hint; 5341 5342 while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) { 5343 lastOffset = offset; 5344 offset = (offset << 1) + 1; 5345 5346 if (offset <= 0) { 5347 offset = maxOffset; 5348 } 5349 } 5350 5351 if (offset > maxOffset) { 5352 offset = maxOffset; 5353 } 5354 5355 lastOffset += hint; 5356 offset += hint; 5357 } 5358 else { 5359 maxOffset = hint + 1; 5360 while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) { 5361 lastOffset = offset; 5362 offset = (offset << 1) + 1; 5363 5364 if (offset <= 0) { 5365 offset = maxOffset; 5366 } 5367 } 5368 if (offset > maxOffset) { 5369 offset = maxOffset; 5370 } 5371 5372 var tmp = lastOffset; 5373 lastOffset = hint - offset; 5374 offset = hint - tmp; 5375 } 5376 5377 lastOffset++; 5378 while (lastOffset < offset) { 5379 var m = lastOffset + (offset - lastOffset >>> 1); 5380 5381 if (compare(value, array[start + m]) > 0) { 5382 lastOffset = m + 1; 5383 } 5384 else { 5385 offset = m; 5386 } 5387 } 5388 return offset; 5389 } 5390 5391 function gallopRight(value, array, start, length, hint, compare) { 5392 var lastOffset = 0; 5393 var maxOffset = 0; 5394 var offset = 1; 5395 5396 if (compare(value, array[start + hint]) < 0) { 5397 maxOffset = hint + 1; 5398 5399 while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) { 5400 lastOffset = offset; 5401 offset = (offset << 1) + 1; 5402 5403 if (offset <= 0) { 5404 offset = maxOffset; 5405 } 5406 } 5407 5408 if (offset > maxOffset) { 5409 offset = maxOffset; 5410 } 5411 5412 var tmp = lastOffset; 5413 lastOffset = hint - offset; 5414 offset = hint - tmp; 5415 } 5416 else { 5417 maxOffset = length - hint; 5418 5419 while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) { 5420 lastOffset = offset; 5421 offset = (offset << 1) + 1; 5422 5423 if (offset <= 0) { 5424 offset = maxOffset; 5425 } 5426 } 5427 5428 if (offset > maxOffset) { 5429 offset = maxOffset; 5430 } 5431 5432 lastOffset += hint; 5433 offset += hint; 5434 } 5435 5436 lastOffset++; 5437 5438 while (lastOffset < offset) { 5439 var m = lastOffset + (offset - lastOffset >>> 1); 5440 5441 if (compare(value, array[start + m]) < 0) { 5442 offset = m; 5443 } 5444 else { 5445 lastOffset = m + 1; 5446 } 5447 } 5448 5449 return offset; 5450 } 5451 5452 function TimSort(array, compare) { 5453 var minGallop = DEFAULT_MIN_GALLOPING; 5454 var runStart; 5455 var runLength; 5456 var stackSize = 0; 5457 5458 var tmp = []; 5459 5460 runStart = []; 5461 runLength = []; 5462 5463 function pushRun(_runStart, _runLength) { 5464 runStart[stackSize] = _runStart; 5465 runLength[stackSize] = _runLength; 5466 stackSize += 1; 5467 } 5468 5469 function mergeRuns() { 5470 while (stackSize > 1) { 5471 var n = stackSize - 2; 5472 5473 if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) { 5474 if (runLength[n - 1] < runLength[n + 1]) { 5475 n--; 5476 } 5477 } 5478 else if (runLength[n] > runLength[n + 1]) { 5479 break; 5480 } 5481 mergeAt(n); 5482 } 5483 } 5484 5485 function forceMergeRuns() { 5486 while (stackSize > 1) { 5487 var n = stackSize - 2; 5488 5489 if (n > 0 && runLength[n - 1] < runLength[n + 1]) { 5490 n--; 5491 } 5492 5493 mergeAt(n); 5494 } 5495 } 5496 5497 function mergeAt(i) { 5498 var start1 = runStart[i]; 5499 var length1 = runLength[i]; 5500 var start2 = runStart[i + 1]; 5501 var length2 = runLength[i + 1]; 5502 5503 runLength[i] = length1 + length2; 5504 5505 if (i === stackSize - 3) { 5506 runStart[i + 1] = runStart[i + 2]; 5507 runLength[i + 1] = runLength[i + 2]; 5508 } 5509 5510 stackSize--; 5511 5512 var k = gallopRight(array[start2], array, start1, length1, 0, compare); 5513 start1 += k; 5514 length1 -= k; 5515 5516 if (length1 === 0) { 5517 return; 5518 } 5519 5520 length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare); 5521 5522 if (length2 === 0) { 5523 return; 5524 } 5525 5526 if (length1 <= length2) { 5527 mergeLow(start1, length1, start2, length2); 5528 } 5529 else { 5530 mergeHigh(start1, length1, start2, length2); 5531 } 5532 } 5533 5534 function mergeLow(start1, length1, start2, length2) { 5535 var i = 0; 5536 5537 for (i = 0; i < length1; i++) { 5538 tmp[i] = array[start1 + i]; 5539 } 5540 5541 var cursor1 = 0; 5542 var cursor2 = start2; 5543 var dest = start1; 5544 5545 array[dest++] = array[cursor2++]; 5546 5547 if (--length2 === 0) { 5548 for (i = 0; i < length1; i++) { 5549 array[dest + i] = tmp[cursor1 + i]; 5550 } 5551 return; 5552 } 5553 5554 if (length1 === 1) { 5555 for (i = 0; i < length2; i++) { 5556 array[dest + i] = array[cursor2 + i]; 5557 } 5558 array[dest + length2] = tmp[cursor1]; 5559 return; 5560 } 5561 5562 var _minGallop = minGallop; 5563 var count1, count2, exit; 5564 5565 while (1) { 5566 count1 = 0; 5567 count2 = 0; 5568 exit = false; 5569 5570 do { 5571 if (compare(array[cursor2], tmp[cursor1]) < 0) { 5572 array[dest++] = array[cursor2++]; 5573 count2++; 5574 count1 = 0; 5575 5576 if (--length2 === 0) { 5577 exit = true; 5578 break; 5579 } 5580 } 5581 else { 5582 array[dest++] = tmp[cursor1++]; 5583 count1++; 5584 count2 = 0; 5585 if (--length1 === 1) { 5586 exit = true; 5587 break; 5588 } 5589 } 5590 } while ((count1 | count2) < _minGallop); 5591 5592 if (exit) { 5593 break; 5594 } 5595 5596 do { 5597 count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare); 5598 5599 if (count1 !== 0) { 5600 for (i = 0; i < count1; i++) { 5601 array[dest + i] = tmp[cursor1 + i]; 5602 } 5603 5604 dest += count1; 5605 cursor1 += count1; 5606 length1 -= count1; 5607 if (length1 <= 1) { 5608 exit = true; 5609 break; 5610 } 5611 } 5612 5613 array[dest++] = array[cursor2++]; 5614 5615 if (--length2 === 0) { 5616 exit = true; 5617 break; 5618 } 5619 5620 count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare); 5621 5622 if (count2 !== 0) { 5623 for (i = 0; i < count2; i++) { 5624 array[dest + i] = array[cursor2 + i]; 5625 } 5626 5627 dest += count2; 5628 cursor2 += count2; 5629 length2 -= count2; 5630 5631 if (length2 === 0) { 5632 exit = true; 5633 break; 5634 } 5635 } 5636 array[dest++] = tmp[cursor1++]; 5637 5638 if (--length1 === 1) { 5639 exit = true; 5640 break; 5641 } 5642 5643 _minGallop--; 5644 } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); 5645 5646 if (exit) { 5647 break; 5648 } 5649 5650 if (_minGallop < 0) { 5651 _minGallop = 0; 5652 } 5653 5654 _minGallop += 2; 5655 } 5656 5657 minGallop = _minGallop; 5658 5659 minGallop < 1 && (minGallop = 1); 5660 5661 if (length1 === 1) { 5662 for (i = 0; i < length2; i++) { 5663 array[dest + i] = array[cursor2 + i]; 5664 } 5665 array[dest + length2] = tmp[cursor1]; 5666 } 5667 else if (length1 === 0) { 5668 throw new Error(); 5669 // throw new Error('mergeLow preconditions were not respected'); 5670 } 5671 else { 5672 for (i = 0; i < length1; i++) { 5673 array[dest + i] = tmp[cursor1 + i]; 5674 } 5675 } 5676 } 5677 5678 function mergeHigh (start1, length1, start2, length2) { 5679 var i = 0; 5680 5681 for (i = 0; i < length2; i++) { 5682 tmp[i] = array[start2 + i]; 5683 } 5684 5685 var cursor1 = start1 + length1 - 1; 5686 var cursor2 = length2 - 1; 5687 var dest = start2 + length2 - 1; 5688 var customCursor = 0; 5689 var customDest = 0; 5690 5691 array[dest--] = array[cursor1--]; 5692 5693 if (--length1 === 0) { 5694 customCursor = dest - (length2 - 1); 5695 5696 for (i = 0; i < length2; i++) { 5697 array[customCursor + i] = tmp[i]; 5698 } 5699 5700 return; 5701 } 5702 5703 if (length2 === 1) { 5704 dest -= length1; 5705 cursor1 -= length1; 5706 customDest = dest + 1; 5707 customCursor = cursor1 + 1; 5708 5709 for (i = length1 - 1; i >= 0; i--) { 5710 array[customDest + i] = array[customCursor + i]; 5711 } 5712 5713 array[dest] = tmp[cursor2]; 5714 return; 5715 } 5716 5717 var _minGallop = minGallop; 5718 5719 while (true) { 5720 var count1 = 0; 5721 var count2 = 0; 5722 var exit = false; 5723 5724 do { 5725 if (compare(tmp[cursor2], array[cursor1]) < 0) { 5726 array[dest--] = array[cursor1--]; 5727 count1++; 5728 count2 = 0; 5729 if (--length1 === 0) { 5730 exit = true; 5731 break; 5732 } 5733 } 5734 else { 5735 array[dest--] = tmp[cursor2--]; 5736 count2++; 5737 count1 = 0; 5738 if (--length2 === 1) { 5739 exit = true; 5740 break; 5741 } 5742 } 5743 } while ((count1 | count2) < _minGallop); 5744 5745 if (exit) { 5746 break; 5747 } 5748 5749 do { 5750 count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare); 5751 5752 if (count1 !== 0) { 5753 dest -= count1; 5754 cursor1 -= count1; 5755 length1 -= count1; 5756 customDest = dest + 1; 5757 customCursor = cursor1 + 1; 5758 5759 for (i = count1 - 1; i >= 0; i--) { 5760 array[customDest + i] = array[customCursor + i]; 5761 } 5762 5763 if (length1 === 0) { 5764 exit = true; 5765 break; 5766 } 5767 } 5768 5769 array[dest--] = tmp[cursor2--]; 5770 5771 if (--length2 === 1) { 5772 exit = true; 5773 break; 5774 } 5775 5776 count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare); 5777 5778 if (count2 !== 0) { 5779 dest -= count2; 5780 cursor2 -= count2; 5781 length2 -= count2; 5782 customDest = dest + 1; 5783 customCursor = cursor2 + 1; 5784 5785 for (i = 0; i < count2; i++) { 5786 array[customDest + i] = tmp[customCursor + i]; 5787 } 5788 5789 if (length2 <= 1) { 5790 exit = true; 5791 break; 5792 } 5793 } 5794 5795 array[dest--] = array[cursor1--]; 5796 5797 if (--length1 === 0) { 5798 exit = true; 5799 break; 5800 } 5801 5802 _minGallop--; 5803 } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); 5804 5805 if (exit) { 5806 break; 5807 } 5808 5809 if (_minGallop < 0) { 5810 _minGallop = 0; 5811 } 5812 5813 _minGallop += 2; 5814 } 5815 5816 minGallop = _minGallop; 5817 5818 if (minGallop < 1) { 5819 minGallop = 1; 5820 } 5821 5822 if (length2 === 1) { 5823 dest -= length1; 5824 cursor1 -= length1; 5825 customDest = dest + 1;
5826 customCursor = cursor1 + 1; 5827 5828 for (i = length1 - 1; i >= 0; i--) { 5829 array[customDest + i] = array[customCursor + i]; 5830 } 5831 5832 array[dest] = tmp[cursor2]; 5833 } 5834 else if (length2 === 0) { 5835 throw new Error(); 5836 // throw new Error('mergeHigh preconditions were not respected'); 5837 } 5838 else { 5839 customCursor = dest - (length2 - 1); 5840 for (i = 0; i < length2; i++) { 5841 array[customCursor + i] = tmp[i]; 5842 } 5843 } 5844 } 5845 5846 this.mergeRuns = mergeRuns; 5847 this.forceMergeRuns = forceMergeRuns; 5848 this.pushRun = pushRun; 5849 } 5850 5851 function sort(array, compare, lo, hi) { 5852 if (!lo) { 5853 lo = 0; 5854 } 5855 if (!hi) { 5856 hi = array.length; 5857 } 5858 5859 var remaining = hi - lo; 5860 5861 if (remaining < 2) { 5862 return; 5863 } 5864 5865 var runLength = 0; 5866 5867 if (remaining < DEFAULT_MIN_MERGE) { 5868 runLength = makeAscendingRun(array, lo, hi, compare); 5869 binaryInsertionSort(array, lo, hi, lo + runLength, compare); 5870 return; 5871 } 5872 5873 var ts = new TimSort(array, compare); 5874 5875 var minRun = minRunLength(remaining); 5876 5877 do { 5878 runLength = makeAscendingRun(array, lo, hi, compare); 5879 if (runLength < minRun) { 5880 var force = remaining; 5881 if (force > minRun) { 5882 force = minRun; 5883 } 5884 5885 binaryInsertionSort(array, lo, lo + force, lo + runLength, compare); 5886 runLength = force; 5887 } 5888 5889 ts.pushRun(lo, runLength); 5890 ts.mergeRuns(); 5891 5892 remaining -= runLength; 5893 lo += runLength; 5894 } while (remaining !== 0); 5895 5896 ts.forceMergeRuns(); 5897 } 5898 5899// Use timsort because in most case elements are partially sorted 5900// https://jsfiddle.net/pissang/jr4x7mdm/8/ 5901 function shapeCompareFunc(a, b) { 5902 if (a.zlevel === b.zlevel) { 5903 if (a.z === b.z) { 5904 // if (a.z2 === b.z2) { 5905 // // FIXME Slow has renderidx compare 5906 // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement 5907 // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012 5908 // return a.__renderidx - b.__renderidx; 5909 // } 5910 return a.z2 - b.z2; 5911 } 5912 return a.z - b.z; 5913 } 5914 return a.zlevel - b.zlevel; 5915 } 5916 /** 5917 * éåîæµ æ³ç°± (M) 5918 * @alias module:zrender/Storage 5919 * @constructor 5920 */ 5921 var Storage = function () { // jshint ignore:line 5922 this._roots = []; 5923 5924 this._displayList = []; 5925 5926 this._displayListLen = 0; 5927 }; 5928 5929 Storage.prototype = { 5930 5931 constructor: Storage, 5932 5933 /** 5934 * @param {Function} cb 5935 * 5936 */ 5937 traverse: function (cb, context) { 5938 for (var i = 0; i < this._roots.length; i++) { 5939 this._roots[i].traverse(cb, context); 5940 } 5941 }, 5942 5943 /** 5944 * æ©æ¿æ´éµâ¬é夿µè¤°ã¢æ®ç¼æ¨ºåéç·åª 5945 * @param {boolean} [update=false] éîæé¦ã¨ç¹é¥ç²å¢ éåæçã¦æç¼ï¿½ 5946 * @param {boolean} [includeIgnore=false] éîæéåæ ignore é¨å¬æç¼ï¿½, é¦ï¿½ update æ¶ï¿½ true é¨å¬æ¤éæ¬æ¹é� 5947 * 5948 * çï¹î{@link module:zrender/graphic/Displayable.prototype.updateDisplayList} 5949 * @return {Array.<module:zrender/graphic/Displayable>} 5950 */ 5951 getDisplayList: function (update, includeIgnore) { 5952 includeIgnore = includeIgnore || false; 5953 if (update) { 5954 this.updateDisplayList(includeIgnore); 5955 } 5956 return this._displayList; 5957 }, 5958 5959 /** 5960 * éåæé¥æ§è°é¨å粯éå æ§¦é椼â¬ï¿½
5961 * å§£å¿î¼ç¼æ¨ºåéå¶å 浼氳çé¢îç´çã¦æå¨æç´°éå ç¹æ´ï¸¿ç´éå ¥äº¶é忣æ¶î ç²é屾洿éçå¢éå¡roupéå²hapeé¨å«å½é¹ã èæ¶æå¦¸éµâ¬éå¤å½²ç佺æ®Shapeæ·æ¿ç¨éçæç¼åªè é� 5962 * éâ¬é庢ç´é¹î¾ç²¯éåæ®æµ¼æ¨ºå绾Ñç´zlevel > z > 黿忤¤åç°éå¤å¸æ´å¿ç·±éæ®ç²¯éå æ§¦é� 5963 * @param {boolean} [includeIgnore=false] éîæéåæ ignore é¨å¬æç¼ï¿½ 5964 */ 5965 updateDisplayList: function (includeIgnore) { 5966 this._displayListLen = 0; 5967 5968 var roots = this._roots; 5969 var displayList = this._displayList; 5970 for (var i = 0, len = roots.length; i < len; i++) { 5971 this._updateAndAddDisplayable(roots[i], null, includeIgnore); 5972 } 5973 5974 displayList.length = this._displayListLen; 5975 5976 env$1.canvasSupported && sort(displayList, shapeCompareFunc); 5977 }, 5978 5979 _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) { 5980 5981 if (el.ignore && !includeIgnore) { 5982 return; 5983 } 5984 5985 el.beforeUpdate(); 5986 5987 if (el.__dirty) { 5988 5989 el.update(); 5990 5991 } 5992 5993 el.afterUpdate(); 5994 5995 var userSetClipPath = el.clipPath; 5996 if (userSetClipPath) { 5997 5998 // FIXME éå ¢å·¼è¤°åæ· 5999 if (clipPaths) { 6000 clipPaths = clipPaths.slice(); 6001 } 6002 else { 6003 clipPaths = []; 6004 } 6005 6006 var currentClipPath = userSetClipPath; 6007 var parentClipPath = el; 6008 // Recursively add clip path 6009 while (currentClipPath) { 6010 // clipPath é¨å«å½é¹ã¡æ§¸é©è½°ç°¬æµ£è·¨æ¤æ©æ¬é clipPath é¨å«å绱� 6011 currentClipPath.parent = parentClipPath; 6012 currentClipPath.updateTransform(); 6013 6014 clipPaths.push(currentClipPath); 6015 6016 parentClipPath = currentClipPath; 6017 currentClipPath = currentClipPath.clipPath; 6018 } 6019 } 6020 6021 if (el.isGroup) { 6022 var children = el._children; 6023 6024 for (var i = 0; i < children.length; i++) { 6025 var child = children[i]; 6026 6027 // Force to mark as dirty if group is dirty 6028 // FIXME __dirtyPath ? 6029 if (el.__dirty) { 6030 child.__dirty = true; 6031 } 6032 6033 this._updateAndAddDisplayable(child, clipPaths, includeIgnore); 6034 } 6035 6036 // Mark group clean here 6037 el.__dirty = false; 6038 6039 } 6040 else { 6041 el.__clipPaths = clipPaths; 6042 6043 this._displayList[this._displayListLen++] = el; 6044 } 6045 }, 6046 6047 /** 6048 * 娣诲å§é¥æ§è°(Shape)é´æ ¬â¬å¯ç²(Group)éçç´éºåå£ 6049 * @param {module:zrender/Element} el 6050 */ 6051 addRoot: function (el) { 6052 if (el.__storage === this) { 6053 return; 6054 } 6055 6056 if (el instanceof Group) { 6057 el.addChildrenToStorage(this); 6058 } 6059 6060 this.addToStorage(el); 6061 this._roots.push(el); 6062 }, 6063 6064 /** 6065 * éç»æ«é¸å§ç¾é¨å«æµè¤°ï¿½(Shape)é´æ ¬â¬å¯ç²(Group) 6066 * @param {string|Array.<string>} [el] æ¿¡åçæ¶è¹âå¨å¯âéç¿ éStorage 6067 */ 6068 delRoot: function (el) { 6069 if (el == null) { 6070 // æ¶å¶å¯ç¹ælå¨å¯â 6071 for (var i = 0; i < this._roots.length; i++) { 6072 var root = this._roots[i]; 6073 if (root instanceof Group) { 6074 root.delChildrenFromStorage(this); 6075 } 6076 } 6077 6078 this._roots = []; 6079 this._displayList = []; 6080 this._displayListLen = 0; 6081 6082 return; 6083 } 6084 6085 if (el instanceof Array) { 6086 for (var i = 0, l = el.length; i < l; i++) { 6087 this.delRoot(el[i]); 6088 } 6089 return; 6090 } 6091 6092 6093 var idx = indexOf(this._roots, el); 6094 if (idx >= 0) { 6095 this.delFromStorage(el); 6096 this._roots.splice(idx, 1); 6097 if (el instanceof Group) { 6098 el.delChildrenFromStorage(this); 6099 } 6100 } 6101 }, 6102 6103 addToStorage: function (el) { 6104 if (el) { 6105 el.__storage = this; 6106 el.dirty(false); 6107 } 6108 return this; 6109 }, 6110 6111 delFromStorage: function (el) { 6112 if (el) { 6113 el.__storage = null; 6114 } 6115 6116 return this; 6117 }, 6118 6119 /** 6120 * å¨å¯âéªæµç¬é²å©æStorage 6121 */ 6122 dispose: function () { 6123 this._renderList = 6124 this._roots = null; 6125 }, 6126 6127 displayableSortFunc: shapeCompareFunc 6128 }; 6129 6130 var SHADOW_PROPS = { 6131 'shadowBlur': 1, 6132 'shadowOffsetX': 1, 6133 'shadowOffsetY': 1, 6134 'textShadowBlur': 1, 6135 'textShadowOffsetX': 1, 6136 'textShadowOffsetY': 1, 6137 'textBoxShadowBlur': 1, 6138 'textBoxShadowOffsetX': 1, 6139 'textBoxShadowOffsetY': 1 6140 }; 6141 6142 var fixShadow = function (ctx, propName, value) { 6143 if (SHADOW_PROPS.hasOwnProperty(propName)) { 6144 return value *= ctx.dpr; 6145 } 6146 return value; 6147 }; 6148 6149 var STYLE_COMMON_PROPS = [ 6150 ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'], 6151 ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10] 6152 ]; 6153 6154// var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
6155// var LINE_PROPS = STYLE_COMMON_PROPS.slice(4); 6156 6157 var Style = function (opts, host) { 6158 this.extendFrom(opts, false); 6159 this.host = host; 6160 }; 6161 6162 function createLinearGradient(ctx, obj, rect) { 6163 var x = obj.x == null ? 0 : obj.x; 6164 var x2 = obj.x2 == null ? 1 : obj.x2; 6165 var y = obj.y == null ? 0 : obj.y; 6166 var y2 = obj.y2 == null ? 0 : obj.y2; 6167 6168 if (!obj.global) { 6169 x = x * rect.width + rect.x; 6170 x2 = x2 * rect.width + rect.x; 6171 y = y * rect.height + rect.y; 6172 y2 = y2 * rect.height + rect.y; 6173 } 6174 6175 // Fix NaN when rect is Infinity 6176 x = isNaN(x) ? 0 : x; 6177 x2 = isNaN(x2) ? 1 : x2; 6178 y = isNaN(y) ? 0 : y; 6179 y2 = isNaN(y2) ? 0 : y2; 6180 6181 var canvasGradient = ctx.createLinearGradient(x, y, x2, y2); 6182 6183 return canvasGradient; 6184 } 6185 6186 function createRadialGradient(ctx, obj, rect) { 6187 var width = rect.width; 6188 var height = rect.height; 6189 var min = Math.min(width, height); 6190 6191 var x = obj.x == null ? 0.5 : obj.x; 6192 var y = obj.y == null ? 0.5 : obj.y; 6193 var r = obj.r == null ? 0.5 : obj.r; 6194 if (!obj.global) { 6195 x = x * width + rect.x; 6196 y = y * height + rect.y; 6197 r = r * min; 6198 } 6199 6200 var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r); 6201 6202 return canvasGradient; 6203 } 6204 6205 6206 Style.prototype = { 6207 6208 constructor: Style, 6209 6210 /** 6211 * @type {module:zrender/graphic/Displayable} 6212 */ 6213 host: null, 6214 6215 /** 6216 * @type {string} 6217 */ 6218 fill: '#000', 6219 6220 /** 6221 * @type {string} 6222 */ 6223 stroke: null, 6224 6225 /** 6226 * @type {number} 6227 */ 6228 opacity: 1, 6229 6230 /** 6231 * @type {Array.<number>} 6232 */ 6233 lineDash: null, 6234 6235 /** 6236 * @type {number} 6237 */ 6238 lineDashOffset: 0, 6239 6240 /** 6241 * @type {number} 6242 */ 6243 shadowBlur: 0, 6244 6245 /** 6246 * @type {number} 6247 */ 6248 shadowOffsetX: 0, 6249 6250 /** 6251 * @type {number} 6252 */ 6253 shadowOffsetY: 0, 6254 6255 /** 6256 * @type {number} 6257 */ 6258 lineWidth: 1, 6259 6260 /** 6261 * If stroke ignore scale 6262 * @type {Boolean} 6263 */ 6264 strokeNoScale: false, 6265 6266 // Bounding rect text configuration 6267 // Not affected by element transform 6268 /** 6269 * @type {string} 6270 */ 6271 text: null, 6272 6273 /** 6274 * If `fontSize` or `fontFamily` exists, `font` will be reset by 6275 * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`. 6276 * So do not visit it directly in upper application (like echarts), 6277 * but use `contain/text#makeFont` instead. 6278 * @type {string} 6279 */ 6280 font: null, 6281 6282 /** 6283 * The same as font. Use font please. 6284 * @deprecated 6285 * @type {string} 6286 */ 6287 textFont: null, 6288 6289 /** 6290 * It helps merging respectively, rather than parsing an entire font string. 6291 * @type {string} 6292 */ 6293 fontStyle: null, 6294 6295 /** 6296 * It helps merging respectively, rather than parsing an entire font string. 6297 * @type {string} 6298 */ 6299 fontWeight: null, 6300 6301 /** 6302 * It helps merging respectively, rather than parsing an entire font string. 6303 * Should be 12 but not '12px'. 6304 * @type {number} 6305 */ 6306 fontSize: null, 6307 6308 /** 6309 * It helps merging respectively, rather than parsing an entire font string. 6310 * @type {string} 6311 */ 6312 fontFamily: null, 6313 6314 /** 6315 * Reserved for special functinality, like 'hr'. 6316 * @type {string} 6317 */ 6318 textTag: null, 6319 6320 /** 6321 * @type {string} 6322 */ 6323 textFill: '#000', 6324 6325 /** 6326 * @type {string} 6327 */ 6328 textStroke: null, 6329 6330 /** 6331 * @type {number} 6332 */ 6333 textWidth: null, 6334 6335 /**
6336 * Only for textBackground. 6337 * @type {number} 6338 */ 6339 textHeight: null, 6340 6341 /** 6342 * textStroke may be set as some color as a default 6343 * value in upper applicaion, where the default value 6344 * of textStrokeWidth should be 0 to make sure that 6345 * user can choose to do not use text stroke. 6346 * @type {number} 6347 */ 6348 textStrokeWidth: 0, 6349 6350 /** 6351 * @type {number} 6352 */ 6353 textLineHeight: null, 6354 6355 /** 6356 * 'inside', 'left', 'right', 'top', 'bottom' 6357 * [x, y] 6358 * Based on x, y of rect. 6359 * @type {string|Array.<number>} 6360 * @default 'inside' 6361 */ 6362 textPosition: 'inside', 6363 6364 /** 6365 * If not specified, use the boundingRect of a `displayable`. 6366 * @type {Object} 6367 */ 6368 textRect: null, 6369 6370 /** 6371 * [x, y] 6372 * @type {Array.<number>} 6373 */ 6374 textOffset: null, 6375 6376 /** 6377 * @type {string} 6378 */ 6379 textAlign: null, 6380 6381 /** 6382 * @type {string} 6383 */ 6384 textVerticalAlign: null, 6385 6386 /** 6387 * @type {number} 6388 */ 6389 textDistance: 5, 6390 6391 /** 6392 * @type {string} 6393 */ 6394 textShadowColor: 'transparent', 6395 6396 /** 6397 * @type {number} 6398 */ 6399 textShadowBlur: 0, 6400 6401 /** 6402 * @type {number} 6403 */ 6404 textShadowOffsetX: 0, 6405 6406 /** 6407 * @type {number} 6408 */ 6409 textShadowOffsetY: 0, 6410 6411 /** 6412 * @type {string} 6413 */ 6414 textBoxShadowColor: 'transparent', 6415 6416 /** 6417 * @type {number} 6418 */ 6419 textBoxShadowBlur: 0, 6420 6421 /** 6422 * @type {number} 6423 */ 6424 textBoxShadowOffsetX: 0, 6425 6426 /** 6427 * @type {number} 6428 */ 6429 textBoxShadowOffsetY: 0, 6430 6431 /** 6432 * Whether transform text. 6433 * Only useful in Path and Image element 6434 * @type {boolean} 6435 */ 6436 transformText: false, 6437 6438 /** 6439 * Text rotate around position of Path or Image 6440 * Only useful in Path and Image element and transformText is false. 6441 */ 6442 textRotation: 0, 6443 6444 /** 6445 * Text origin of text rotation, like [10, 40]. 6446 * Based on x, y of rect. 6447 * Useful in label rotation of circular symbol. 6448 * By default, this origin is textPosition. 6449 * Can be 'center'. 6450 * @type {string|Array.<number>} 6451 */ 6452 textOrigin: null, 6453 6454 /** 6455 * @type {string} 6456 */ 6457 textBackgroundColor: null, 6458 6459 /** 6460 * @type {string} 6461 */ 6462 textBorderColor: null, 6463 6464 /** 6465 * @type {number} 6466 */ 6467 textBorderWidth: 0, 6468 6469 /** 6470 * @type {number} 6471 */ 6472 textBorderRadius: 0, 6473 6474 /** 6475 * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]` 6476 * @type {number|Array.<number>} 6477 */ 6478 textPadding: null, 6479 6480 /** 6481 * Text styles for rich text. 6482 * @type {Object} 6483 */ 6484 rich: null, 6485 6486 /** 6487 * {outerWidth, outerHeight, ellipsis, placeholder} 6488 * @type {Object} 6489 */ 6490 truncate: null, 6491 6492 /** 6493 * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation 6494 * @type {string} 6495 */ 6496 blend: null, 6497 6498 /** 6499 * @param {CanvasRenderingContext2D} ctx 6500 */ 6501 bind: function (ctx, el, prevEl) { 6502 var style = this; 6503 var prevStyle = prevEl && prevEl.style; 6504 var firstDraw = !prevStyle; 6505 6506 for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { 6507 var prop = STYLE_COMMON_PROPS[i]; 6508 var styleName = prop[0]; 6509 6510 if (firstDraw || style[styleName] !== prevStyle[styleName]) { 6511 // FIXME Invalid property value will cause style leak from previous element. 6512 ctx[styleName] = 6513 fixShadow(ctx, styleName, style[styleName] || prop[1]); 6514 } 6515 } 6516 6517 if ((firstDraw || style.fill !== prevStyle.fill)) { 6518 ctx.fillStyle = style.fill; 6519 } 6520 if ((firstDraw || style.stroke !== prevStyle.stroke)) { 6521 ctx.strokeStyle = style.stroke; 6522 } 6523 if ((firstDraw || style.opacity !== prevStyle.opacity)) { 6524 ctx.globalAlpha = style.opacity == null ? 1 : style.opacity; 6525 } 6526 6527 if ((firstDraw || style.blend !== prevStyle.blend)) { 6528 ctx.globalCompositeOperation = style.blend || 'source-over'; 6529 } 6530 if (this.hasStroke()) { 6531 var lineWidth = style.lineWidth; 6532 ctx.lineWidth = lineWidth / ( 6533 (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1 6534 ); 6535 } 6536 }, 6537 6538 hasFill: function () { 6539 var fill = this.fill; 6540 return fill != null && fill !== 'none'; 6541 }, 6542 6543 hasStroke: function () { 6544 var stroke = this.stroke; 6545 return stroke != null && stroke !== 'none' && this.lineWidth > 0; 6546 }, 6547 6548 /** 6549 * Extend from other style 6550 * @param {zrender/graphic/Style} otherStyle 6551 * @param {boolean} overwrite true: overwrirte any way. 6552 * false: overwrite only when !target.hasOwnProperty 6553 * others: overwrite when property is not null/undefined. 6554 */ 6555 extendFrom: function (otherStyle, overwrite) { 6556 if (otherStyle) { 6557 for (var name in otherStyle) { 6558 if (otherStyle.hasOwnProperty(name) 6559 && (overwrite === true 6560 || ( 6561 overwrite === false 6562 ? !this.hasOwnProperty(name) 6563 : otherStyle[name] != null 6564 ) 6565 ) 6566 ) { 6567 this[name] = otherStyle[name]; 6568 } 6569 } 6570 } 6571 }, 6572 6573 /** 6574 * Batch setting style with a given object 6575 * @param {Object|string} obj 6576 * @param {*} [obj] 6577 */ 6578 set: function (obj, value) { 6579 if (typeof obj === 'string') { 6580 this[obj] = value; 6581 } 6582 else { 6583 this.extendFrom(obj, true); 6584 } 6585 }, 6586 6587 /** 6588 * Clone 6589 * @return {zrender/graphic/Style} [description] 6590 */ 6591 clone: function () { 6592 var newStyle = new this.constructor(); 6593 newStyle.extendFrom(this, true); 6594 return newStyle; 6595 }, 6596 6597 getGradient: function (ctx, obj, rect) { 6598 var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient; 6599 var canvasGradient = method(ctx, obj, rect); 6600 var colorStops = obj.colorStops; 6601 for (var i = 0; i < colorStops.length; i++) { 6602 canvasGradient.addColorStop( 6603 colorStops[i].offset, colorStops[i].color 6604 ); 6605 } 6606 return canvasGradient; 6607 } 6608 6609 }; 6610 6611 var styleProto = Style.prototype; 6612 for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { 6613 var prop = STYLE_COMMON_PROPS[i]; 6614 if (!(prop[0] in styleProto)) { 6615 styleProto[prop[0]] = prop[1]; 6616 } 6617 } 6618 6619// Provide for others 6620 Style.getGradient = styleProto.getGradient; 6621 6622 var Pattern = function (image, repeat) {
6623 // Should do nothing more in this constructor. Because gradient can be 6624 // declard by `color: {image: ...}`, where this constructor will not be called. 6625 6626 this.image = image; 6627 this.repeat = repeat; 6628 6629 // Can be cloned 6630 this.type = 'pattern'; 6631 }; 6632 6633 Pattern.prototype.getCanvasPattern = function (ctx) { 6634 return ctx.createPattern(this.image, this.repeat || 'repeat'); 6635 }; 6636 6637 /** 6638 * @module zrender/Layer 6639 * @author pissang(https://www.github.com/pissang) 6640 */ 6641 6642 function returnFalse() { 6643 return false; 6644 } 6645 6646 /** 6647 * éæ¶ç¼dom 6648 * 6649 * @inner 6650 * @param {string} id dom id 坰坿¤ 6651 * @param {Painter} painter painter instance 6652 * @param {number} number 6653 */ 6654 function createDom(id, painter, dpr) { 6655 var newDom = createCanvas(); 6656 var width = painter.getWidth(); 6657 var height = painter.getHeight(); 6658 6659 var newDomStyle = newDom.style; 6660 if (newDomStyle) { // In node or some other non-browser environment 6661 newDomStyle.position = 'absolute'; 6662 newDomStyle.left = 0; 6663 newDomStyle.top = 0; 6664 newDomStyle.width = width + 'px'; 6665 newDomStyle.height = height + 'px'; 6666 6667 newDom.setAttribute('data-zr-dom-id', id); 6668 } 6669 6670 newDom.width = width * dpr; 6671 newDom.height = height * dpr; 6672 6673 return newDom; 6674 } 6675 6676 /** 6677 * @alias module:zrender/Layer 6678 * @constructor 6679 * @extends module:zrender/mixin/Transformable 6680 * @param {string} id 6681 * @param {module:zrender/Painter} painter 6682 * @param {number} [dpr] 6683 */ 6684 var Layer = function(id, painter, dpr) { 6685 var dom; 6686 dpr = dpr || devicePixelRatio; 6687 if (typeof id === 'string') { 6688 dom = createDom(id, painter, dpr); 6689 } 6690 // Not using isDom because in node it will return false 6691 else if (isObject$1(id)) { 6692 dom = id; 6693 id = dom.id; 6694 } 6695 this.id = id; 6696 this.dom = dom; 6697 6698 var domStyle = dom.style; 6699 if (domStyle) { // Not in node 6700 dom.onselectstart = returnFalse; // é¬å®å¤æ¤¤ç¸æ½°é«å¤è é¨å«æç� 6701 domStyle['-webkit-user-select'] = 'none'; 6702 domStyle['user-select'] = 'none'; 6703 domStyle['-webkit-touch-callout'] = 'none'; 6704 domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)'; 6705 domStyle['padding'] = 0; 6706 domStyle['margin'] = 0; 6707 domStyle['border-width'] = 0; 6708 } 6709 6710 this.domBack = null; 6711 this.ctxBack = null; 6712 6713 this.painter = painter; 6714 6715 this.config = null; 6716 6717 // Configs 6718 /** 6719 * å§£å¿î¼å¨å¯âé¢è¯²ç«·é¨å¯îé¹ï¿½ 6720 * @type {string} 6721 * @default 0 6722 */ 6723 this.clearColor = 0; 6724 /** 6725 * éîæå¯®â¬éîå§©é¬ä½¹Ä绯� 6726 * @type {boolean} 6727 * @default false 6728 */ 6729 this.motionBlur = false; 6730 /** 6731 * é¦ã¥ç´éîå§©é¬ä½¹Äç»¯å©æ®éè·ºâ¬æ¬å¨é¢îç´æ¶åºç¬æ¶â¬ç¯Ñè´©éå ¢æ®alphaéç¡·ç´éè°ç§ºæ¾¶Ñç¬æ©ç¡ 秺éåº¢æ¨ 6732 * @type {number} 6733 * @default 0.7 6734 */ 6735 this.lastFrameAlpha = 0.7; 6736 6737 /** 6738 * Layer dpr 6739 * @type {number} 6740 */ 6741 this.dpr = dpr; 6742 }; 6743 6744 Layer.prototype = { 6745 6746 constructor: Layer, 6747 6748 __dirty: true, 6749 6750 __used: false, 6751 6752 __drawIndex: 0, 6753 __startIndex: 0, 6754 __endIndex: 0, 6755 6756 incremental: false, 6757 6758 getElementCount: function () { 6759 return this.__endIndex - this.__startIndex; 6760 }, 6761 6762 initContext: function () { 6763 this.ctx = this.dom.getContext('2d'); 6764 this.ctx.dpr = this.dpr; 6765 }, 6766 6767 createBackBuffer: function () { 6768 var dpr = this.dpr; 6769 6770 this.domBack = createDom('back-' + this.id, this.painter, dpr); 6771 this.ctxBack = this.domBack.getContext('2d'); 6772 6773 if (dpr != 1) { 6774 this.ctxBack.scale(dpr, dpr); 6775 } 6776 }, 6777 6778 /** 6779 * @param {number} width 6780 * @param {number} height 6781 */ 6782 resize: function (width, height) { 6783 var dpr = this.dpr; 6784 6785 var dom = this.dom; 6786 var domStyle = dom.style; 6787 var domBack = this.domBack; 6788 6789 if (domStyle) { 6790 domStyle.width = width + 'px'; 6791 domStyle.height = height + 'px'; 6792 } 6793 6794 dom.width = width * dpr; 6795 dom.height = height * dpr; 6796 6797 if (domBack) { 6798 domBack.width = width * dpr; 6799 domBack.height = height * dpr; 6800 6801 if (dpr != 1) { 6802 this.ctxBack.scale(dpr, dpr); 6803 } 6804 } 6805 }, 6806 6807 /** 6808 * å¨å¯âçã¥ç°é¢è¯²ç«· 6809 * @param {boolean} [clearAll]=false Clear all with out motion blur 6810 * @param {Color} [clearColor] 6811 */ 6812 clear: function (clearAll, clearColor) { 6813 var dom = this.dom; 6814 var ctx = this.ctx; 6815 var width = dom.width; 6816 var height = dom.height; 6817 6818 var clearColor = clearColor || this.clearColor; 6819 var haveMotionBLur = this.motionBlur && !clearAll; 6820 var lastFrameAlpha = this.lastFrameAlpha; 6821 6822 var dpr = this.dpr; 6823 6824 if (haveMotionBLur) { 6825 if (!this.domBack) { 6826 this.createBackBuffer(); 6827 } 6828 6829 this.ctxBack.globalCompositeOperation = 'copy'; 6830 this.ctxBack.drawImage( 6831 dom, 0, 0, 6832 width / dpr, 6833 height / dpr 6834 ); 6835 } 6836 6837 ctx.clearRect(0, 0, width, height); 6838 if (clearColor && clearColor !== 'transparent') { 6839 var clearColorGradientOrPattern; 6840 // Gradient 6841 if (clearColor.colorStops) { 6842 // Cache canvas gradient 6843 clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, { 6844 x: 0, 6845 y: 0, 6846 width: width, 6847 height: height 6848 }); 6849 6850 clearColor.__canvasGradient = clearColorGradientOrPattern; 6851 } 6852 // Pattern 6853 else if (clearColor.image) { 6854 clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx); 6855 } 6856 ctx.save(); 6857 ctx.fillStyle = clearColorGradientOrPattern || clearColor; 6858 ctx.fillRect(0, 0, width, height);
6859 ctx.restore(); 6860 } 6861 6862 if (haveMotionBLur) { 6863 var domBack = this.domBack; 6864 ctx.save(); 6865 ctx.globalAlpha = lastFrameAlpha; 6866 ctx.drawImage(domBack, 0, 0, width, height); 6867 ctx.restore(); 6868 } 6869 } 6870 }; 6871 6872 var requestAnimationFrame = ( 6873 typeof window !== 'undefined' 6874 && ( 6875 (window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) 6876 // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809 6877 || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window)) 6878 || window.mozRequestAnimationFrame 6879 || window.webkitRequestAnimationFrame 6880 ) 6881 ) || function (func) { 6882 setTimeout(func, 16); 6883 }; 6884 6885 var globalImageCache = new LRU(50); 6886 6887 /** 6888 * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc 6889 * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image 6890 */ 6891 function findExistImage(newImageOrSrc) { 6892 if (typeof newImageOrSrc === 'string') { 6893 var cachedImgObj = globalImageCache.get(newImageOrSrc); 6894 return cachedImgObj && cachedImgObj.image; 6895 } 6896 else { 6897 return newImageOrSrc; 6898 } 6899 } 6900 6901 /** 6902 * Caution: User should cache loaded images, but not just count on LRU. 6903 * Consider if required images more than LRU size, will dead loop occur? 6904 * 6905 * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc 6906 * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image. 6907 * @param {module:zrender/Element} [hostEl] For calling `dirty`. 6908 * @param {Function} [cb] params: (image, cbPayload) 6909 * @param {Object} [cbPayload] Payload on cb calling. 6910 * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image 6911 */ 6912 function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) { 6913 if (!newImageOrSrc) { 6914 return image; 6915 } 6916 else if (typeof newImageOrSrc === 'string') { 6917 6918 // Image should not be loaded repeatly. 6919 if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) { 6920 return image; 6921 } 6922 6923 // Only when there is no existent image or existent image src 6924 // is different, this method is responsible for load. 6925 var cachedImgObj = globalImageCache.get(newImageOrSrc); 6926 6927 var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload}; 6928 6929 if (cachedImgObj) { 6930 image = cachedImgObj.image; 6931 !isImageReady(image) && cachedImgObj.pending.push(pendingWrap); 6932 } 6933 else { 6934 !image && (image = new Image()); 6935 image.onload = imageOnLoad; 6936 6937 globalImageCache.put( 6938 newImageOrSrc, 6939 image.__cachedImgObj = { 6940 image: image, 6941 pending: [pendingWrap] 6942 } 6943 ); 6944 6945 image.src = image.__zrImageSrc = newImageOrSrc; 6946 } 6947 6948 return image; 6949 } 6950 // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas 6951 else { 6952 return newImageOrSrc; 6953 } 6954 } 6955 6956 function imageOnLoad() { 6957 var cachedImgObj = this.__cachedImgObj; 6958 this.onload = this.__cachedImgObj = null; 6959 6960 for (var i = 0; i < cachedImgObj.pending.length; i++) { 6961 var pendingWrap = cachedImgObj.pending[i]; 6962 var cb = pendingWrap.cb; 6963 cb && cb(this, pendingWrap.cbPayload); 6964 pendingWrap.hostEl.dirty(); 6965 } 6966 cachedImgObj.pending.length = 0; 6967 } 6968 6969 function isImageReady(image) { 6970 return image && image.width && image.height; 6971 } 6972 6973 var textWidthCache = {}; 6974 var textWidthCacheCounter = 0; 6975 6976 var TEXT_CACHE_MAX = 5000; 6977 var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g; 6978 6979 var DEFAULT_FONT = '12px sans-serif'; 6980 6981// Avoid assign to an exported variable, for transforming to cjs. 6982 var methods$1 = {}; 6983 6984 function $override$1(name, fn) { 6985 methods$1[name] = fn; 6986 } 6987 6988 /** 6989 * @public 6990 * @param {string} text 6991 * @param {string} font 6992 * @return {number} width 6993 */ 6994 function getWidth(text, font) { 6995 font = font || DEFAULT_FONT;
6996 var key = text + ':' + font; 6997 if (textWidthCache[key]) { 6998 return textWidthCache[key]; 6999 } 7000 7001 var textLines = (text + '').split('\n'); 7002 var width = 0; 7003 7004 for (var i = 0, l = textLines.length; i < l; i++) { 7005 // textContain.measureText may be overrided in SVG or VML 7006 width = Math.max(measureText(textLines[i], font).width, width); 7007 } 7008 7009 if (textWidthCacheCounter > TEXT_CACHE_MAX) { 7010 textWidthCacheCounter = 0; 7011 textWidthCache = {}; 7012 } 7013 textWidthCacheCounter++; 7014 textWidthCache[key] = width; 7015 7016 return width; 7017 } 7018 7019 /** 7020 * @public 7021 * @param {string} text 7022 * @param {string} font 7023 * @param {string} [textAlign='left'] 7024 * @param {string} [textVerticalAlign='top'] 7025 * @param {Array.<number>} [textPadding] 7026 * @param {Object} [rich] 7027 * @param {Object} [truncate] 7028 * @return {Object} {x, y, width, height, lineHeight} 7029 */ 7030 function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) { 7031 return rich 7032 ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) 7033 : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate); 7034 } 7035 7036 function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) { 7037 var contentBlock = parsePlainText(text, font, textPadding, truncate); 7038 var outerWidth = getWidth(text, font); 7039 if (textPadding) { 7040 outerWidth += textPadding[1] + textPadding[3]; 7041 } 7042 var outerHeight = contentBlock.outerHeight; 7043 7044 var x = adjustTextX(0, outerWidth, textAlign); 7045 var y = adjustTextY(0, outerHeight, textVerticalAlign); 7046 7047 var rect = new BoundingRect(x, y, outerWidth, outerHeight); 7048 rect.lineHeight = contentBlock.lineHeight; 7049 7050 return rect; 7051 } 7052 7053 function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) { 7054 var contentBlock = parseRichText(text, { 7055 rich: rich, 7056 truncate: truncate, 7057 font: font, 7058 textAlign: textAlign, 7059 textPadding: textPadding 7060 }); 7061 var outerWidth = contentBlock.outerWidth; 7062 var outerHeight = contentBlock.outerHeight; 7063 7064 var x = adjustTextX(0, outerWidth, textAlign); 7065 var y = adjustTextY(0, outerHeight, textVerticalAlign); 7066 7067 return new BoundingRect(x, y, outerWidth, outerHeight); 7068 } 7069 7070 /** 7071 * @public 7072 * @param {number} x 7073 * @param {number} width 7074 * @param {string} [textAlign='left'] 7075 * @return {number} Adjusted x. 7076 */ 7077 function adjustTextX(x, width, textAlign) { 7078 // FIXME Right to left language 7079 if (textAlign === 'right') { 7080 x -= width; 7081 } 7082 else if (textAlign === 'center') { 7083 x -= width / 2; 7084 } 7085 return x; 7086 } 7087 7088 /** 7089 * @public 7090 * @param {number} y 7091 * @param {number} height 7092 * @param {string} [textVerticalAlign='top'] 7093 * @return {number} Adjusted y. 7094 */ 7095 function adjustTextY(y, height, textVerticalAlign) { 7096 if (textVerticalAlign === 'middle') { 7097 y -= height / 2; 7098 } 7099 else if (textVerticalAlign === 'bottom') { 7100 y -= height; 7101 } 7102 return y; 7103 } 7104 7105 /** 7106 * @public 7107 * @param {stirng} textPosition 7108 * @param {Object} rect {x, y, width, height} 7109 * @param {number} distance 7110 * @return {Object} {x, y, textAlign, textVerticalAlign} 7111 */ 7112 function adjustTextPositionOnRect(textPosition, rect, distance) { 7113 7114 var x = rect.x; 7115 var y = rect.y; 7116 7117 var height = rect.height; 7118 var width = rect.width; 7119 var halfHeight = height / 2; 7120 7121 var textAlign = 'left'; 7122 var textVerticalAlign = 'top'; 7123 7124 switch (textPosition) { 7125 case 'left': 7126 x -= distance; 7127 y += halfHeight; 7128 textAlign = 'right'; 7129 textVerticalAlign = 'middle'; 7130 break; 7131 case 'right': 7132 x += distance + width; 7133 y += halfHeight; 7134 textVerticalAlign = 'middle'; 7135 break; 7136 case 'top': 7137 x += width / 2; 7138 y -= distance; 7139 textAlign = 'center'; 7140 textVerticalAlign = 'bottom'; 7141 break; 7142 case 'bottom': 7143 x += width / 2; 7144 y += height + distance; 7145 textAlign = 'center'; 7146 break; 7147 case 'inside': 7148 x += width / 2; 7149 y += halfHeight; 7150 textAlign = 'center'; 7151 textVerticalAlign = 'middle'; 7152 break; 7153 case 'insideLeft': 7154 x += distance; 7155 y += halfHeight; 7156 textVerticalAlign = 'middle'; 7157 break; 7158 case 'insideRight': 7159 x += width - distance; 7160 y += halfHeight; 7161 textAlign = 'right'; 7162 textVerticalAlign = 'middle'; 7163 break; 7164 case 'insideTop': 7165 x += width / 2; 7166 y += distance; 7167 textAlign = 'center'; 7168 break; 7169 case 'insideBottom': 7170 x += width / 2; 7171 y += height - distance; 7172 textAlign = 'center'; 7173 textVerticalAlign = 'bottom'; 7174 break; 7175 case 'insideTopLeft': 7176 x += distance; 7177 y += distance; 7178 break; 7179 case 'insideTopRight': 7180 x += width - distance; 7181 y += distance; 7182 textAlign = 'right'; 7183 break; 7184 case 'insideBottomLeft': 7185 x += distance; 7186 y += height - distance; 7187 textVerticalAlign = 'bottom'; 7188 break; 7189 case 'insideBottomRight': 7190 x += width - distance; 7191 y += height - distance; 7192 textAlign = 'right'; 7193 textVerticalAlign = 'bottom'; 7194 break; 7195 } 7196 7197 return { 7198 x: x, 7199 y: y, 7200 textAlign: textAlign, 7201 textVerticalAlign: textVerticalAlign 7202 }; 7203 } 7204 7205 /** 7206 * Show ellipsis if overflow. 7207 * 7208 * @public 7209 * @param {string} text 7210 * @param {string} containerWidth 7211 * @param {string} font 7212 * @param {number} [ellipsis='...'] 7213 * @param {Object} [options] 7214 * @param {number} [options.maxIterations=3] 7215 * @param {number} [options.minChar=0] If truncate result are less 7216 * then minChar, ellipsis will not show, which is 7217 * better for user hint in some cases. 7218 * @param {number} [options.placeholder=''] When all truncated, use the placeholder. 7219 * @return {string} 7220 */ 7221 function truncateText(text, containerWidth, font, ellipsis, options) { 7222 if (!containerWidth) { 7223 return ''; 7224 } 7225 7226 var textLines = (text + '').split('\n'); 7227 options = prepareTruncateOptions(containerWidth, font, ellipsis, options); 7228 7229 // FIXME 7230 // It is not appropriate that every line has '...' when truncate multiple lines. 7231 for (var i = 0, len = textLines.length; i < len; i++) { 7232 textLines[i] = truncateSingleLine(textLines[i], options); 7233 } 7234 7235 return textLines.join('\n'); 7236 } 7237 7238 function prepareTruncateOptions(containerWidth, font, ellipsis, options) { 7239 options = extend({}, options); 7240 7241 options.font = font; 7242 var ellipsis = retrieve2(ellipsis, '...'); 7243 options.maxIterations = retrieve2(options.maxIterations, 2); 7244 var minChar = options.minChar = retrieve2(options.minChar, 0); 7245 // FIXME 7246 // Other languages? 7247 options.cnCharWidth = getWidth('é¥ï¿½', font); 7248 // FIXME 7249 // Consider proportional font? 7250 var ascCharWidth = options.ascCharWidth = getWidth('a', font); 7251 options.placeholder = retrieve2(options.placeholder, ''); 7252 7253 // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'. 7254 // Example 2: minChar: 3, text: 'ç¼æå®³', truncate result: 'ç¼ï¿½', but not: '...'. 7255 var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap. 7256 for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) { 7257 contentWidth -= ascCharWidth; 7258 } 7259 7260 var ellipsisWidth = getWidth(ellipsis); 7261 if (ellipsisWidth > contentWidth) { 7262 ellipsis = ''; 7263 ellipsisWidth = 0; 7264 } 7265 7266 contentWidth = containerWidth - ellipsisWidth; 7267 7268 options.ellipsis = ellipsis; 7269 options.ellipsisWidth = ellipsisWidth; 7270 options.contentWidth = contentWidth; 7271 options.containerWidth = containerWidth; 7272 7273 return options; 7274 } 7275 7276 function truncateSingleLine(textLine, options) { 7277 var containerWidth = options.containerWidth; 7278 var font = options.font; 7279 var contentWidth = options.contentWidth; 7280 7281 if (!containerWidth) { 7282 return ''; 7283 } 7284 7285 var lineWidth = getWidth(textLine, font); 7286 7287 if (lineWidth <= containerWidth) { 7288 return textLine; 7289 } 7290 7291 for (var j = 0;; j++) { 7292 if (lineWidth <= contentWidth || j >= options.maxIterations) { 7293 textLine += options.ellipsis; 7294 break; 7295 } 7296 7297 var subLength = j === 0 7298 ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth) 7299 : lineWidth > 0 7300 ? Math.floor(textLine.length * contentWidth / lineWidth) 7301 : 0; 7302 7303 textLine = textLine.substr(0, subLength); 7304 lineWidth = getWidth(textLine, font); 7305 } 7306 7307 if (textLine === '') { 7308 textLine = options.placeholder; 7309 } 7310 7311 return textLine; 7312 } 7313 7314 function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) { 7315 var width = 0; 7316 var i = 0; 7317 for (var len = text.length; i < len && width < contentWidth; i++) {
7318 var charCode = text.charCodeAt(i); 7319 width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth; 7320 } 7321 return i; 7322 } 7323 7324 /** 7325 * @public 7326 * @param {string} font 7327 * @return {number} line height 7328 */ 7329 function getLineHeight(font) { 7330 // FIXME A rough approach. 7331 return getWidth('é¥ï¿½', font); 7332 } 7333 7334 /** 7335 * @public 7336 * @param {string} text 7337 * @param {string} font 7338 * @return {Object} width 7339 */ 7340 function measureText(text, font) { 7341 return methods$1.measureText(text, font); 7342 } 7343 7344// Avoid assign to an exported variable, for transforming to cjs. 7345 methods$1.measureText = function (text, font) { 7346 var ctx = getContext(); 7347 ctx.font = font || DEFAULT_FONT; 7348 return ctx.measureText(text); 7349 }; 7350 7351 /** 7352 * @public 7353 * @param {string} text 7354 * @param {string} font 7355 * @param {Object} [truncate] 7356 * @return {Object} block: {lineHeight, lines, height, outerHeight} 7357 * Notice: for performance, do not calculate outerWidth util needed. 7358 */ 7359 function parsePlainText(text, font, padding, truncate) { 7360 text != null && (text += ''); 7361 7362 var lineHeight = getLineHeight(font); 7363 var lines = text ? text.split('\n') : []; 7364 var height = lines.length * lineHeight; 7365 var outerHeight = height; 7366 7367 if (padding) { 7368 outerHeight += padding[0] + padding[2]; 7369 } 7370 7371 if (text && truncate) { 7372 var truncOuterHeight = truncate.outerHeight; 7373 var truncOuterWidth = truncate.outerWidth; 7374 if (truncOuterHeight != null && outerHeight > truncOuterHeight) { 7375 text = ''; 7376 lines = []; 7377 } 7378 else if (truncOuterWidth != null) { 7379 var options = prepareTruncateOptions( 7380 truncOuterWidth - (padding ? padding[1] + padding[3] : 0), 7381 font, 7382 truncate.ellipsis, 7383 {minChar: truncate.minChar, placeholder: truncate.placeholder} 7384 ); 7385 7386 // FIXME 7387 // It is not appropriate that every line has '...' when truncate multiple lines. 7388 for (var i = 0, len = lines.length; i < len; i++) { 7389 lines[i] = truncateSingleLine(lines[i], options); 7390 } 7391 } 7392 } 7393 7394 return { 7395 lines: lines, 7396 height: height, 7397 outerHeight: outerHeight, 7398 lineHeight: lineHeight 7399 }; 7400 } 7401 7402 /** 7403 * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx' 7404 * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'. 7405 * 7406 * @public 7407 * @param {string} text 7408 * @param {Object} style 7409 * @return {Object} block 7410 * { 7411 * width, 7412 * height, 7413 * lines: [{ 7414 * lineHeight, 7415 * width, 7416 * tokens: [[{ 7417 * styleName, 7418 * text, 7419 * width, // include textPadding 7420 * height, // include textPadding 7421 * textWidth, // pure text width 7422 * textHeight, // pure text height 7423 * lineHeihgt, 7424 * font, 7425 * textAlign, 7426 * textVerticalAlign 7427 * }], [...], ...] 7428 * }, ...] 7429 * } 7430 * If styleName is undefined, it is plain text. 7431 */ 7432 function parseRichText(text, style) { 7433 var contentBlock = {lines: [], width: 0, height: 0}; 7434 7435 text != null && (text += ''); 7436 if (!text) { 7437 return contentBlock; 7438 } 7439 7440 var lastIndex = STYLE_REG.lastIndex = 0; 7441 var result; 7442 while ((result = STYLE_REG.exec(text)) != null) { 7443 var matchedIndex = result.index; 7444 if (matchedIndex > lastIndex) { 7445 pushTokens(contentBlock, text.substring(lastIndex, matchedIndex)); 7446 } 7447 pushTokens(contentBlock, result[2], result[1]); 7448 lastIndex = STYLE_REG.lastIndex; 7449 } 7450 7451 if (lastIndex < text.length) { 7452 pushTokens(contentBlock, text.substring(lastIndex, text.length)); 7453 } 7454 7455 var lines = contentBlock.lines; 7456 var contentHeight = 0; 7457 var contentWidth = 0; 7458 // For `textWidth: 100%` 7459 var pendingList = []; 7460 7461 var stlPadding = style.textPadding; 7462 7463 var truncate = style.truncate; 7464 var truncateWidth = truncate && truncate.outerWidth; 7465 var truncateHeight = truncate && truncate.outerHeight; 7466 if (stlPadding) { 7467 truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]); 7468 truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]); 7469 } 7470 7471 // Calculate layout info of tokens. 7472 for (var i = 0; i < lines.length; i++) { 7473 var line = lines[i]; 7474 var lineHeight = 0; 7475 var lineWidth = 0; 7476 7477 for (var j = 0; j < line.tokens.length; j++) { 7478 var token = line.tokens[j]; 7479 var tokenStyle = token.styleName && style.rich[token.styleName] || {}; 7480 // textPadding should not inherit from style. 7481 var textPadding = token.textPadding = tokenStyle.textPadding; 7482 7483 // textFont has been asigned to font by `normalizeStyle`. 7484 var font = token.font = tokenStyle.font || style.font; 7485 7486 // textHeight can be used when textVerticalAlign is specified in token. 7487 var tokenHeight = token.textHeight = retrieve2( 7488 // textHeight should not be inherited, consider it can be specified 7489 // as box height of the block. 7490 tokenStyle.textHeight, getLineHeight(font) 7491 ); 7492 textPadding && (tokenHeight += textPadding[0] + textPadding[2]); 7493 token.height = tokenHeight; 7494 token.lineHeight = retrieve3( 7495 tokenStyle.textLineHeight, style.textLineHeight, tokenHeight 7496 ); 7497 7498 token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign; 7499 token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle'; 7500 7501 if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) { 7502 return {lines: [], width: 0, height: 0}; 7503 } 7504 7505 token.textWidth = getWidth(token.text, font); 7506 var tokenWidth = tokenStyle.textWidth; 7507 var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto'; 7508 7509 // Percent width, can be `100%`, can be used in drawing separate 7510 // line when box width is needed to be auto. 7511 if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') { 7512 token.percentWidth = tokenWidth; 7513 pendingList.push(token); 7514 tokenWidth = 0;
7515 // Do not truncate in this case, because there is no user case 7516 // and it is too complicated. 7517 } 7518 else { 7519 if (tokenWidthNotSpecified) { 7520 tokenWidth = token.textWidth; 7521 7522 // FIXME: If image is not loaded and textWidth is not specified, calling 7523 // `getBoundingRect()` will not get correct result. 7524 var textBackgroundColor = tokenStyle.textBackgroundColor; 7525 var bgImg = textBackgroundColor && textBackgroundColor.image; 7526 7527 // Use cases: 7528 // (1) If image is not loaded, it will be loaded at render phase and call 7529 // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded 7530 // image, and then the right size will be calculated here at the next tick. 7531 // See `graphic/helper/text.js`. 7532 // (2) If image loaded, and `textBackgroundColor.image` is image src string, 7533 // use `imageHelper.findExistImage` to find cached image. 7534 // `imageHelper.findExistImage` will always be called here before 7535 // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText` 7536 // which ensures that image will not be rendered before correct size calcualted. 7537 if (bgImg) { 7538 bgImg = findExistImage(bgImg); 7539 if (isImageReady(bgImg)) { 7540 tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height); 7541 } 7542 } 7543 } 7544 7545 var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0; 7546 tokenWidth += paddingW; 7547 7548 var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null; 7549 7550 if (remianTruncWidth != null && remianTruncWidth < tokenWidth) { 7551 if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) { 7552 token.text = ''; 7553 token.textWidth = tokenWidth = 0; 7554 } 7555 else { 7556 token.text = truncateText( 7557 token.text, remianTruncWidth - paddingW, font, truncate.ellipsis, 7558 {minChar: truncate.minChar} 7559 ); 7560 token.textWidth = getWidth(token.text, font); 7561 tokenWidth = token.textWidth + paddingW; 7562 } 7563 } 7564 } 7565 7566 lineWidth += (token.width = tokenWidth); 7567 tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight)); 7568 } 7569 7570 line.width = lineWidth; 7571 line.lineHeight = lineHeight; 7572 contentHeight += lineHeight; 7573 contentWidth = Math.max(contentWidth, lineWidth); 7574 } 7575 7576 contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth); 7577 contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight); 7578 7579 if (stlPadding) { 7580 contentBlock.outerWidth += stlPadding[1] + stlPadding[3]; 7581 contentBlock.outerHeight += stlPadding[0] + stlPadding[2]; 7582 } 7583 7584 for (var i = 0; i < pendingList.length; i++) { 7585 var token = pendingList[i]; 7586 var percentWidth = token.percentWidth; 7587 // Should not base on outerWidth, because token can not be placed out of padding. 7588 token.width = parseInt(percentWidth, 10) / 100 * contentWidth; 7589 } 7590 7591 return contentBlock; 7592 } 7593 7594 function pushTokens(block, str, styleName) { 7595 var isEmptyStr = str === ''; 7596 var strs = str.split('\n'); 7597 var lines = block.lines; 7598 7599 for (var i = 0; i < strs.length; i++) { 7600 var text = strs[i]; 7601 var token = { 7602 styleName: styleName, 7603 text: text, 7604 isLineHolder: !text && !isEmptyStr 7605 }; 7606 7607 // The first token should be appended to the last line. 7608 if (!i) { 7609 var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens; 7610 7611 // Consider cases: 7612 // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item 7613 // (which is a placeholder) should be replaced by new token. 7614 // (2) A image backage, where token likes {a|}. 7615 // (3) A redundant '' will affect textAlign in line. 7616 // (4) tokens with the same tplName should not be merged, because 7617 // they should be displayed in different box (with border and padding). 7618 var tokensLen = tokens.length; 7619 (tokensLen === 1 && tokens[0].isLineHolder) 7620 ? (tokens[0] = token) 7621 // Consider text is '', only insert when it is the "lineHolder" or 7622 // "emptyStr". Otherwise a redundant '' will affect textAlign in line. 7623 : ((text || !tokensLen || isEmptyStr) && tokens.push(token)); 7624 } 7625 // Other tokens always start a new line. 7626 else { 7627 // If there is '', insert it as a placeholder. 7628 lines.push({tokens: [token]}); 7629 } 7630 } 7631 } 7632 7633 function makeFont(style) { 7634 // FIXME in node-canvas fontWeight is before fontStyle 7635 // Use `fontSize` `fontFamily` to check whether font properties are defined. 7636 var font = (style.fontSize || style.fontFamily) && [ 7637 style.fontStyle, 7638 style.fontWeight, 7639 (style.fontSize || 12) + 'px', 7640 // If font properties are defined, `fontFamily` should not be ignored. 7641 style.fontFamily || 'sans-serif' 7642 ].join(' '); 7643 return font && trim(font) || style.textFont || style.font; 7644 } 7645 7646 function buildPath(ctx, shape) { 7647 var x = shape.x; 7648 var y = shape.y; 7649 var width = shape.width; 7650 var height = shape.height; 7651 var r = shape.r; 7652 var r1; 7653 var r2; 7654 var r3; 7655 var r4; 7656 7657 // Convert width and height to positive for better borderRadius 7658 if (width < 0) { 7659 x = x + width; 7660 width = -width; 7661 } 7662 if (height < 0) { 7663 y = y + height; 7664 height = -height; 7665 } 7666 7667 if (typeof r === 'number') { 7668 r1 = r2 = r3 = r4 = r; 7669 } 7670 else if (r instanceof Array) { 7671 if (r.length === 1) { 7672 r1 = r2 = r3 = r4 = r[0]; 7673 } 7674 else if (r.length === 2) { 7675 r1 = r3 = r[0]; 7676 r2 = r4 = r[1]; 7677 } 7678 else if (r.length === 3) { 7679 r1 = r[0]; 7680 r2 = r4 = r[1]; 7681 r3 = r[2]; 7682 } 7683 else { 7684 r1 = r[0]; 7685 r2 = r[1]; 7686 r3 = r[2]; 7687 r4 = r[3]; 7688 } 7689 } 7690 else { 7691 r1 = r2 = r3 = r4 = 0; 7692 } 7693 7694 var total; 7695 if (r1 + r2 > width) { 7696 total = r1 + r2; 7697 r1 *= width / total; 7698 r2 *= width / total; 7699 } 7700 if (r3 + r4 > width) { 7701 total = r3 + r4; 7702 r3 *= width / total; 7703 r4 *= width / total; 7704 } 7705 if (r2 + r3 > height) { 7706 total = r2 + r3; 7707 r2 *= height / total; 7708 r3 *= height / total; 7709 } 7710 if (r1 + r4 > height) { 7711 total = r1 + r4; 7712 r1 *= height / total; 7713 r4 *= height / total; 7714 } 7715 ctx.moveTo(x + r1, y); 7716 ctx.lineTo(x + width - r2, y); 7717 r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0); 7718 ctx.lineTo(x + width, y + height - r3); 7719 r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2); 7720 ctx.lineTo(x + r4, y + height); 7721 r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI); 7722 ctx.lineTo(x, y + r1); 7723 r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5); 7724 } 7725 7726// TODO: Have not support 'start', 'end' yet. 7727 var VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1}; 7728 var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1}; 7729 7730 /** 7731 * @param {module:zrender/graphic/Style} style 7732 * @return {module:zrender/graphic/Style} The input style. 7733 */ 7734 function normalizeTextStyle(style) { 7735 normalizeStyle(style); 7736 each$1(style.rich, normalizeStyle); 7737 return style; 7738 } 7739 7740 function normalizeStyle(style) { 7741 if (style) { 7742 7743 style.font = makeFont(style); 7744 7745 var textAlign = style.textAlign; 7746 textAlign === 'middle' && (textAlign = 'center'); 7747 style.textAlign = ( 7748 textAlign == null || VALID_TEXT_ALIGN[textAlign] 7749 ) ? textAlign : 'left'; 7750 7751 // Compatible with textBaseline. 7752 var textVerticalAlign = style.textVerticalAlign || style.textBaseline; 7753 textVerticalAlign === 'center' && (textVerticalAlign = 'middle'); 7754 style.textVerticalAlign = ( 7755 textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign] 7756 ) ? textVerticalAlign : 'top'; 7757 7758 var textPadding = style.textPadding; 7759 if (textPadding) { 7760 style.textPadding = normalizeCssArray(style.textPadding); 7761 } 7762 } 7763 } 7764 7765 /** 7766 * @param {CanvasRenderingContext2D} ctx 7767 * @param {string} text 7768 * @param {module:zrender/graphic/Style} style 7769 * @param {Object|boolean} [rect] {x, y, width, height} 7770 * If set false, rect text is not used. 7771 */ 7772 function renderText(hostEl, ctx, text, style, rect) { 7773 style.rich 7774 ? renderRichText(hostEl, ctx, text, style, rect) 7775 : renderPlainText(hostEl, ctx, text, style, rect); 7776 } 7777 7778 function renderPlainText(hostEl, ctx, text, style, rect) { 7779 var font = setCtx(ctx, 'font', style.font || DEFAULT_FONT); 7780 7781 var textPadding = style.textPadding; 7782 7783 var contentBlock = hostEl.__textCotentBlock; 7784 if (!contentBlock || hostEl.__dirty) { 7785 contentBlock = hostEl.__textCotentBlock = parsePlainText( 7786 text, font, textPadding, style.truncate 7787 ); 7788 } 7789 7790 var outerHeight = contentBlock.outerHeight; 7791 7792 var textLines = contentBlock.lines; 7793 var lineHeight = contentBlock.lineHeight; 7794 7795 var boxPos = getBoxPosition(outerHeight, style, rect); 7796 var baseX = boxPos.baseX; 7797 var baseY = boxPos.baseY; 7798 var textAlign = boxPos.textAlign; 7799 var textVerticalAlign = boxPos.textVerticalAlign; 7800 7801 // Origin of textRotation should be the base point of text drawing. 7802 applyTextRotation(ctx, style, rect, baseX, baseY); 7803 7804 var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign); 7805 var textX = baseX; 7806 var textY = boxY; 7807
7808 var needDrawBg = needDrawBackground(style); 7809 if (needDrawBg || textPadding) { 7810 // Consider performance, do not call getTextWidth util necessary. 7811 var textWidth = getWidth(text, font); 7812 var outerWidth = textWidth; 7813 textPadding && (outerWidth += textPadding[1] + textPadding[3]); 7814 var boxX = adjustTextX(baseX, outerWidth, textAlign); 7815 7816 needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight); 7817 7818 if (textPadding) { 7819 textX = getTextXForPadding(baseX, textAlign, textPadding); 7820 textY += textPadding[0]; 7821 } 7822 } 7823 7824 setCtx(ctx, 'textAlign', textAlign || 'left'); 7825 // Force baseline to be "middle". Otherwise, if using "top", the 7826 // text will offset downward a little bit in font "Microsoft YaHei". 7827 setCtx(ctx, 'textBaseline', 'middle'); 7828 7829 // Always set shadowBlur and shadowOffset to avoid leak from displayable. 7830 setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0); 7831 setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent'); 7832 setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0); 7833 setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0); 7834 7835 // `textBaseline` is set as 'middle'. 7836 textY += lineHeight / 2; 7837 7838 var textStrokeWidth = style.textStrokeWidth; 7839 var textStroke = getStroke(style.textStroke, textStrokeWidth); 7840 var textFill = getFill(style.textFill); 7841 7842 if (textStroke) { 7843 setCtx(ctx, 'lineWidth', textStrokeWidth); 7844 setCtx(ctx, 'strokeStyle', textStroke); 7845 } 7846 if (textFill) { 7847 setCtx(ctx, 'fillStyle', textFill); 7848 } 7849 7850 for (var i = 0; i < textLines.length; i++) { 7851 // Fill after stroke so the outline will not cover the main part. 7852 textStroke && ctx.strokeText(textLines[i], textX, textY); 7853 textFill && ctx.fillText(textLines[i], textX, textY); 7854 textY += lineHeight; 7855 } 7856 } 7857 7858 function renderRichText(hostEl, ctx, text, style, rect) { 7859 var contentBlock = hostEl.__textCotentBlock; 7860 7861 if (!contentBlock || hostEl.__dirty) { 7862 contentBlock = hostEl.__textCotentBlock = parseRichText(text, style); 7863 } 7864 7865 drawRichText(hostEl, ctx, contentBlock, style, rect); 7866 } 7867 7868 function drawRichText(hostEl, ctx, contentBlock, style, rect) { 7869 var contentWidth = contentBlock.width; 7870 var outerWidth = contentBlock.outerWidth; 7871 var outerHeight = contentBlock.outerHeight; 7872 var textPadding = style.textPadding; 7873 7874 var boxPos = getBoxPosition(outerHeight, style, rect); 7875 var baseX = boxPos.baseX; 7876 var baseY = boxPos.baseY; 7877 var textAlign = boxPos.textAlign; 7878 var textVerticalAlign = boxPos.textVerticalAlign; 7879 7880 // Origin of textRotation should be the base point of text drawing. 7881 applyTextRotation(ctx, style, rect, baseX, baseY); 7882 7883 var boxX = adjustTextX(baseX, outerWidth, textAlign); 7884 var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign); 7885 var xLeft = boxX; 7886 var lineTop = boxY; 7887 if (textPadding) { 7888 xLeft += textPadding[3]; 7889 lineTop += textPadding[0]; 7890 } 7891 var xRight = xLeft + contentWidth; 7892 7893 needDrawBackground(style) && drawBackground( 7894 hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight 7895 ); 7896 7897 for (var i = 0; i < contentBlock.lines.length; i++) { 7898 var line = contentBlock.lines[i]; 7899 var tokens = line.tokens; 7900 var tokenCount = tokens.length; 7901 var lineHeight = line.lineHeight; 7902 var usedWidth = line.width; 7903 7904 var leftIndex = 0; 7905 var lineXLeft = xLeft; 7906 var lineXRight = xRight; 7907 var rightIndex = tokenCount - 1; 7908 var token; 7909 7910 while ( 7911 leftIndex < tokenCount 7912 && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left') 7913 ) { 7914 placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left'); 7915 usedWidth -= token.width; 7916 lineXLeft += token.width; 7917 leftIndex++; 7918 } 7919 7920 while ( 7921 rightIndex >= 0 7922 && (token = tokens[rightIndex], token.textAlign === 'right') 7923 ) { 7924 placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right'); 7925 usedWidth -= token.width; 7926 lineXRight -= token.width; 7927 rightIndex--; 7928 } 7929 7930 // The other tokens are placed as textAlign 'center' if there is enough space. 7931 lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2; 7932 while (leftIndex <= rightIndex) { 7933 token = tokens[leftIndex]; 7934 // Consider width specified by user, use 'center' rather than 'left'. 7935 placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center'); 7936 lineXLeft += token.width; 7937 leftIndex++; 7938 } 7939 7940 lineTop += lineHeight; 7941 } 7942 } 7943 7944 function applyTextRotation(ctx, style, rect, x, y) { 7945 // textRotation only apply in RectText. 7946 if (rect && style.textRotation) { 7947 var origin = style.textOrigin; 7948 if (origin === 'center') { 7949 x = rect.width / 2 + rect.x; 7950 y = rect.height / 2 + rect.y; 7951 } 7952 else if (origin) { 7953 x = origin[0] + rect.x; 7954 y = origin[1] + rect.y; 7955 } 7956 7957 ctx.translate(x, y); 7958 // Positive: anticlockwise 7959 ctx.rotate(-style.textRotation); 7960 ctx.translate(-x, -y); 7961 } 7962 } 7963 7964 function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) { 7965 var tokenStyle = style.rich[token.styleName] || {}; 7966 7967 // 'ctx.textBaseline' is always set as 'middle', for sake of 7968 // the bias of "Microsoft YaHei". 7969 var textVerticalAlign = token.textVerticalAlign; 7970 var y = lineTop + lineHeight / 2; 7971 if (textVerticalAlign === 'top') { 7972 y = lineTop + token.height / 2; 7973 } 7974 else if (textVerticalAlign === 'bottom') { 7975 y = lineTop + lineHeight - token.height / 2; 7976 } 7977
7978 !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground( 7979 hostEl, 7980 ctx, 7981 tokenStyle, 7982 textAlign === 'right' 7983 ? x - token.width 7984 : textAlign === 'center' 7985 ? x - token.width / 2 7986 : x, 7987 y - token.height / 2, 7988 token.width, 7989 token.height 7990 ); 7991 7992 var textPadding = token.textPadding; 7993 if (textPadding) { 7994 x = getTextXForPadding(x, textAlign, textPadding); 7995 y -= token.height / 2 - textPadding[2] - token.textHeight / 2; 7996 } 7997 7998 setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0)); 7999 setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent'); 8000 setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0)); 8001 setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0)); 8002 8003 setCtx(ctx, 'textAlign', textAlign); 8004 // Force baseline to be "middle". Otherwise, if using "top", the 8005 // text will offset downward a little bit in font "Microsoft YaHei". 8006 setCtx(ctx, 'textBaseline', 'middle'); 8007 8008 setCtx(ctx, 'font', token.font || DEFAULT_FONT); 8009 8010 var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth); 8011 var textFill = getFill(tokenStyle.textFill || style.textFill); 8012 var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth); 8013 8014 // Fill after stroke so the outline will not cover the main part. 8015 if (textStroke) { 8016 setCtx(ctx, 'lineWidth', textStrokeWidth); 8017 setCtx(ctx, 'strokeStyle', textStroke); 8018 ctx.strokeText(token.text, x, y); 8019 } 8020 if (textFill) { 8021 setCtx(ctx, 'fillStyle', textFill); 8022 ctx.fillText(token.text, x, y); 8023 } 8024 } 8025 8026 function needDrawBackground(style) { 8027 return style.textBackgroundColor 8028 || (style.textBorderWidth && style.textBorderColor); 8029 } 8030 8031// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius} 8032// shape: {x, y, width, height} 8033 function drawBackground(hostEl, ctx, style, x, y, width, height) { 8034 var textBackgroundColor = style.textBackgroundColor; 8035 var textBorderWidth = style.textBorderWidth; 8036 var textBorderColor = style.textBorderColor; 8037 var isPlainBg = isString(textBackgroundColor); 8038 8039 setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0); 8040 setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent'); 8041 setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0); 8042 setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0); 8043 8044 if (isPlainBg || (textBorderWidth && textBorderColor)) { 8045 ctx.beginPath(); 8046 var textBorderRadius = style.textBorderRadius; 8047 if (!textBorderRadius) { 8048 ctx.rect(x, y, width, height); 8049 } 8050 else { 8051 buildPath(ctx, { 8052 x: x, y: y, width: width, height: height, r: textBorderRadius 8053 }); 8054 } 8055 ctx.closePath(); 8056 } 8057 8058 if (isPlainBg) { 8059 setCtx(ctx, 'fillStyle', textBackgroundColor); 8060 ctx.fill(); 8061 } 8062 else if (isObject$1(textBackgroundColor)) { 8063 var image = textBackgroundColor.image; 8064 8065 image = createOrUpdateImage( 8066 image, null, hostEl, onBgImageLoaded, textBackgroundColor 8067 ); 8068 if (image && isImageReady(image)) { 8069 ctx.drawImage(image, x, y, width, height); 8070 } 8071 } 8072 8073 if (textBorderWidth && textBorderColor) { 8074 setCtx(ctx, 'lineWidth', textBorderWidth); 8075 setCtx(ctx, 'strokeStyle', textBorderColor); 8076 ctx.stroke(); 8077 } 8078 } 8079
8080 function onBgImageLoaded(image, textBackgroundColor) { 8081 // Replace image, so that `contain/text.js#parseRichText` 8082 // will get correct result in next tick. 8083 textBackgroundColor.image = image; 8084 } 8085 8086 function getBoxPosition(blockHeiht, style, rect) { 8087 var baseX = style.x || 0; 8088 var baseY = style.y || 0; 8089 var textAlign = style.textAlign; 8090 var textVerticalAlign = style.textVerticalAlign; 8091 8092 // Text position represented by coord 8093 if (rect) { 8094 var textPosition = style.textPosition; 8095 if (textPosition instanceof Array) { 8096 // Percent 8097 baseX = rect.x + parsePercent(textPosition[0], rect.width); 8098 baseY = rect.y + parsePercent(textPosition[1], rect.height); 8099 } 8100 else { 8101 var res = adjustTextPositionOnRect( 8102 textPosition, rect, style.textDistance 8103 ); 8104 baseX = res.x; 8105 baseY = res.y; 8106 // Default align and baseline when has textPosition 8107 textAlign = textAlign || res.textAlign; 8108 textVerticalAlign = textVerticalAlign || res.textVerticalAlign; 8109 } 8110 8111 // textOffset is only support in RectText, otherwise 8112 // we have to adjust boundingRect for textOffset. 8113 var textOffset = style.textOffset; 8114 if (textOffset) { 8115 baseX += textOffset[0]; 8116 baseY += textOffset[1]; 8117 } 8118 } 8119 8120 return { 8121 baseX: baseX, 8122 baseY: baseY, 8123 textAlign: textAlign, 8124 textVerticalAlign: textVerticalAlign 8125 }; 8126 } 8127 8128 function setCtx(ctx, prop, value) { 8129 ctx[prop] = fixShadow(ctx, prop, value); 8130 return ctx[prop]; 8131 } 8132 8133 /** 8134 * @param {string} [stroke] If specified, do not check style.textStroke. 8135 * @param {string} [lineWidth] If specified, do not check style.textStroke. 8136 * @param {number} style 8137 */ 8138 function getStroke(stroke, lineWidth) { 8139 return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none') 8140 ? null 8141 // TODO pattern and gradient? 8142 : (stroke.image || stroke.colorStops) 8143 ? '#000' 8144 : stroke; 8145 } 8146 8147 function getFill(fill) { 8148 return (fill == null || fill === 'none') 8149 ? null 8150 // TODO pattern and gradient? 8151 : (fill.image || fill.colorStops) 8152 ? '#000' 8153 : fill; 8154 } 8155 8156 function parsePercent(value, maxValue) { 8157 if (typeof value === 'string') { 8158 if (value.lastIndexOf('%') >= 0) { 8159 return parseFloat(value) / 100 * maxValue; 8160 } 8161 return parseFloat(value); 8162 } 8163 return value; 8164 } 8165 8166 function getTextXForPadding(x, textAlign, textPadding) { 8167 return textAlign === 'right' 8168 ? (x - textPadding[1]) 8169 : textAlign === 'center' 8170 ? (x + textPadding[3] / 2 - textPadding[1] / 2) 8171 : (x + textPadding[3]); 8172 } 8173 8174 /** 8175 * @param {string} text 8176 * @param {module:zrender/Style} style 8177 * @return {boolean} 8178 */ 8179 function needDrawText(text, style) { 8180 return text != null 8181 && (text 8182 || style.textBackgroundColor 8183 || (style.textBorderWidth && style.textBorderColor) 8184 || style.textPadding 8185 ); 8186 } 8187 8188 /** 8189 * Mixin for drawing text in a element bounding rect 8190 * @module zrender/mixin/RectText 8191 */ 8192 8193 var tmpRect$1 = new BoundingRect(); 8194 8195 var RectText = function () {}; 8196 8197 RectText.prototype = { 8198 8199 constructor: RectText, 8200 8201 /** 8202 * Draw text in a rect with specified position. 8203 * @param {CanvasRenderingContext2D} ctx 8204 * @param {Object} rect Displayable rect 8205 */ 8206 drawRectText: function (ctx, rect) { 8207 var style = this.style; 8208 8209 rect = style.textRect || rect; 8210 8211 // Optimize, avoid normalize every time. 8212 this.__dirty && normalizeTextStyle(style, true); 8213 8214 var text = style.text; 8215 8216 // Convert to string 8217 text != null && (text += ''); 8218 8219 if (!needDrawText(text, style)) { 8220 return; 8221 } 8222 8223 // FIXME 8224 ctx.save(); 8225 8226 // Transform rect to view space 8227 var transform = this.transform; 8228 if (!style.transformText) { 8229 if (transform) { 8230 tmpRect$1.copy(rect); 8231 tmpRect$1.applyTransform(transform); 8232 rect = tmpRect$1; 8233 } 8234 } 8235 else { 8236 this.setTransform(ctx); 8237 } 8238 8239 // transformText and textRotation can not be used at the same time. 8240 renderText(this, ctx, text, style, rect); 8241
8242 ctx.restore(); 8243 } 8244 }; 8245 8246 /** 8247 * éî粯é忮饿§è°é©è¹è¢« 8248 * Base class of all displayable graphic objects 8249 * @module zrender/graphic/Displayable 8250 */ 8251 8252 8253 /** 8254 * @alias module:zrender/graphic/Displayable 8255 * @extends module:zrender/Element 8256 * @extends module:zrender/graphic/mixin/RectText 8257 */ 8258 function Displayable(opts) { 8259 8260 opts = opts || {}; 8261 8262 Element.call(this, opts); 8263 8264 // Extend properties 8265 for (var name in opts) { 8266 if ( 8267 opts.hasOwnProperty(name) && 8268 name !== 'style' 8269 ) { 8270 this[name] = opts[name]; 8271 } 8272 } 8273 8274 /** 8275 * @type {module:zrender/graphic/Style} 8276 */ 8277 this.style = new Style(opts.style, this); 8278 8279 this._rect = null; 8280 // Shapes for cascade clipping. 8281 this.__clipPaths = []; 8282 8283 // FIXME Stateful must be mixined after style is setted 8284 // Stateful.call(this, opts); 8285 } 8286 8287 Displayable.prototype = { 8288 8289 constructor: Displayable, 8290 8291 type: 'displayable', 8292 8293 /** 8294 * Displayable éîææ¶é¸¿å°éåainter æ¶î ç´°éè§åµçã¦ç£çæ¿å½éî æ§¸éï¹æ¸¶ç佹槸éï¹æ¸¶çä¾å¸éæ®ç²¯é� 8295 * Dirty flag. From which painter will determine if this displayable object needs brush 8296 * @name module:zrender/graphic/Displayable#__dirty 8297 * @type {boolean} 8298 */ 8299 __dirty: true, 8300 8301 /** 8302 * 饿§è°éîæéîîéå±¼è´trueéæµç¬ç¼æ¨ºå饿§è°é屼絾éîç²é³åÐéæ¦ç´¶éå¦ç°¨æµ � 8303 * If ignore drawing of the displayable object. Mouse event will still be triggered 8304 * @name module:/zrender/graphic/Displayable#invisible 8305 * @type {boolean} 8306 * @default false 8307 */ 8308 invisible: false, 8309 8310 /** 8311 * @name module:/zrender/graphic/Displayable#z 8312 * @type {number} 8313 * @default 0 8314 */ 8315 z: 0, 8316 8317 /** 8318 * @name module:/zrender/graphic/Displayable#z 8319 * @type {number} 8320 * @default 0 8321 */ 8322 z2: 0, 8323 8324 /** 8325 * zç俵eveléå±½å ç¹æ°±ç²¯é¢è¯²æ¹ªéîç°canvasæ¶ï¿½ 8326 * @name module:/zrender/graphic/Displayable#zlevel 8327 * @type {number} 8328 * @default 0 8329 */ 8330 zlevel: 0, 8331 8332 /** 8333 * éîæéîå«é·ï¿½ 8334 * @name module:/zrender/graphic/Displayable#draggable 8335 * @type {boolean} 8336 * @default false 8337 */ 8338 draggable: false, 8339 8340 /** 8341 * éîæå§ï½ æ¹ªé·æ ¨å«¿ 8342 * @name module:/zrender/graphic/Displayable#draggable 8343 * @type {boolean} 8344 * @default false 8345 */ 8346 dragging: false, 8347 8348 /** 8349 * éîæé©ç¨¿ç°²æ¦§ç³ç£æµå¬©æ¬¢ 8350 * @name module:/zrender/graphic/Displayable#silent 8351 * @type {boolean} 8352 * @default false 8353 */ 8354 silent: false, 8355 8356 /** 8357 * If enable culling 8358 * @type {boolean} 8359 * @default false 8360 */ 8361 culling: false, 8362 8363 /** 8364 * Mouse cursor when hovered 8365 * @name module:/zrender/graphic/Displayable#cursor 8366 * @type {string} 8367 */ 8368 cursor: 'pointer', 8369 8370 /** 8371 * If hover area is bounding rect 8372 * @name module:/zrender/graphic/Displayable#rectHover 8373 * @type {string} 8374 */ 8375 rectHover: false, 8376 8377 /** 8378 * Render the element progressively when the value >= 0, 8379 * usefull for large data. 8380 * @type {boolean} 8381 */ 8382 progressive: false, 8383 8384 /** 8385 * @type {boolean} 8386 */ 8387 incremental: false, 8388 // inplace is used with incremental 8389 inplace: false, 8390 8391 beforeBrush: function (ctx) {}, 8392 8393 afterBrush: function (ctx) {}, 8394 8395 /** 8396 * 饿§è°ç¼æ¨ºåéè§ç¡¶ 8397 * @param {CanvasRenderingContext2D} ctx 8398 */ 8399 // Interface 8400 brush: function (ctx, prevEl) {}, 8401 8402 /** 8403 * é¾å³°å½éâ¬çå¿å¯é¥å¯¸æ´ 8404 * @return {module:zrender/core/BoundingRect} 8405 */ 8406 // Interface 8407 getBoundingRect: function () {}, 8408 8409 /** 8410 * éãæé§æç£ x, y éîæé¦ã¥æµè¤°îç¬ 8411 * If displayable element contain coord x, y 8412 * @param {number} x 8413 * @param {number} y 8414 * @return {boolean} 8415 */ 8416 contain: function (x, y) { 8417 return this.rectContain(x, y); 8418 }, 8419 8420 /** 8421 * @param {Function} cb 8422 * @param {} context 8423 */ 8424 traverse: function (cb, context) { 8425 cb.call(context, this); 8426 }, 8427 8428 /** 8429 * éãæé§æç£ x, y éîæé¦ã¥æµè¤°ã¢æ®éåæ´¿é©æç¬ 8430 * If bounding rect of element contain coord x, y 8431 * @param {number} x 8432 * @param {number} y 8433 * @return {boolean} 8434 */ 8435 rectContain: function (x, y) { 8436 var coord = this.transformCoordToLocal(x, y);
8437 var rect = this.getBoundingRect(); 8438 return rect.contain(coord[0], coord[1]); 8439 }, 8440 8441 /** 8442 * éåªî饿§è°éåªç¤æ¶é¸¿å°éå±½èæ¶æ¿æ¹ªæ¶å¬©ç«´ç¯Ñå¸ç¼ï¿½ 8443 * Mark displayable element dirty and refresh next frame 8444 */ 8445 dirty: function () { 8446 this.__dirty = true; 8447 8448 this._rect = null; 8449 8450 this.__zr && this.__zr.refresh(); 8451 }, 8452 8453 /** 8454 * 饿§è°éîææµ¼æ°³Ðéæç°¨æµ � 8455 * If displayable object binded any event 8456 * @return {boolean} 8457 */ 8458 // TODO, 髿°³ç¹ bind ç¼æç¾é¨åªç°¨æµ � 8459 // isSilent: function () { 8460 // return !( 8461 // this.hoverable || this.draggable 8462 // || this.onmousemove || this.onmouseover || this.onmouseout 8463 // || this.onmousedown || this.onmouseup || this.onclick 8464 // || this.ondragenter || this.ondragover || this.ondragleave 8465 // || this.ondrop 8466 // ); 8467 // }, 8468 /** 8469 * Alias for animate('style') 8470 * @param {boolean} loop 8471 */ 8472 animateStyle: function (loop) { 8473 return this.animate('style', loop); 8474 }, 8475 8476 attrKV: function (key, value) { 8477 if (key !== 'style') { 8478 Element.prototype.attrKV.call(this, key, value); 8479 } 8480 else { 8481 this.style.set(value); 8482 } 8483 }, 8484 8485 /** 8486 * @param {Object|string} key 8487 * @param {*} value 8488 */ 8489 setStyle: function (key, value) { 8490 this.style.set(key, value); 8491 this.dirty(false); 8492 return this; 8493 }, 8494 8495 /** 8496 * Use given style object 8497 * @param {Object} obj 8498 */ 8499 useStyle: function (obj) { 8500 this.style = new Style(obj, this); 8501 this.dirty(false); 8502 return this; 8503 } 8504 }; 8505 8506 inherits(Displayable, Element); 8507 8508 mixin(Displayable, RectText); 8509 8510 /** 8511 * @alias zrender/graphic/Image 8512 * @extends module:zrender/graphic/Displayable 8513 * @constructor 8514 * @param {Object} opts 8515 */ 8516 function ZImage(opts) { 8517 Displayable.call(this, opts); 8518 } 8519 8520 ZImage.prototype = { 8521 8522 constructor: ZImage, 8523 8524 type: 'image', 8525 8526 brush: function (ctx, prevEl) { 8527 var style = this.style; 8528 var src = style.image; 8529 8530 // Must bind each time 8531 style.bind(ctx, this, prevEl); 8532 8533 var image = this._image = createOrUpdateImage( 8534 src, 8535 this._image, 8536 this, 8537 this.onload 8538 ); 8539 8540 if (!image || !isImageReady(image)) { 8541 return; 8542 } 8543 8544 // é¥å§å¢å®¸è¬ç²¡éçºæµç¹å±¾å 8545 // if (image.nodeName.toUpperCase() == 'IMG') { 8546 // if (!image.complete) { 8547 // return; 8548 // } 8549 // } 8550 // Else is canvas 8551 8552 var x = style.x || 0; 8553 var y = style.y || 0; 8554 var width = style.width; 8555 var height = style.height; 8556 var aspect = image.width / image.height; 8557 if (width == null && height != null) { 8558 // Keep image/height ratio 8559 width = height * aspect; 8560 } 8561 else if (height == null && width != null) { 8562 height = width / aspect; 8563 } 8564 else if (width == null && height == null) { 8565 width = image.width; 8566 height = image.height; 8567 } 8568 8569 // çå§çtransform 8570 this.setTransform(ctx); 8571 8572 if (style.sWidth && style.sHeight) { 8573 var sx = style.sx || 0; 8574 var sy = style.sy || 0; 8575 ctx.drawImage( 8576 image, 8577 sx, sy, style.sWidth, style.sHeight, 8578 x, y, width, height 8579 ); 8580 } 8581 else if (style.sx && style.sy) { 8582 var sx = style.sx; 8583 var sy = style.sy; 8584 var sWidth = width - sx; 8585 var sHeight = height - sy; 8586 ctx.drawImage( 8587 image, 8588 sx, sy, sWidth, sHeight, 8589 x, y, width, height 8590 ); 8591 } 8592 else { 8593 ctx.drawImage(image, x, y, width, height); 8594 } 8595 8596 // Draw rect text 8597 if (style.text != null) { 8598 // Only restore transform when needs draw text. 8599 this.restoreTransform(ctx);
8600 this.drawRectText(ctx, this.getBoundingRect()); 8601 } 8602 }, 8603 8604 getBoundingRect: function () { 8605 var style = this.style; 8606 if (! this._rect) { 8607 this._rect = new BoundingRect( 8608 style.x || 0, style.y || 0, style.width || 0, style.height || 0 8609 ); 8610 } 8611 return this._rect; 8612 } 8613 }; 8614 8615 inherits(ZImage, Displayable); 8616 8617 var HOVER_LAYER_ZLEVEL = 1e5; 8618 var CANVAS_ZLEVEL = 314159; 8619 8620 var EL_AFTER_INCREMENTAL_INC = 0.01; 8621 var INCREMENTAL_INC = 0.001; 8622 8623 function parseInt10(val) { 8624 return parseInt(val, 10); 8625 } 8626 8627 function isLayerValid(layer) { 8628 if (!layer) { 8629 return false; 8630 } 8631 8632 if (layer.__builtin__) { 8633 return true; 8634 } 8635 8636 if (typeof(layer.resize) !== 'function' 8637 || typeof(layer.refresh) !== 'function' 8638 ) { 8639 return false; 8640 } 8641 8642 return true; 8643 } 8644 8645 var tmpRect = new BoundingRect(0, 0, 0, 0); 8646 var viewRect = new BoundingRect(0, 0, 0, 0); 8647 function isDisplayableCulled(el, width, height) { 8648 tmpRect.copy(el.getBoundingRect()); 8649 if (el.transform) { 8650 tmpRect.applyTransform(el.transform); 8651 } 8652 viewRect.width = width; 8653 viewRect.height = height; 8654 return !tmpRect.intersect(viewRect); 8655 } 8656 8657 function isClipPathChanged(clipPaths, prevClipPaths) { 8658 if (clipPaths == prevClipPaths) { // Can both be null or undefined 8659 return false; 8660 } 8661 8662 if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) { 8663 return true; 8664 } 8665 for (var i = 0; i < clipPaths.length; i++) { 8666 if (clipPaths[i] !== prevClipPaths[i]) { 8667 return true; 8668 } 8669 } 8670 } 8671 8672 function doClip(clipPaths, ctx) { 8673 for (var i = 0; i < clipPaths.length; i++) { 8674 var clipPath = clipPaths[i]; 8675 8676 clipPath.setTransform(ctx); 8677 ctx.beginPath(); 8678 clipPath.buildPath(ctx, clipPath.shape); 8679 ctx.clip(); 8680 // Transform back 8681 clipPath.restoreTransform(ctx); 8682 } 8683 } 8684 8685 function createRoot(width, height) { 8686 var domRoot = document.createElement('div'); 8687 8688 // domRoot.onselectstart = returnFalse; // é¬å®å¤æ¤¤ç¸æ½°é«å¤è é¨å«æç� 8689 domRoot.style.cssText = [ 8690 'position:relative', 8691 'overflow:hidden', 8692 'width:' + width + 'px', 8693 'height:' + height + 'px', 8694 'padding:0', 8695 'margin:0', 8696 'border-width:0' 8697 ].join(';') + ';'; 8698 8699 return domRoot; 8700 } 8701 8702 8703 /** 8704 * @alias module:zrender/Painter 8705 * @constructor 8706 * @param {HTMLElement} root ç¼æ¨ºæµç¹ç°æ« 8707 * @param {module:zrender/Storage} storage 8708 * @param {Object} opts 8709 */ 8710 var Painter = function (root, storage, opts) { 8711 8712 this.type = 'canvas'; 8713 8714 // In node environment using node-canvas 8715 var singleCanvas = !root.nodeName // In node ? 8716 || root.nodeName.toUpperCase() === 'CANVAS'; 8717 8718 this._opts = opts = extend({}, opts || {}); 8719 8720 /** 8721 * @type {number} 8722 */ 8723 this.dpr = opts.devicePixelRatio || devicePixelRatio; 8724 /** 8725 * @type {boolean} 8726 * @private 8727 */ 8728 this._singleCanvas = singleCanvas; 8729 /** 8730 * ç¼æ¨ºæµç¹ç°æ« 8731 * @type {HTMLElement} 8732 */ 8733 this.root = root; 8734 8735 var rootStyle = root.style; 8736 8737 if (rootStyle) { 8738 rootStyle['-webkit-tap-highlight-color'] = 'transparent'; 8739 rootStyle['-webkit-user-select'] = 8740 rootStyle['user-select'] = 8741 rootStyle['-webkit-touch-callout'] = 'none'; 8742 8743 root.innerHTML = ''; 8744 } 8745 8746 /** 8747 * @type {module:zrender/Storage} 8748 */ 8749 this.storage = storage; 8750 8751 /** 8752 * @type {Array.<number>} 8753 * @private 8754 */ 8755 var zlevelList = this._zlevelList = []; 8756 8757 /** 8758 * @type {Object.<string, module:zrender/Layer>} 8759 * @private 8760 */ 8761 var layers = this._layers = {}; 8762 8763 /** 8764 * @type {Object.<string, Object>} 8765 * @private 8766 */ 8767 this._layerConfig = {}; 8768 8769 /** 8770 * zrender will do compositing when root is a canvas and have multiple zlevels. 8771 */ 8772 this._needsManuallyCompositing = false; 8773 8774 if (!singleCanvas) { 8775 this._width = this._getSize(0); 8776 this._height = this._getSize(1); 8777 8778 var domRoot = this._domRoot = createRoot( 8779 this._width, this._height 8780 ); 8781 root.appendChild(domRoot); 8782 } 8783 else { 8784 var width = root.width; 8785 var height = root.height; 8786 8787 if (opts.width != null) { 8788 width = opts.width; 8789 } 8790 if (opts.height != null) { 8791 height = opts.height; 8792 } 8793 this.dpr = opts.devicePixelRatio || 1; 8794 8795 // Use canvas width and height directly 8796 root.width = width * this.dpr; 8797 root.height = height * this.dpr; 8798 8799 this._width = width; 8800 this._height = height; 8801 8802 // Create layer if only one given canvas 8803 // Device can be specified to create a high dpi image. 8804 var mainLayer = new Layer(root, this, this.dpr); 8805 mainLayer.__builtin__ = true; 8806 mainLayer.initContext(); 8807 // FIXME Use canvas width and height 8808 // mainLayer.resize(width, height); 8809 layers[CANVAS_ZLEVEL] = mainLayer; 8810 mainLayer.zlevel = CANVAS_ZLEVEL; 8811 // Not use common zlevel. 8812 zlevelList.push(CANVAS_ZLEVEL); 8813 8814 this._domRoot = root; 8815 } 8816 8817 /** 8818 * @type {module:zrender/Layer} 8819 * @private 8820 */ 8821 this._hoverlayer = null; 8822 8823 this._hoverElements = []; 8824 }; 8825 8826 Painter.prototype = { 8827 8828 constructor: Painter, 8829 8830 getType: function () { 8831 return 'canvas'; 8832 }, 8833 8834 /** 8835 * If painter use a single canvas 8836 * @return {boolean} 8837 */ 8838 isSingleCanvas: function () { 8839 return this._singleCanvas; 8840 }, 8841 /** 8842 * @return {HTMLDivElement} 8843 */ 8844 getViewportRoot: function () { 8845 return this._domRoot; 8846 }, 8847 8848 getViewportRootOffset: function () { 8849 var viewportRoot = this.getViewportRoot(); 8850 if (viewportRoot) { 8851 return { 8852 offsetLeft: viewportRoot.offsetLeft || 0, 8853 offsetTop: viewportRoot.offsetTop || 0 8854 }; 8855 } 8856 }, 8857 8858 /** 8859 * ééæ 8860 * @param {boolean} [paintAll=false] 寮ååç¼æ¨ºåéµâ¬é塪isplayable 8861 */ 8862 refresh: function (paintAll) { 8863 8864 var list = this.storage.getDisplayList(true); 8865 8866 var zlevelList = this._zlevelList; 8867 8868 this._redrawId = Math.random(); 8869 8870 this._paintList(list, paintAll, this._redrawId); 8871 8872 // Paint custum layers 8873 for (var i = 0; i < zlevelList.length; i++) { 8874 var z = zlevelList[i]; 8875 var layer = this._layers[z]; 8876 if (!layer.__builtin__ && layer.refresh) { 8877 var clearColor = i === 0 ? this._backgroundColor : null; 8878 layer.refresh(clearColor); 8879 } 8880 } 8881 8882 this.refreshHover(); 8883 8884 return this; 8885 }, 8886 8887 addHover: function (el, hoverStyle) { 8888 if (el.__hoverMir) { 8889 return; 8890 } 8891 var elMirror = new el.constructor({ 8892 style: el.style, 8893 shape: el.shape 8894 }); 8895 elMirror.__from = el; 8896 el.__hoverMir = elMirror;
8897 elMirror.setStyle(hoverStyle); 8898 this._hoverElements.push(elMirror); 8899 }, 8900 8901 removeHover: function (el) { 8902 var elMirror = el.__hoverMir; 8903 var hoverElements = this._hoverElements; 8904 var idx = indexOf(hoverElements, elMirror); 8905 if (idx >= 0) { 8906 hoverElements.splice(idx, 1); 8907 } 8908 el.__hoverMir = null; 8909 }, 8910 8911 clearHover: function (el) { 8912 var hoverElements = this._hoverElements; 8913 for (var i = 0; i < hoverElements.length; i++) { 8914 var from = hoverElements[i].__from; 8915 if (from) { 8916 from.__hoverMir = null; 8917 } 8918 } 8919 hoverElements.length = 0; 8920 }, 8921 8922 refreshHover: function () { 8923 var hoverElements = this._hoverElements; 8924 var len = hoverElements.length; 8925 var hoverLayer = this._hoverlayer; 8926 hoverLayer && hoverLayer.clear(); 8927 8928 if (!len) { 8929 return; 8930 } 8931 sort(hoverElements, this.storage.displayableSortFunc); 8932 8933 // Use a extream large zlevel 8934 // FIXME? 8935 if (!hoverLayer) { 8936 hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL); 8937 } 8938 8939 var scope = {}; 8940 hoverLayer.ctx.save(); 8941 for (var i = 0; i < len;) { 8942 var el = hoverElements[i]; 8943 var originalEl = el.__from; 8944 // Original el is removed 8945 // PENDING 8946 if (!(originalEl && originalEl.__zr)) { 8947 hoverElements.splice(i, 1); 8948 originalEl.__hoverMir = null; 8949 len--; 8950 continue; 8951 } 8952 i++; 8953 8954 // Use transform 8955 // FIXME style and shape ? 8956 if (!originalEl.invisible) { 8957 el.transform = originalEl.transform; 8958 el.invTransform = originalEl.invTransform; 8959 el.__clipPaths = originalEl.__clipPaths; 8960 // el. 8961 this._doPaintEl(el, hoverLayer, true, scope); 8962 } 8963 } 8964 hoverLayer.ctx.restore(); 8965 }, 8966 8967 getHoverLayer: function () { 8968 return this.getLayer(HOVER_LAYER_ZLEVEL); 8969 }, 8970 8971 _paintList: function (list, paintAll, redrawId) { 8972 if (this._redrawId !== redrawId) { 8973 return; 8974 } 8975 8976 paintAll = paintAll || false; 8977 8978 this._updateLayerStatus(list); 8979 8980 var finished = this._doPaintList(list, paintAll); 8981 8982 if (this._needsManuallyCompositing) { 8983 this._compositeManually(); 8984 } 8985 8986 if (!finished) { 8987 var self = this; 8988 requestAnimationFrame(function () { 8989 self._paintList(list, paintAll, redrawId); 8990 }); 8991 } 8992 }, 8993 8994 _compositeManually: function () { 8995 var ctx = this.getLayer(CANVAS_ZLEVEL).ctx; 8996 var width = this._domRoot.width; 8997 var height = this._domRoot.height; 8998 ctx.clearRect(0, 0, width, height); 8999 // PENDING, If only builtin layer? 9000 this.eachBuiltinLayer(function (layer) { 9001 if (layer.virtual) { 9002 ctx.drawImage(layer.dom, 0, 0, width, height); 9003 } 9004 }); 9005 }, 9006 9007 _doPaintList: function (list, paintAll) { 9008 var layerList = []; 9009 for (var zi = 0; zi < this._zlevelList.length; zi++) { 9010 var zlevel = this._zlevelList[zi]; 9011 var layer = this._layers[zlevel]; 9012 if (layer.__builtin__ 9013 && layer !== this._hoverlayer 9014 && (layer.__dirty || paintAll) 9015 ) { 9016 layerList.push(layer); 9017 } 9018 } 9019 9020 var finished = true; 9021 9022 for (var k = 0; k < layerList.length; k++) { 9023 var layer = layerList[k]; 9024 var ctx = layer.ctx; 9025 var scope = {}; 9026 ctx.save(); 9027 9028 var start = paintAll ? layer.__startIndex : layer.__drawIndex; 9029 9030 var useTimer = !paintAll && layer.incremental && Date.now; 9031 var startTime = useTimer && Date.now(); 9032 9033 var clearColor = layer.zlevel === this._zlevelList[0] 9034 ? this._backgroundColor : null; 9035 // All elements in this layer are cleared. 9036 if (layer.__startIndex === layer.__endIndex) { 9037 layer.clear(false, clearColor); 9038 } 9039 else if (start === layer.__startIndex) { 9040 var firstEl = list[start]; 9041 if (!firstEl.incremental || !firstEl.notClear || paintAll) { 9042 layer.clear(false, clearColor); 9043 } 9044 } 9045 if (start === -1) { 9046 console.error('For some unknown reason. drawIndex is -1'); 9047 start = layer.__startIndex; 9048 } 9049 for (var i = start; i < layer.__endIndex; i++) { 9050 var el = list[i]; 9051 this._doPaintEl(el, layer, paintAll, scope); 9052 el.__dirty = false; 9053 9054 if (useTimer) { 9055 // Date.now can be executed in 13,025,305 ops/second. 9056 var dTime = Date.now() - startTime; 9057 // Give 15 millisecond to draw. 9058 // The rest elements will be drawn in the next frame. 9059 if (dTime > 15) { 9060 break; 9061 } 9062 } 9063 } 9064 9065 layer.__drawIndex = i; 9066 9067 if (layer.__drawIndex < layer.__endIndex) { 9068 finished = false; 9069 } 9070 9071 if (scope.prevElClipPaths) { 9072 // Needs restore the state. If last drawn element is in the clipping area.
9073 ctx.restore(); 9074 } 9075 9076 ctx.restore(); 9077 } 9078 9079 if (env$1.wxa) { 9080 // Flush for weixin application 9081 each$1(this._layers, function (layer) { 9082 if (layer && layer.ctx && layer.ctx.draw) { 9083 layer.ctx.draw(); 9084 } 9085 }); 9086 } 9087 9088 return finished; 9089 }, 9090 9091 _doPaintEl: function (el, currentLayer, forcePaint, scope) { 9092 var ctx = currentLayer.ctx; 9093 var m = el.transform; 9094 if ( 9095 (currentLayer.__dirty || forcePaint) 9096 // Ignore invisible element 9097 && !el.invisible 9098 // Ignore transparent element 9099 && el.style.opacity !== 0 9100 // Ignore scale 0 element, in some environment like node-canvas 9101 // Draw a scale 0 element can cause all following draw wrong 9102 // And setTransform with scale 0 will cause set back transform failed. 9103 && !(m && !m[0] && !m[3]) 9104 // Ignore culled element 9105 && !(el.culling && isDisplayableCulled(el, this._width, this._height)) 9106 ) { 9107 9108 var clipPaths = el.__clipPaths; 9109 9110 // Optimize when clipping on group with several elements 9111 if (!scope.prevElClipPaths 9112 || isClipPathChanged(clipPaths, scope.prevElClipPaths) 9113 ) { 9114 // If has previous clipping state, restore from it 9115 if (scope.prevElClipPaths) { 9116 currentLayer.ctx.restore(); 9117 scope.prevElClipPaths = null; 9118 9119 // Reset prevEl since context has been restored 9120 scope.prevEl = null; 9121 } 9122 // New clipping state 9123 if (clipPaths) { 9124 ctx.save(); 9125 doClip(clipPaths, ctx); 9126 scope.prevElClipPaths = clipPaths; 9127 } 9128 } 9129 el.beforeBrush && el.beforeBrush(ctx); 9130 9131 el.brush(ctx, scope.prevEl || null); 9132 scope.prevEl = el; 9133 9134 el.afterBrush && el.afterBrush(ctx); 9135 } 9136 }, 9137 9138 /** 9139 * é¾å³°å½ zlevel éµâ¬é¦ã¥ç°éå±½î§éæ»ç¬çæ¨ºæ¹ªéæ¬ç´°éæ¶ç¼æ¶â¬æ¶î æé¨å«ç° 9140 * @param {number} zlevel 9141 * @param {boolean} virtual Virtual layer will not be inserted into dom. 9142 * @return {module:zrender/Layer} 9143 */ 9144 getLayer: function (zlevel, virtual) { 9145 if (this._singleCanvas && !this._needsManuallyCompositing) { 9146 zlevel = CANVAS_ZLEVEL; 9147 } 9148 var layer = this._layers[zlevel]; 9149 if (!layer) { 9150 // Create a new layer 9151 layer = new Layer('zr_' + zlevel, this, this.dpr); 9152 layer.zlevel = zlevel; 9153 layer.__builtin__ = true; 9154 9155 if (this._layerConfig[zlevel]) { 9156 merge(layer, this._layerConfig[zlevel], true); 9157 } 9158 9159 if (virtual) { 9160 layer.virtual = virtual; 9161 } 9162 9163 this.insertLayer(zlevel, layer); 9164 9165 // Context is created after dom inserted to document 9166 // Or excanvas will get 0px clientWidth and clientHeight 9167 layer.initContext(); 9168 } 9169 9170 return layer; 9171 }, 9172 9173 insertLayer: function (zlevel, layer) { 9174 9175 var layersMap = this._layers; 9176 var zlevelList = this._zlevelList; 9177 var len = zlevelList.length; 9178 var prevLayer = null; 9179 var i = -1; 9180 var domRoot = this._domRoot; 9181 9182 if (layersMap[zlevel]) { 9183 zrLog('ZLevel ' + zlevel + ' has been used already'); 9184 return; 9185 } 9186 // Check if is a valid layer 9187 if (!isLayerValid(layer)) {
9188 zrLog('Layer of zlevel ' + zlevel + ' is not valid'); 9189 return; 9190 } 9191 9192 if (len > 0 && zlevel > zlevelList[0]) { 9193 for (i = 0; i < len - 1; i++) { 9194 if ( 9195 zlevelList[i] < zlevel 9196 && zlevelList[i + 1] > zlevel 9197 ) { 9198 break; 9199 } 9200 } 9201 prevLayer = layersMap[zlevelList[i]]; 9202 } 9203 zlevelList.splice(i + 1, 0, zlevel); 9204 9205 layersMap[zlevel] = layer; 9206 9207 // Vitual layer will not directly show on the screen. 9208 // (It can be a WebGL layer and assigned to a ZImage element) 9209 // But it still under management of zrender. 9210 if (!layer.virtual) { 9211 if (prevLayer) { 9212 var prevDom = prevLayer.dom; 9213 if (prevDom.nextSibling) { 9214 domRoot.insertBefore( 9215 layer.dom, 9216 prevDom.nextSibling 9217 ); 9218 } 9219 else { 9220 domRoot.appendChild(layer.dom); 9221 } 9222 } 9223 else { 9224 if (domRoot.firstChild) { 9225 domRoot.insertBefore(layer.dom, domRoot.firstChild); 9226 } 9227 else { 9228 domRoot.appendChild(layer.dom); 9229 } 9230 } 9231 } 9232 }, 9233 9234 // Iterate each layer 9235 eachLayer: function (cb, context) { 9236 var zlevelList = this._zlevelList; 9237 var z; 9238 var i; 9239 for (i = 0; i < zlevelList.length; i++) { 9240 z = zlevelList[i]; 9241 cb.call(context, this._layers[z], z); 9242 } 9243 }, 9244 9245 // Iterate each buildin layer 9246 eachBuiltinLayer: function (cb, context) { 9247 var zlevelList = this._zlevelList; 9248 var layer; 9249 var z; 9250 var i; 9251 for (i = 0; i < zlevelList.length; i++) { 9252 z = zlevelList[i]; 9253 layer = this._layers[z]; 9254 if (layer.__builtin__) { 9255 cb.call(context, layer, z); 9256 } 9257 } 9258 }, 9259 9260 // Iterate each other layer except buildin layer 9261 eachOtherLayer: function (cb, context) { 9262 var zlevelList = this._zlevelList; 9263 var layer; 9264 var z; 9265 var i; 9266 for (i = 0; i < zlevelList.length; i++) { 9267 z = zlevelList[i]; 9268 layer = this._layers[z]; 9269 if (!layer.__builtin__) { 9270 cb.call(context, layer, z); 9271 } 9272 } 9273 }, 9274 9275 /** 9276 * é¾å³°å½éµâ¬éå¤å¡éæ¶ç¼é¨å«ç° 9277 * @param {Array.<module:zrender/Layer>} [prevLayer] 9278 */ 9279 getLayers: function () { 9280 return this._layers; 9281 }, 9282 9283 _updateLayerStatus: function (list) { 9284 9285 this.eachBuiltinLayer(function (layer, z) { 9286 layer.__dirty = layer.__used = false; 9287 }); 9288 9289 function updatePrevLayer(idx) { 9290 if (prevLayer) { 9291 if (prevLayer.__endIndex !== idx) { 9292 prevLayer.__dirty = true; 9293 } 9294 prevLayer.__endIndex = idx; 9295 } 9296 } 9297 9298 if (this._singleCanvas) { 9299 for (var i = 1; i < list.length; i++) { 9300 var el = list[i]; 9301 if (el.zlevel !== list[i - 1].zlevel || el.incremental) { 9302 this._needsManuallyCompositing = true; 9303 break; 9304 } 9305 } 9306 } 9307 9308 var prevLayer = null; 9309 var incrementalLayerCount = 0; 9310 for (var i = 0; i < list.length; i++) { 9311 var el = list[i]; 9312 var zlevel = el.zlevel; 9313 var layer; 9314 // PENDING If change one incremental element style ? 9315 // TODO Where there are non-incremental elements between incremental elements. 9316 if (el.incremental) { 9317 layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing); 9318 layer.incremental = true; 9319 incrementalLayerCount = 1; 9320 } 9321 else { 9322 layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing); 9323 } 9324 9325 if (!layer.__builtin__) {
9326 zrLog('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id); 9327 } 9328 9329 if (layer !== prevLayer) { 9330 layer.__used = true; 9331 if (layer.__startIndex !== i) { 9332 layer.__dirty = true; 9333 } 9334 layer.__startIndex = i; 9335 if (!layer.incremental) { 9336 layer.__drawIndex = i; 9337 } 9338 else { 9339 // Mark layer draw index needs to update. 9340 layer.__drawIndex = -1; 9341 } 9342 updatePrevLayer(i); 9343 prevLayer = layer; 9344 } 9345 if (el.__dirty) { 9346 layer.__dirty = true; 9347 if (layer.incremental && layer.__drawIndex < 0) { 9348 // Start draw from the first dirty element. 9349 layer.__drawIndex = i; 9350 } 9351 } 9352 } 9353 9354 updatePrevLayer(i); 9355 9356 this.eachBuiltinLayer(function (layer, z) { 9357 // Used in last frame but not in this frame. Needs clear 9358 if (!layer.__used && layer.getElementCount() > 0) { 9359 layer.__dirty = true; 9360 layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0; 9361 } 9362 // For incremental layer. In case start index changed and no elements are dirty. 9363 if (layer.__dirty && layer.__drawIndex < 0) { 9364 layer.__drawIndex = layer.__startIndex; 9365 } 9366 }); 9367 }, 9368 9369 /** 9370 * å¨å´æ«hoverçåî»éµâ¬éå¤å´ç¹ï¿½ 9371 */ 9372 clear: function () { 9373 this.eachBuiltinLayer(this._clearLayer); 9374 return this; 9375 }, 9376 9377 _clearLayer: function (layer) { 9378 layer.clear(); 9379 }, 9380 9381 setBackgroundColor: function (backgroundColor) { 9382 this._backgroundColor = backgroundColor; 9383 }, 9384 9385 /** 9386 * æ·î½æ¼é¸å§ç¾zlevelé¨å粯é跺弬é� 9387 * 9388 * @param {string} zlevel 9389 * @param {Object} config é°å¶ççµç¡ è 9390 * @param {string} [config.clearColor=0] å§£å¿î¼å¨å¯âé¢è¯²ç«·é¨å¯îé¹ï¿½ 9391 * @param {string} [config.motionBlur=false] éîæå¯®â¬éîå§©é¬ä½¹Ä绯� 9392 * @param {number} [config.lastFrameAlpha=0.7] 9393 * é¦ã¥ç´éîå§©é¬ä½¹Äç»¯å©æ®éè·ºâ¬æ¬å¨é¢îç´æ¶åºç¬æ¶â¬ç¯Ñè´©éå ¢æ®alphaéç¡·ç´éè°ç§ºæ¾¶Ñç¬æ©ç¡ 秺éåº¢æ¨ 9394 */ 9395 configLayer: function (zlevel, config) { 9396 if (config) { 9397 var layerConfig = this._layerConfig; 9398 if (!layerConfig[zlevel]) { 9399 layerConfig[zlevel] = config; 9400 } 9401 else { 9402 merge(layerConfig[zlevel], config, true); 9403 } 9404 9405 for (var i = 0; i < this._zlevelList.length; i++) { 9406 var _zlevel = this._zlevelList[i]; 9407 if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) { 9408 var layer = this._layers[_zlevel]; 9409 merge(layer, layerConfig[zlevel], true); 9410 } 9411 } 9412 } 9413 }, 9414 9415 /** 9416 * éç»æ«é¸å§ç¾ç� 9417 * @param {number} zlevel çåå¢é¦ã§æ®zlevel 9418 */ 9419 delLayer: function (zlevel) { 9420 var layers = this._layers; 9421 var zlevelList = this._zlevelList; 9422 var layer = layers[zlevel]; 9423 if (!layer) { 9424 return; 9425 } 9426 layer.dom.parentNode.removeChild(layer.dom); 9427 delete layers[zlevel]; 9428 9429 zlevelList.splice(indexOf(zlevelList, zlevel), 1); 9430 }, 9431 9432 /** 9433 * éåç æ¾¶Ñç¬é樺寲é庨å¸ç¼ï¿½ 9434 */ 9435 resize: function (width, height) { 9436 if (!this._domRoot.style) { // Maybe in node or worker 9437 if (width == null || height == null) { 9438 return; 9439 } 9440 this._width = width; 9441 this._height = height; 9442 9443 this.getLayer(CANVAS_ZLEVEL).resize(width, height); 9444 } 9445 else { 9446 var domRoot = this._domRoot; 9447 // FIXME Why ? 9448 domRoot.style.display = 'none'; 9449 9450 // Save input w/h 9451 var opts = this._opts; 9452 width != null && (opts.width = width); 9453 height != null && (opts.height = height); 9454 9455 width = this._getSize(0); 9456 height = this._getSize(1); 9457 9458 domRoot.style.display = ''; 9459 9460 // 浼樺寲å¨âæ¹ç¹çºæª¯éç°å½é¨å esize 9461 if (this._width != width || height != this._height) { 9462 domRoot.style.width = width + 'px'; 9463 domRoot.style.height = height + 'px'; 9464 9465 for (var id in this._layers) { 9466 if (this._layers.hasOwnProperty(id)) { 9467 this._layers[id].resize(width, height); 9468 } 9469 } 9470 each$1(this._progressiveLayers, function (layer) { 9471 layer.resize(width, height); 9472 }); 9473 9474 this.refresh(true); 9475 } 9476 9477 this._width = width; 9478 this._height = height; 9479 9480 } 9481 return this; 9482 }, 9483 9484 /**
9485 * å¨å´æ«éæ å«é¨åªç«´æ¶îç° 9486 * @param {number} zlevel 9487 */ 9488 clearLayer: function (zlevel) { 9489 var layer = this._layers[zlevel]; 9490 if (layer) { 9491 layer.clear(); 9492 } 9493 }, 9494 9495 /** 9496 * é²å©æ 9497 */ 9498 dispose: function () { 9499 this.root.innerHTML = ''; 9500 9501 this.root = 9502 this.storage = 9503 9504 this._domRoot = 9505 this._layers = null; 9506 }, 9507 9508 /** 9509 * Get canvas which has all thing rendered 9510 * @param {Object} opts 9511 * @param {string} [opts.backgroundColor] 9512 * @param {number} [opts.pixelRatio] 9513 */ 9514 getRenderedCanvas: function (opts) { 9515 opts = opts || {}; 9516 if (this._singleCanvas && !this._compositeManually) { 9517 return this._layers[CANVAS_ZLEVEL].dom; 9518 } 9519 9520 var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); 9521 imageLayer.initContext(); 9522 imageLayer.clear(false, opts.backgroundColor || this._backgroundColor); 9523 9524 if (opts.pixelRatio <= this.dpr) { 9525 this.refresh(); 9526 9527 var width = imageLayer.dom.width; 9528 var height = imageLayer.dom.height; 9529 var ctx = imageLayer.ctx; 9530 this.eachLayer(function (layer) { 9531 if (layer.__builtin__) { 9532 ctx.drawImage(layer.dom, 0, 0, width, height); 9533 } 9534 else if (layer.renderToCanvas) { 9535 imageLayer.ctx.save(); 9536 layer.renderToCanvas(imageLayer.ctx); 9537 imageLayer.ctx.restore(); 9538 } 9539 }); 9540 } 9541 else { 9542 // PENDING, echarts-gl and incremental rendering. 9543 var scope = {}; 9544 var displayList = this.storage.getDisplayList(true); 9545 for (var i = 0; i < displayList.length; i++) { 9546 var el = displayList[i]; 9547 this._doPaintEl(el, imageLayer, true, scope); 9548 } 9549 } 9550 9551 return imageLayer.dom; 9552 }, 9553 /** 9554 * é¾å³°å½ç¼æ¨ºæµéåç ç¹è害 9555 */ 9556 getWidth: function () { 9557 return this._width; 9558 }, 9559 9560 /** 9561 * é¾å³°å½ç¼æ¨ºæµéåç æ¥æ¨ºå®³ 9562 */ 9563 getHeight: function () { 9564 return this._height; 9565 }, 9566 9567 _getSize: function (whIdx) { 9568 var opts = this._opts; 9569 var wh = ['width', 'height'][whIdx]; 9570 var cwh = ['clientWidth', 'clientHeight'][whIdx]; 9571 var plt = ['paddingLeft', 'paddingTop'][whIdx]; 9572 var prb = ['paddingRight', 'paddingBottom'][whIdx]; 9573 9574 if (opts[wh] != null && opts[wh] !== 'auto') { 9575 return parseFloat(opts[wh]); 9576 } 9577 9578 var root = this.root; 9579 // IE8 does not support getComputedStyle, but it use VML. 9580 var stl = document.defaultView.getComputedStyle(root); 9581 9582 return ( 9583 (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) 9584 - (parseInt10(stl[plt]) || 0) 9585 - (parseInt10(stl[prb]) || 0) 9586 ) | 0; 9587 }, 9588 9589 pathToImage: function (path, dpr) { 9590 dpr = dpr || this.dpr; 9591 9592 var canvas = document.createElement('canvas'); 9593 var ctx = canvas.getContext('2d'); 9594 var rect = path.getBoundingRect(); 9595 var style = path.style; 9596 var shadowBlurSize = style.shadowBlur * dpr; 9597 var shadowOffsetX = style.shadowOffsetX * dpr; 9598 var shadowOffsetY = style.shadowOffsetY * dpr; 9599 var lineWidth = style.hasStroke() ? style.lineWidth : 0; 9600 9601 var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize); 9602 var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize); 9603 var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize); 9604 var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize); 9605 var width = rect.width + leftMargin + rightMargin; 9606 var height = rect.height + topMargin + bottomMargin; 9607 9608 canvas.width = width * dpr; 9609 canvas.height = height * dpr; 9610 9611 ctx.scale(dpr, dpr); 9612 ctx.clearRect(0, 0, width, height); 9613 ctx.dpr = dpr; 9614 9615 var pathTransform = { 9616 position: path.position, 9617 rotation: path.rotation, 9618 scale: path.scale 9619 }; 9620 path.position = [leftMargin - rect.x, topMargin - rect.y]; 9621 path.rotation = 0; 9622 path.scale = [1, 1]; 9623 path.updateTransform(); 9624 if (path) { 9625 path.brush(ctx); 9626 } 9627 9628 var ImageShape = ZImage; 9629 var imgShape = new ImageShape({ 9630 style: { 9631 x: 0, 9632 y: 0, 9633 image: canvas 9634 } 9635 }); 9636 9637 if (pathTransform.position != null) { 9638 imgShape.position = path.position = pathTransform.position; 9639 } 9640 9641 if (pathTransform.rotation != null) { 9642 imgShape.rotation = path.rotation = pathTransform.rotation; 9643 } 9644 9645 if (pathTransform.scale != null) { 9646 imgShape.scale = path.scale = pathTransform.scale; 9647 } 9648 9649 return imgShape; 9650 } 9651 }; 9652 9653 /** 9654 * æµå¬©æ¬¢æå姪绫� 9655 * @module zrender/core/event 9656 * @author Kener (@Kener-鿥å², [email protected]) 9657 */ 9658 9659 var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener; 9660 9661 var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; 9662 9663 function getBoundingClientRect(el) { 9664 // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect 9665 return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0}; 9666 } 9667 9668// `calculate` is optional, default false 9669 function clientToLocal(el, e, out, calculate) { 9670 out = out || {}; 9671 9672 // According to the W3C Working Draft, offsetX and offsetY should be relative 9673 // to the padding edge of the target element. The only browser using this convention 9674 // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does 9675 // not support the properties. 9676 // (see http://www.jacklmoore.com/notes/mouse-position/) 9677 // In zr painter.dom, padding edge equals to border edge. 9678 9679 // FIXME 9680 // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and 9681 // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y 9682 // is too complex. So css-transfrom dont support in this case temporarily. 9683 if (calculate || !env$1.canvasSupported) { 9684 defaultGetZrXY(el, e, out); 9685 } 9686 // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned 9687 // ancestor element, so we should make sure el is positioned (e.g., not position:static). 9688 // BTW1, Webkit don't return the same results as FF in non-simple cases (like add 9689 // zoom-factor, overflow / opacity layers, transforms ...) 9690 // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d. 9691 // <https://bugs.jquery.com/ticket/8523#comment:14> 9692 // BTW3, In ff, offsetX/offsetY is always 0. 9693 else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) { 9694 out.zrX = e.layerX; 9695 out.zrY = e.layerY; 9696 } 9697 // For IE6+, chrome, safari, opera. (When will ff support offsetX?) 9698 else if (e.offsetX != null) { 9699 out.zrX = e.offsetX; 9700 out.zrY = e.offsetY; 9701 } 9702 // For some other device, e.g., IOS safari. 9703 else { 9704 defaultGetZrXY(el, e, out); 9705 } 9706 9707 return out; 9708 } 9709 9710 function defaultGetZrXY(el, e, out) { 9711 // This well-known method below does not support css transform. 9712 var box = getBoundingClientRect(el); 9713 out.zrX = e.clientX - box.left; 9714 out.zrY = e.clientY - box.top; 9715 } 9716 9717 /** 9718 * æ¿¡åççæ¨ºæ¹ªç»î¿ç¬éç°ç¥µéã§æ®æ¶â¬æµæ²omçï¹å½é¨åªç°¨æµ 讹ç´é´æ¢ouchæµå¬©æ¬¢é岿¸¶çä½½æµé¹îç«´æ¶å¬©ç°¨æµ è·ºæ½é�. 9719 * `calculate` is optional, default false. 9720 */ 9721 function normalizeEvent(el, e, calculate) { 9722 9723 e = e || window.event; 9724 9725 if (e.zrX != null) { 9726 return e; 9727 } 9728 9729 var eventType = e.type; 9730 var isTouch = eventType && eventType.indexOf('touch') >= 0; 9731 9732 if (!isTouch) { 9733 clientToLocal(el, e, e, calculate); 9734 e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3; 9735 } 9736 else { 9737 var touch = eventType != 'touchend' 9738 ? e.targetTouches[0] 9739 : e.changedTouches[0]; 9740 touch && clientToLocal(el, touch, e, calculate); 9741 } 9742 9743 // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0; 9744 // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
9745 // If e.which has been defined, if may be readonly, 9746 // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which 9747 var button = e.button; 9748 if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) { 9749 e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); 9750 } 9751 9752 return e; 9753 } 9754 9755 /** 9756 * @param {HTMLElement} el 9757 * @param {string} name 9758 * @param {Function} handler 9759 */ 9760 function addEventListener(el, name, handler) { 9761 if (isDomLevel2) { 9762 // Reproduct the console warning: 9763 // [Violation] Added non-passive event listener to a scroll-blocking <some> event. 9764 // Consider marking event handler as 'passive' to make the page more responsive. 9765 // Just set console log level: verbose in chrome dev tool. 9766 // then the warning log will be printed when addEventListener called. 9767 // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md 9768 // We have not yet found a neat way to using passive. Because in zrender the dom event 9769 // listener delegate all of the upper events of element. Some of those events need 9770 // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts. 9771 // Before passive can be adopted, these issues should be considered: 9772 // (1) Whether and how a zrender user specifies an event listener passive. And by default, 9773 // passive or not. 9774 // (2) How to tread that some zrender event listener is passive, and some is not. If 9775 // we use other way but not preventDefault of mousewheel and touchmove, browser 9776 // compatibility should be handled. 9777 9778 // var opts = (env.passiveSupported && name === 'mousewheel') 9779 // ? {passive: true} 9780 // // By default, the third param of el.addEventListener is `capture: false`. 9781 // : void 0; 9782 // el.addEventListener(name, handler /* , opts */); 9783 el.addEventListener(name, handler); 9784 } 9785 else { 9786 el.attachEvent('on' + name, handler); 9787 } 9788 } 9789 9790 function removeEventListener(el, name, handler) { 9791 if (isDomLevel2) { 9792 el.removeEventListener(name, handler); 9793 } 9794 else { 9795 el.detachEvent('on' + name, handler); 9796 } 9797 } 9798 9799 /** 9800 * preventDefault and stopPropagation. 9801 * Notice: do not do that in zrender. Upper application 9802 * do that if necessary. 9803 * 9804 * @memberOf module:zrender/core/event 9805 * @method 9806 * @param {Event} e : eventçµç¡ è 9807 */ 9808 var stop = isDomLevel2 9809 ? function (e) { 9810 e.preventDefault(); 9811 e.stopPropagation(); 9812 e.cancelBubble = true; 9813 } 9814 : function (e) { 9815 e.returnValue = false; 9816 e.cancelBubble = true; 9817 }; 9818 9819 function notLeftMouse(e) { 9820 // If e.which is undefined, considered as left mouse event. 9821 return e.which > 1; 9822 } 9823 9824 /** 9825 * éã§æ¾æ¶è¤è¢«, çå¨å®³éå²î ¸éåå¢éå¤å§©é¢ç»å¸¶éè·ºæ« 9826 * 9827 * @module zrender/animation/Animation 9828 * @author pissang(https://github.com/pissang) 9829 */ 9830// TODO Additive animation 9831// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/ 9832// https://developer.apple.com/videos/wwdc2014/#236 9833 9834 /** 9835 * @typedef {Object} IZRenderStage 9836 * @property {Function} update 9837 */ 9838 9839 /** 9840 * @alias module:zrender/animation/Animation 9841 * @constructor 9842 * @param {Object} [options] 9843 * @param {Function} [options.onframe] 9844 * @param {IZRenderStage} [options.stage] 9845 * @example 9846 * var animation = new Animation(); 9847 * var obj = { 9848 * x: 100, 9849 * y: 100 9850 * }; 9851 * animation.animate(node.position) 9852 * .when(1000, { 9853 * x: 500, 9854 * y: 500 9855 * }) 9856 * .when(2000, { 9857 * x: 100, 9858 * y: 100 9859 * }) 9860 * .start('spline'); 9861 */ 9862 var Animation = function (options) { 9863 9864 options = options || {}; 9865 9866 this.stage = options.stage || {}; 9867 9868 this.onframe = options.onframe || function() {}; 9869 9870 // private properties 9871 this._clips = []; 9872 9873 this._running = false; 9874 9875 this._time; 9876 9877 this._pausedTime; 9878 9879 this._pauseStart; 9880 9881 this._paused = false; 9882 9883 Eventful.call(this); 9884 }; 9885 9886 Animation.prototype = { 9887 9888 constructor: Animation, 9889 /** 9890 * å¨£è¯²å§ clip 9891 * @param {module:zrender/animation/Clip} clip 9892 */ 9893 addClip: function (clip) { 9894 this._clips.push(clip); 9895 }, 9896 /** 9897 * å¨£è¯²å§ animator 9898 * @param {module:zrender/animation/Animator} animator 9899 */ 9900 addAnimator: function (animator) { 9901 animator.animation = this; 9902 var clips = animator.getClips(); 9903 for (var i = 0; i < clips.length; i++) { 9904 this.addClip(clips[i]); 9905 } 9906 }, 9907 /** 9908 * éç»æ«éã§æ¾éå¨î 9909 * @param {module:zrender/animation/Clip} clip 9910 */ 9911 removeClip: function(clip) { 9912 var idx = indexOf(this._clips, clip); 9913 if (idx >= 0) { 9914 this._clips.splice(idx, 1); 9915 } 9916 }, 9917 9918 /** 9919 * éç»æ«éã§æ¾éå¨î 9920 * @param {module:zrender/animation/Animator} animator 9921 */ 9922 removeAnimator: function (animator) { 9923 var clips = animator.getClips(); 9924 for (var i = 0; i < clips.length; i++) { 9925 this.removeClip(clips[i]); 9926 } 9927 animator.animation = null; 9928 }, 9929 9930 _update: function() { 9931 var time = new Date().getTime() - this._pausedTime; 9932 var delta = time - this._time; 9933 var clips = this._clips; 9934 var len = clips.length; 9935 9936 var deferredEvents = []; 9937 var deferredClips = []; 9938 for (var i = 0; i < len; i++) { 9939 var clip = clips[i]; 9940 var e = clip.step(time, delta); 9941 // Throw out the events need to be called after 9942 // stage.update, like destroy 9943 if (e) { 9944 deferredEvents.push(e); 9945 deferredClips.push(clip); 9946 } 9947 } 9948 9949 // Remove the finished clip 9950 for (var i = 0; i < len;) { 9951 if (clips[i]._needsRemove) { 9952 clips[i] = clips[len - 1]; 9953 clips.pop(); 9954 len--; 9955 } 9956 else { 9957 i++; 9958 } 9959 } 9960 9961 len = deferredEvents.length; 9962 for (var i = 0; i < len; i++) { 9963 deferredClips[i].fire(deferredEvents[i]); 9964 } 9965 9966 this._time = time; 9967 9968 this.onframe(delta); 9969 9970 // 'frame' should be triggered before stage, because upper application 9971 // depends on the sequence (e.g., echarts-stream and finish 9972 // event judge) 9973 this.trigger('frame', delta); 9974 9975 if (this.stage.update) {
9976 this.stage.update(); 9977 } 9978 }, 9979 9980 _startLoop: function () { 9981 var self = this; 9982 9983 this._running = true; 9984 9985 function step() { 9986 if (self._running) { 9987 9988 requestAnimationFrame(step); 9989 9990 !self._paused && self._update(); 9991 } 9992 } 9993 9994 requestAnimationFrame(step); 9995 }, 9996 9997 /** 9998 * Start animation. 9999 */ 10000 start: function () { 10001 10002 this._time = new Date().getTime(); 10003 this._pausedTime = 0; 10004 10005 this._startLoop(); 10006 }, 10007 10008 /** 10009 * Stop animation. 10010 */ 10011 stop: function () { 10012 this._running = false; 10013 }, 10014 10015 /** 10016 * Pause animation. 10017 */ 10018 pause: function () { 10019 if (!this._paused) { 10020 this._pauseStart = new Date().getTime(); 10021 this._paused = true; 10022 } 10023 }, 10024 10025 /** 10026 * Resume animation. 10027 */ 10028 resume: function () { 10029 if (this._paused) { 10030 this._pausedTime += (new Date().getTime()) - this._pauseStart; 10031 this._paused = false; 10032 } 10033 }, 10034 10035 /** 10036 * Clear animation. 10037 */ 10038 clear: function () { 10039 this._clips = []; 10040 }, 10041 10042 /** 10043 * Whether animation finished. 10044 */ 10045 isFinished: function () { 10046 return !this._clips.length; 10047 }, 10048 10049 /** 10050 * Creat animator for a target, whose props can be animated. 10051 * 10052 * @param {Object} target 10053 * @param {Object} options 10054 * @param {boolean} [options.loop=false] Whether loop animation. 10055 * @param {Function} [options.getter=null] Get value from target. 10056 * @param {Function} [options.setter=null] Set value to target. 10057 * @return {module:zrender/animation/Animation~Animator} 10058 */ 10059 // TODO Gap 10060 animate: function (target, options) { 10061 options = options || {}; 10062 10063 var animator = new Animator( 10064 target, 10065 options.loop, 10066 options.getter, 10067 options.setter 10068 ); 10069 10070 this.addAnimator(animator); 10071 10072 return animator; 10073 } 10074 }; 10075 10076 mixin(Animation, Eventful); 10077 10078 /** 10079 * Only implements needed gestures for mobile. 10080 */ 10081 10082 var GestureMgr = function () { 10083 10084 /** 10085 * @private 10086 * @type {Array.<Object>} 10087 */ 10088 this._track = []; 10089 }; 10090 10091 GestureMgr.prototype = { 10092 10093 constructor: GestureMgr, 10094 10095 recognize: function (event, target, root) { 10096 this._doTrack(event, target, root); 10097 return this._recognize(event); 10098 }, 10099 10100 clear: function () { 10101 this._track.length = 0; 10102 return this; 10103 }, 10104 10105 _doTrack: function (event, target, root) { 10106 var touches = event.touches; 10107 10108 if (!touches) { 10109 return; 10110 } 10111 10112 var trackItem = { 10113 points: [], 10114 touches: [], 10115 target: target, 10116 event: event 10117 }; 10118 10119 for (var i = 0, len = touches.length; i < len; i++) { 10120 var touch = touches[i]; 10121 var pos = clientToLocal(root, touch, {}); 10122 trackItem.points.push([pos.zrX, pos.zrY]); 10123 trackItem.touches.push(touch); 10124 } 10125 10126 this._track.push(trackItem); 10127 }, 10128 10129 _recognize: function (event) { 10130 for (var eventName in recognizers) { 10131 if (recognizers.hasOwnProperty(eventName)) { 10132 var gestureInfo = recognizers[eventName](this._track, event); 10133 if (gestureInfo) { 10134 return gestureInfo; 10135 } 10136 } 10137 } 10138 } 10139 }; 10140 10141 function dist$1(pointPair) { 10142 var dx = pointPair[1][0] - pointPair[0][0]; 10143 var dy = pointPair[1][1] - pointPair[0][1]; 10144 10145 return Math.sqrt(dx * dx + dy * dy); 10146 } 10147 10148 function center(pointPair) { 10149 return [ 10150 (pointPair[0][0] + pointPair[1][0]) / 2, 10151 (pointPair[0][1] + pointPair[1][1]) / 2 10152 ]; 10153 } 10154 10155 var recognizers = { 10156 10157 pinch: function (track, event) { 10158 var trackLen = track.length; 10159 10160 if (!trackLen) { 10161 return; 10162 } 10163 10164 var pinchEnd = (track[trackLen - 1] || {}).points; 10165 var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd; 10166 10167 if (pinchPre 10168 && pinchPre.length > 1 10169 && pinchEnd 10170 && pinchEnd.length > 1 10171 ) { 10172 var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre); 10173 !isFinite(pinchScale) && (pinchScale = 1); 10174 10175 event.pinchScale = pinchScale; 10176 10177 var pinchCenter = center(pinchEnd); 10178 event.pinchX = pinchCenter[0]; 10179 event.pinchY = pinchCenter[1]; 10180 10181 return { 10182 type: 'pinch', 10183 target: track[0].target, 10184 event: event 10185 }; 10186 } 10187 } 10188 10189 // Only pinch currently. 10190 }; 10191 10192 var TOUCH_CLICK_DELAY = 300; 10193 10194 var mouseHandlerNames = [ 10195 'click', 'dblclick', 'mousewheel', 'mouseout', 10196 'mouseup', 'mousedown', 'mousemove', 'contextmenu' 10197 ]; 10198 10199 var touchHandlerNames = [ 10200 'touchstart', 'touchend', 'touchmove' 10201 ]; 10202 10203 var pointerEventNames = { 10204 pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1 10205 }; 10206 10207 var pointerHandlerNames = map(mouseHandlerNames, function (name) { 10208 var nm = name.replace('mouse', 'pointer'); 10209 return pointerEventNames[nm] ? nm : name; 10210 });
10211 10212 function eventNameFix(name) { 10213 return (name === 'mousewheel' && env$1.browser.firefox) ? 'DOMMouseScroll' : name; 10214 } 10215 10216 function processGesture(proxy, event, stage) { 10217 var gestureMgr = proxy._gestureMgr; 10218 10219 stage === 'start' && gestureMgr.clear(); 10220 10221 var gestureInfo = gestureMgr.recognize( 10222 event, 10223 proxy.handler.findHover(event.zrX, event.zrY, null).target, 10224 proxy.dom 10225 ); 10226 10227 stage === 'end' && gestureMgr.clear(); 10228 10229 // Do not do any preventDefault here. Upper application do that if necessary. 10230 if (gestureInfo) { 10231 var type = gestureInfo.type; 10232 event.gestureEvent = type; 10233 10234 proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event); 10235 } 10236 } 10237 10238// function onMSGestureChange(proxy, event) { 10239// if (event.translationX || event.translationY) { 10240// // mousemove is carried by MSGesture to reduce the sensitivity. 10241// proxy.handler.dispatchToElement(event.target, 'mousemove', event); 10242// } 10243// if (event.scale !== 1) { 10244// event.pinchX = event.offsetX; 10245// event.pinchY = event.offsetY; 10246// event.pinchScale = event.scale; 10247// proxy.handler.dispatchToElement(event.target, 'pinch', event); 10248// } 10249// } 10250 10251 /** 10252 * Prevent mouse event from being dispatched after Touch Events action 10253 * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js> 10254 * 1. Mobile browsers dispatch mouse events 300ms after touchend. 10255 * 2. Chrome for Android dispatch mousedown for long-touch about 650ms 10256 * Result: Blocking Mouse Events for 700ms. 10257 */ 10258 function setTouchTimer(instance) { 10259 instance._touching = true; 10260 clearTimeout(instance._touchTimer); 10261 instance._touchTimer = setTimeout(function () { 10262 instance._touching = false; 10263 }, 700); 10264 } 10265 10266 10267 var domHandlers = { 10268 /** 10269 * Mouse move handler 10270 * @inner 10271 * @param {Event} event 10272 */ 10273 mousemove: function (event) { 10274 event = normalizeEvent(this.dom, event); 10275 10276 this.trigger('mousemove', event); 10277 }, 10278 10279 /** 10280 * Mouse out handler 10281 * @inner 10282 * @param {Event} event 10283 */ 10284 mouseout: function (event) { 10285 event = normalizeEvent(this.dom, event); 10286 10287 var element = event.toElement || event.relatedTarget; 10288 if (element != this.dom) { 10289 while (element && element.nodeType != 9) { 10290 // è¹çæéåæé¦â®ootæ¶î æ®domå¯®æ¡æ£é¨å´ouseOut 10291 if (element === this.dom) { 10292 return; 10293 } 10294 10295 element = element.parentNode; 10296 } 10297 } 10298 10299 this.trigger('mouseout', event); 10300 }, 10301 10302 /** 10303 * Touchå¯®â¬æ¿®å¬ªæ·æ´æ¿å±é� 10304 * @inner 10305 * @param {Event} event 10306 */ 10307 touchstart: function (event) { 10308 // Default mouse behaviour should not be disabled here. 10309 // For example, page may needs to be slided. 10310 event = normalizeEvent(this.dom, event); 10311 10312 // Mark touch, which is useful in distinguish touch and 10313 // mouse event in upper applicatoin. 10314 event.zrByTouch = true; 10315 10316 this._lastTouchMoment = new Date(); 10317 10318 processGesture(this, event, 'start'); 10319 10320 // In touch device, trigger `mousemove`(`mouseover`) should 10321 // be triggered, and must before `mousedown` triggered. 10322 domHandlers.mousemove.call(this, event); 10323 10324 domHandlers.mousedown.call(this, event); 10325 10326 setTouchTimer(this); 10327 }, 10328 10329 /** 10330 * Touchç»è¯²å§©éå¶ ç°²éè¥æ 10331 * @inner 10332 * @param {Event} event 10333 */ 10334 touchmove: function (event) { 10335 10336 event = normalizeEvent(this.dom, event); 10337 10338 // Mark touch, which is useful in distinguish touch and 10339 // mouse event in upper applicatoin. 10340 event.zrByTouch = true; 10341 10342 processGesture(this, event, 'change'); 10343 10344 // Mouse move should always be triggered no matter whether
10345 // there is gestrue event, because mouse move and pinch may 10346 // be used at the same time. 10347 domHandlers.mousemove.call(this, event); 10348 10349 setTouchTimer(this); 10350 }, 10351 10352 /** 10353 * Touchç¼æ´æ½«éå¶ ç°²éè¥æ 10354 * @inner 10355 * @param {Event} event 10356 */ 10357 touchend: function (event) { 10358 10359 event = normalizeEvent(this.dom, event); 10360 10361 // Mark touch, which is useful in distinguish touch and 10362 // mouse event in upper applicatoin. 10363 event.zrByTouch = true; 10364 10365 processGesture(this, event, 'end'); 10366 10367 domHandlers.mouseup.call(this, event); 10368 10369 // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is 10370 // triggered in `touchstart`. This seems to be illogical, but by this mechanism, 10371 // we can conveniently implement "hover style" in both PC and touch device just 10372 // by listening to `mouseover` to add "hover style" and listening to `mouseout` 10373 // to remove "hover style" on an element, without any additional code for 10374 // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover 10375 // style" will remain for user view) 10376 10377 // click event should always be triggered no matter whether 10378 // there is gestrue event. System click can not be prevented. 10379 if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) { 10380 domHandlers.click.call(this, event); 10381 } 10382 10383 setTouchTimer(this); 10384 }, 10385 10386 pointerdown: function (event) { 10387 domHandlers.mousedown.call(this, event); 10388 10389 // if (useMSGuesture(this, event)) { 10390 // this._msGesture.addPointer(event.pointerId); 10391 // } 10392 }, 10393 10394 pointermove: function (event) { 10395 // FIXME 10396 // pointermove is so sensitive that it always triggered when 10397 // tap(click) on touch screen, which affect some judgement in 10398 // upper application. So, we dont support mousemove on MS touch 10399 // device yet. 10400 if (!isPointerFromTouch(event)) { 10401 domHandlers.mousemove.call(this, event); 10402 } 10403 }, 10404 10405 pointerup: function (event) { 10406 domHandlers.mouseup.call(this, event); 10407 }, 10408 10409 pointerout: function (event) { 10410 // pointerout will be triggered when tap on touch screen 10411 // (IE11+/Edge on MS Surface) after click event triggered, 10412 // which is inconsistent with the mousout behavior we defined 10413 // in touchend. So we unify them. 10414 // (check domHandlers.touchend for detailed explanation) 10415 if (!isPointerFromTouch(event)) { 10416 domHandlers.mouseout.call(this, event); 10417 } 10418 } 10419 }; 10420 10421 function isPointerFromTouch(event) { 10422 var pointerType = event.pointerType; 10423 return pointerType === 'pen' || pointerType === 'touch'; 10424 } 10425 10426// function useMSGuesture(handlerProxy, event) { 10427// return isPointerFromTouch(event) && !!handlerProxy._msGesture; 10428// } 10429 10430// Common handlers 10431 each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { 10432 domHandlers[name] = function (event) { 10433 event = normalizeEvent(this.dom, event); 10434 this.trigger(name, event); 10435 }; 10436 }); 10437 10438 /** 10439 * æ¶ç帶éå被ç¹ç°ç·¥éæ¿îéæom æµå¬©æ¬¢æ¾¶åæéè¥æ 10440 * 10441 * @inner 10442 * @param {module:zrender/Handler} instance éºÑåç»«è¯²çæ¸ï¿½ 10443 */ 10444 function initDomHandler(instance) { 10445 each$1(touchHandlerNames, function (name) { 10446 instance._handlers[name] = bind(domHandlers[name], instance); 10447 }); 10448 10449 each$1(pointerHandlerNames, function (name) { 10450 instance._handlers[name] = bind(domHandlers[name], instance); 10451 }); 10452 10453 each$1(mouseHandlerNames, function (name) { 10454 instance._handlers[name] = makeMouseHandler(domHandlers[name], instance); 10455 }); 10456 10457 function makeMouseHandler(fn, instance) { 10458 return function () { 10459 if (instance._touching) { 10460 return; 10461 } 10462 return fn.apply(instance, arguments); 10463 }; 10464 } 10465 } 10466 10467 10468 function HandlerDomProxy(dom) { 10469 Eventful.call(this); 10470 10471 this.dom = dom; 10472 10473 /** 10474 * @private 10475 * @type {boolean} 10476 */ 10477 this._touching = false; 10478 10479 /** 10480 * @private 10481 * @type {number} 10482 */ 10483 this._touchTimer; 10484 10485 /** 10486 * @private 10487 * @type {module:zrender/core/GestureMgr} 10488 */ 10489 this._gestureMgr = new GestureMgr(); 10490 10491 this._handlers = {}; 10492 10493 initDomHandler(this); 10494 10495 if (env$1.pointerEventsSupported) { // Only IE11+/Edge 10496 // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240), 10497 // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event 10498 // at the same time. 10499 // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on 10500 // screen, which do not occurs in pointer event. 10501 // So we use pointer event to both detect touch gesture and mouse behavior. 10502 mountHandlers(pointerHandlerNames, this); 10503 10504 // FIXME 10505 // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable, 10506 // which does not prevent defuault behavior occasionally (which may cause view port 10507 // zoomed in but use can not zoom it back). And event.preventDefault() does not work. 10508 // So we have to not to use MSGesture and not to supp
10508ort touchmove and pinch on MS 10509 // touch screen. And we only support click behavior on MS touch screen now. 10510 10511 // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+. 10512 // We dont support touch on IE on win7. 10513 // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx> 10514 // if (typeof MSGesture === 'function') { 10515 // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line 10516 // dom.addEventListener('MSGestureChange', onMSGestureChange); 10517 // } 10518 } 10519 else { 10520 if (env$1.touchEventsSupported) { 10521 mountHandlers(touchHandlerNames, this); 10522 // Handler of 'mouseout' event is needed in touch mode, which will be mounted below. 10523 // addEventListener(root, 'mouseout', this._mouseoutHandler); 10524 } 10525 10526 // 1. Considering some devices that both enable touch and mouse event (like on MS Surface 10527 // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise 10528 // mouse event can not be handle in those devices. 10529 // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent 10530 // mouseevent after touch event triggered, see `setTouchTimer`. 10531 mountHandlers(mouseHandlerNames, this); 10532 } 10533 10534 function mountHandlers(handlerNames, instance) { 10535 each$1(handlerNames, function (name) { 10536 addEventListener(dom, eventNameFix(name), instance._handlers[name]); 10537 }, instance); 10538 } 10539 } 10540 10541 var handlerDomProxyProto = HandlerDomProxy.prototype; 10542 handlerDomProxyProto.dispose = function () { 10543 var handlerNames = mouseHandlerNames.concat(touchHandlerNames); 10544 10545 for (var i = 0; i < handlerNames.length; i++) { 10546 var name = handlerNames[i]; 10547 removeEventListener(this.dom, eventNameFix(name), this._handlers[name]); 10548 } 10549 }; 10550 10551 handlerDomProxyProto.setCursor = function (cursorStyle) { 10552 this.dom.style && (this.dom.style.cursor = cursorStyle || 'default'); 10553 }; 10554 10555 mixin(HandlerDomProxy, Eventful); 10556 10557 /*! 10558* ZRender, a high performance 2d drawing library. 10559* 10560* Copyright (c) 2013, Baidu Inc. 10561* All rights reserved. 10562* 10563* LICENSE 10564* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt 10565*/ 10566 10567 var useVML = !env$1.canvasSupported; 10568 10569 var painterCtors = { 10570 canvas: Painter 10571 }; 10572 10573 var instances$1 = {}; // ZRenderç¹ç°ç·¥mapç»±ã ç´© 10574 10575 /** 10576 * @type {string} 10577 */ 10578 var version$1 = '4.0.4'; 10579 10580 /** 10581 * Initializing a zrender instance 10582 * @param {HTMLElement} dom 10583 * @param {Object} opts 10584 * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' 10585 * @param {number} [opts.devicePixelRatio] 10586 * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined) 10587 * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined) 10588 * @return {module:zrender/ZRender} 10589 */ 10590 function init$1(dom, opts) { 10591 var zr = new ZRender(guid(), dom, opts); 10592 instances$1[zr.id] = zr; 10593 return zr; 10594 } 10595 10596 /** 10597 * Dispose zrender instance 10598 * @param {module:zrender/ZRender} zr 10599 */ 10600 function dispose$1(zr) { 10601 if (zr) { 10602 zr.dispose(); 10603 } 10604 else { 10605 for (var key in instances$1) { 10606 if (instances$1.hasOwnProperty(key)) { 10607 instances$1[key].dispose(); 10608 } 10609 } 10610 instances$1 = {}; 10611 } 10612 10613 return this; 10614 } 10615 10616 /** 10617 * Get zrender instance by id 10618 * @param {string} id zrender instance id 10619 * @return {module:zrender/ZRender} 10620 */ 10621 function getInstance(id) { 10622 return instances$1[id]; 10623 } 10624 10625 function registerPainter(name, Ctor) { 10626 painterCtors[name] = Ctor; 10627 } 10628 10629 function delInstance(id) { 10630 delete instances$1[id]; 10631 } 10632 10633 /** 10634 * @module zrender/ZRender 10635 */ 10636 /** 10637 * @constructor 10638 * @alias module:zrender/ZRender 10639 * @param {string} id 10640 * @param {HTMLElement} dom 10641 * @param {Object} opts 10642 * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' 10643 * @param {number} [opts.devicePixelRatio] 10644 * @param {number} [opts.width] Can be 'auto' (the same as null/undefined) 10645 * @param {number} [opts.height] Can be 'auto' (the same as null/undefined) 10646 */ 10647 var ZRender = function (id, dom, opts) { 10648 10649 opts = opts || {}; 10650 10651 /** 10652 * @type {HTMLDomElement} 10653 */ 10654 this.dom = dom; 10655 10656 /** 10657 * @type {string} 10658 */ 10659 this.id = id; 10660 10661 var self = this; 10662 var storage = new Storage(); 10663 10664 var rendererType = opts.renderer; 10665 // TODO WebGL 10666 if (useVML) { 10667 if (!painterCtors.vml) { 10668 throw new Error('You need to require \'zrender/vml/vml\' to supp
10668ort IE8'); 10669 } 10670 rendererType = 'vml'; 10671 } 10672 else if (!rendererType || !painterCtors[rendererType]) { 10673 rendererType = 'canvas'; 10674 } 10675 var painter = new painterCtors[rendererType](dom, storage, opts, id); 10676 10677 this.storage = storage; 10678 this.painter = painter; 10679 10680 var handerProxy = (!env$1.node && !env$1.worker) ? new HandlerDomProxy(painter.getViewportRoot()) : null; 10681 this.handler = new Handler(storage, painter, handerProxy, painter.root); 10682 10683 /** 10684 * @type {module:zrender/animation/Animation} 10685 */ 10686 this.animation = new Animation({ 10687 stage: { 10688 update: bind(this.flush, this) 10689 } 10690 }); 10691 this.animation.start(); 10692 10693 /** 10694 * @type {boolean} 10695 * @private 10696 */ 10697 this._needsRefresh; 10698 10699 // æ·î½æ¼ storage.delFromStorage, å§£å¿î¼éç»æ«éåªç¤æ¶å¬ªå¢ éç»æ«éã§æ¾ 10700 // FIXME éå¤å£ugly 10701 var oldDelFromStorage = storage.delFromStorage; 10702 var oldAddToStorage = storage.addToStorage; 10703 10704 storage.delFromStorage = function (el) { 10705 oldDelFromStorage.call(storage, el); 10706 10707 el && el.removeSelfFromZr(self); 10708 }; 10709 10710 storage.addToStorage = function (el) { 10711 oldAddToStorage.call(storage, el); 10712 10713 el.addSelfToZr(self); 10714 }; 10715 }; 10716 10717 ZRender.prototype = { 10718 10719 constructor: ZRender, 10720 /** 10721 * é¾å³°å½ç¹ç°ç·¥éîç«´éåªç 10722 * @return {string} 10723 */ 10724 getId: function () { 10725 return this.id; 10726 }, 10727 10728 /** 10729 * 娣诲å§éåªç¤ 10730 * @param {module:zrender/Element} el 10731 */ 10732 add: function (el) { 10733 this.storage.addRoot(el); 10734 this._needsRefresh = true; 10735 }, 10736 10737 /** 10738 * éç»æ«éåªç¤ 10739 * @param {module:zrender/Element} el 10740 */ 10741 remove: function (el) { 10742 this.storage.delRoot(el); 10743 this._needsRefresh = true; 10744 }, 10745 10746 /** 10747 * Change configuration of layer 10748 * @param {string} zLevel 10749 * @param {Object} config 10750 * @param {string} [config.clearColor=0] Clear color 10751 * @param {string} [config.motionBlur=false] If enable motion blur 10752 * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer 10753 */ 10754 configLayer: function (zLevel, config) { 10755 if (this.painter.configLayer) { 10756 this.painter.configLayer(zLevel, config); 10757 } 10758 this._needsRefresh = true; 10759 }, 10760 10761 /** 10762 * Set background color 10763 * @param {string} backgroundColor 10764 */ 10765 setBackgroundColor: function (backgroundColor) { 10766 if (this.painter.setBackgroundColor) { 10767 this.painter.setBackgroundColor(backgroundColor); 10768 } 10769 this._needsRefresh = true; 10770 }, 10771 10772 /** 10773 * Repaint the canvas immediately 10774 */ 10775 refreshImmediately: function () { 10776 // var start = new Date(); 10777 // Clear needsRefresh ahead to avoid something wrong happens in refresh 10778 // Or it will cause zrender refreshes again and again. 10779 this._needsRefresh = false; 10780 this.painter.refresh(); 10781 /** 10782 * Avoid trigger zr.refresh in Element#beforeUpdate hook 10783 */ 10784 this._needsRefresh = false; 10785 // var end = new Date(); 10786 // var log = document.getElementById('log'); 10787 // if (log) { 10788 // log.innerHTML = log.innerHTML + '<br>' + (end - start); 10789 // } 10790 }, 10791 10792 /** 10793 * Mark and repaint the canvas in the next frame of browser 10794 */ 10795 refresh: function() { 10796 this._needsRefresh = true; 10797 }, 10798 10799 /** 10800 * Perform all refresh 10801 */ 10802 flush: function () {
10803 var triggerRendered; 10804 10805 if (this._needsRefresh) { 10806 triggerRendered = true; 10807 this.refreshImmediately(); 10808 } 10809 if (this._needsRefreshHover) { 10810 triggerRendered = true; 10811 this.refreshHoverImmediately(); 10812 } 10813 10814 triggerRendered && this.trigger('rendered'); 10815 }, 10816 10817 /** 10818 * Add element to hover layer 10819 * @param {module:zrender/Element} el 10820 * @param {Object} style 10821 */ 10822 addHover: function (el, style) { 10823 if (this.painter.addHover) { 10824 this.painter.addHover(el, style); 10825 this.refreshHover(); 10826 } 10827 }, 10828 10829 /** 10830 * Add element from hover layer 10831 * @param {module:zrender/Element} el 10832 */ 10833 removeHover: function (el) { 10834 if (this.painter.removeHover) { 10835 this.painter.removeHover(el); 10836 this.refreshHover(); 10837 } 10838 }, 10839 10840 /** 10841 * Clear all hover elements in hover layer 10842 * @param {module:zrender/Element} el 10843 */ 10844 clearHover: function () { 10845 if (this.painter.clearHover) { 10846 this.painter.clearHover(); 10847 this.refreshHover(); 10848 } 10849 }, 10850 10851 /** 10852 * Refresh hover in next frame 10853 */ 10854 refreshHover: function () { 10855 this._needsRefreshHover = true; 10856 }, 10857 10858 /** 10859 * Refresh hover immediately 10860 */ 10861 refreshHoverImmediately: function () { 10862 this._needsRefreshHover = false; 10863 this.painter.refreshHover && this.painter.refreshHover(); 10864 }, 10865 10866 /** 10867 * Resize the canvas. 10868 * Should be invoked when container size is changed 10869 * @param {Object} [opts] 10870 * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined) 10871 * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined) 10872 */ 10873 resize: function(opts) { 10874 opts = opts || {}; 10875 this.painter.resize(opts.width, opts.height); 10876 this.handler.resize(); 10877 }, 10878 10879 /** 10880 * Stop and clear all animation immediately 10881 */ 10882 clearAnimation: function () { 10883 this.animation.clear(); 10884 }, 10885 10886 /** 10887 * Get container width 10888 */ 10889 getWidth: function() { 10890 return this.painter.getWidth(); 10891 }, 10892 10893 /** 10894 * Get container height 10895 */ 10896 getHeight: function() { 10897 return this.painter.getHeight(); 10898 }, 10899 10900 /** 10901 * Export the canvas as Base64 URL 10902 * @param {string} type 10903 * @param {string} [backgroundColor='#fff'] 10904 * @return {string} Base64 URL 10905 */ 10906 // toDataURL: function(type, backgroundColor) { 10907 // return this.painter.getRenderedCanvas({ 10908 // backgroundColor: backgroundColor 10909 // }).toDataURL(type); 10910 // }, 10911 10912 /**
10913 * Converting a path to image. 10914 * It has much better performance of drawing image rather than drawing a vector path. 10915 * @param {module:zrender/graphic/Path} e 10916 * @param {number} width 10917 * @param {number} height 10918 */ 10919 pathToImage: function(e, dpr) { 10920 return this.painter.pathToImage(e, dpr); 10921 }, 10922 10923 /** 10924 * Set default cursor 10925 * @param {string} [cursorStyle='default'] æ¸å¬ªî§ crosshair 10926 */ 10927 setCursorStyle: function (cursorStyle) { 10928 this.handler.setCursorStyle(cursorStyle); 10929 }, 10930 10931 /** 10932 * Find hovered element 10933 * @param {number} x 10934 * @param {number} y 10935 * @return {Object} {target, topTarget} 10936 */ 10937 findHover: function (x, y) { 10938 return this.handler.findHover(x, y); 10939 }, 10940 10941 /** 10942 * Bind event 10943 * 10944 * @param {string} eventName Event name 10945 * @param {Function} eventHandler Handler function 10946 * @param {Object} [context] Context object 10947 */ 10948 on: function(eventName, eventHandler, context) { 10949 this.handler.on(eventName, eventHandler, context); 10950 }, 10951 10952 /** 10953 * Unbind event 10954 * @param {string} eventName Event name 10955 * @param {Function} [eventHandler] Handler function 10956 */ 10957 off: function(eventName, eventHandler) { 10958 this.handler.off(eventName, eventHandler); 10959 }, 10960 10961 /** 10962 * Trigger event manually 10963 * 10964 * @param {string} eventName Event name 10965 * @param {event=} event Event object 10966 */ 10967 trigger: function (eventName, event) { 10968 this.handler.trigger(eventName, event); 10969 }, 10970 10971 10972 /** 10973 * Clear all objects and the canvas. 10974 */ 10975 clear: function () { 10976 this.storage.delRoot(); 10977 this.painter.clear(); 10978 }, 10979 10980 /** 10981 * Dispose self. 10982 */ 10983 dispose: function () { 10984 this.animation.stop(); 10985 10986 this.clear(); 10987 this.storage.dispose(); 10988 this.painter.dispose(); 10989 this.handler.dispose(); 10990 10991 this.animation = 10992 this.storage = 10993 this.painter = 10994 this.handler = null; 10995 10996 delInstance(this.id); 10997 } 10998 }; 10999 11000 11001 11002 var zrender = (Object.freeze || Object)({ 11003 version: version$1, 11004 init: init$1, 11005 dispose: dispose$1, 11006 getInstance: getInstance, 11007 registerPainter: registerPainter 11008 }); 11009 11010 /* 11011* Licensed to the Apache Software Foundation (ASF) under one 11012* or more contributor license agreements. See the NOTICE file 11013* distributed with this work for additional information 11014* regarding copyright ownership. The ASF licenses this file 11015* to you under the Apache License, Version 2.0 (the 11016* "License"); you may not use this file except in compliance 11017* with the License. You may obtain a copy of the License at 11018* 11019* http://www.apache.org/licenses/LICENSE-2.0 11020* 11021* Unless required by applicable law or agreed to in writing, 11022* software distributed under the License is distributed on an 11023* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11024* KIND, either express or implied. See the License for the 11025* specific language governing permissions and limitations 11026* under the License. 11027*/ 11028 11029 var each$2 = each$1; 11030 var isObject$2 = isObject$1; 11031 var isArray$1 = isArray; 11032 11033 /** 11034 * Make the name displayable. But we should 11035 * make sure it is not duplicated with user 11036 * specified name, so use '\0'; 11037 */ 11038 var DUMMY_COMPONENT_NAME_PREFIX = 'series\0'; 11039 11040 /** 11041 * If value is not array, then translate it to array. 11042 * @param {*} value 11043 * @return {Array} [value] or value 11044 */ 11045 function normalizeToArray(value) { 11046 return value instanceof Array 11047 ? value 11048 : value == null 11049 ? [] 11050 : [value]; 11051 } 11052 11053 /** 11054 * Sync default option between normal and emphasis like `position` and `show` 11055 * In case some one will write code like 11056 * label: { 11057 * show: false, 11058 * position: 'outside', 11059 * fontSize: 18 11060 * }, 11061 * emphasis: { 11062 * label: { show: true } 11063 * } 11064 * @param {Object} opt 11065 * @param {string} key 11066 * @param {Array.<string>} subOpts 11067 */ 11068 function defaultEmphasis(opt, key, subOpts) { 11069 // Caution: performance sensitive. 11070 if (opt) { 11071 opt[key] = opt[key] || {}; 11072 opt.emphasis = opt.emphasis || {}; 11073 opt.emphasis[key] = opt.emphasis[key] || {}; 11074 11075 // Default emphasis option from normal 11076 for (var i = 0, len = subOpts.length; i < len; i++) { 11077 var subOptName = subOpts[i]; 11078 if (!opt.emphasis[key].hasOwnProperty(subOptName) 11079 && opt[key].hasOwnProperty(subOptName) 11080 ) { 11081 opt.emphasis[key][subOptName] = opt[key][subOptName]; 11082 } 11083 } 11084 } 11085 } 11086 11087 var TEXT_STYLE_OPTIONS = [ 11088 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 11089 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 11090 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 11091 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 11092 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 11093 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding' 11094 ]; 11095 11096// modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([ 11097// 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter', 11098// 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 11099// // FIXME: deprecated, check and remove it. 11100// 'textStyle' 11101// ]); 11102 11103 /** 11104 * The method do not ensure performance. 11105 * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] 11106 * This helper method retieves value from data. 11107 * @param {string|number|Date|Array|Object} dataItem 11108 * @return {number|string|Date|Array.<number|string|Date>} 11109 */ 11110 function getDataItemValue(dataItem) { 11111 return (isObject$2(dataItem) && !isArray$1(dataItem) && !(dataItem instanceof Date)) 11112 ? dataItem.value : dataItem; 11113 } 11114 11115 /** 11116 * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] 11117 * This helper method determine if dataItem has extra option besides value 11118 * @param {string|number|Date|Array|Object} dataItem 11119 */ 11120 function isDataItemOption(dataItem) { 11121 return isObject$2(dataItem) 11122 && !(dataItem instanceof Array); 11123 // // markLine data can be array 11124 // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); 11125 } 11126 11127 /** 11128 * Mapping to exists for merge. 11129 * 11130 * @public 11131 * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists 11132 * @param {Object|Array.<Object>} newCptOptions 11133 * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}], 11134 * index of which is the same as exists. 11135 */ 11136 function mappingToExists(exists, newCptOptions) { 11137 // Mapping by the order by original option (but not order of 11138 // new option) in merge mode. Because we should ensure 11139 // some specified index (like xAxisIndex) is consistent with 11140 // original option, which is easy to understand, espatially in 11141 // media query. And in most case, merge option is used to 11142 // update partial option but not be expected to change order. 11143 newCptOptions = (newCptOptions || []).slice(); 11144 11145 var result = map(exists || [], function (obj, index) { 11146 return {exist: obj}; 11147 }); 11148 11149 // Mapping by id or name if specified. 11150 each$2(newCptOptions, function (cptOption, index) { 11151 if (!isObject$2(cptOption)) { 11152 return; 11153 } 11154 11155 // id has highest priority. 11156 for (var i = 0; i < result.length; i++) { 11157 if (!result[i].option // Consider name: two map to one. 11158 && cptOption.id != null 11159 && result[i].exist.id === cptOption.id + '' 11160 ) { 11161 result[i].option = cptOption; 11162 newCptOptions[index] = null; 11163 return; 11164 } 11165 } 11166 11167 for (var i = 0; i < result.length; i++) { 11168 var exist = result[i].exist; 11169 if (!result[i].option // Consider name: two map to one. 11170 // Can not match when both ids exist but different. 11171 && (exist.id == null || cptOption.id == null) 11172 && cptOption.name != null 11173 && !isIdInner(cptOption) 11174 && !isIdInner(exist) 11175 && exist.name === cptOption.name + '' 11176 ) { 11177 result[i].option = cptOption; 11178 newCptOptions[index] = null; 11179 return; 11180 } 11181 } 11182 }); 11183 11184 // Otherwise mapping by index. 11185 each$2(newCptOptions, function (cptOption, index) { 11186 if (!isObject$2(cptOption)) { 11187 return; 11188 } 11189 11190 var i = 0; 11191 for (; i < result.length; i++) { 11192 var exist = result[i].exist; 11193 if (!result[i].option 11194 // Existing model that already has id should be able to 11195 // mapped to (because after mapping performed model may 11196 // be assigned with a id, whish should not affect next 11197 // mapping), except those has inner id. 11198 && !isIdInner(exist) 11199 // Caution:
11200 // Do not overwrite id. But name can be overwritten, 11201 // because axis use name as 'show label text'. 11202 // 'exist' always has id and name and we dont 11203 // need to check it. 11204 && cptOption.id == null 11205 ) { 11206 result[i].option = cptOption; 11207 break; 11208 } 11209 } 11210 11211 if (i >= result.length) { 11212 result.push({option: cptOption}); 11213 } 11214 }); 11215 11216 return result; 11217 } 11218 11219 /** 11220 * Make id and name for mapping result (result of mappingToExists) 11221 * into `keyInfo` field. 11222 * 11223 * @public 11224 * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}], 11225 * which order is the same as exists. 11226 * @return {Array.<Object>} The input. 11227 */ 11228 function makeIdAndName(mapResult) { 11229 // We use this id to hash component models and view instances 11230 // in echarts. id can be specified by user, or auto generated. 11231 11232 // The id generation rule ensures new view instance are able 11233 // to mapped to old instance when setOption are called in 11234 // no-merge mode. So we generate model id by name and plus 11235 // type in view id. 11236 11237 // name can be duplicated among components, which is convenient 11238 // to specify multi components (like series) by one name. 11239 11240 // Ensure that each id is distinct. 11241 var idMap = createHashMap(); 11242 11243 each$2(mapResult, function (item, index) { 11244 var existCpt = item.exist; 11245 existCpt && idMap.set(existCpt.id, item); 11246 }); 11247 11248 each$2(mapResult, function (item, index) { 11249 var opt = item.option; 11250 11251 assert$1( 11252 !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 11253 'id duplicates: ' + (opt && opt.id) 11254 ); 11255 11256 opt && opt.id != null && idMap.set(opt.id, item); 11257 !item.keyInfo && (item.keyInfo = {}); 11258 }); 11259 11260 // Make name and id. 11261 each$2(mapResult, function (item, index) { 11262 var existCpt = item.exist; 11263 var opt = item.option; 11264 var keyInfo = item.keyInfo; 11265 11266 if (!isObject$2(opt)) { 11267 return; 11268 } 11269 11270 // name can be overwitten. Consider case: axis.name = '20km'. 11271 // But id generated by name will not be changed, which affect 11272 // only in that case: setOption with 'not merge mode' and view 11273 // instance will be recreated, which can be accepted. 11274 keyInfo.name = opt.name != null 11275 ? opt.name + '' 11276 : existCpt 11277 ? existCpt.name 11278 // Avoid diffferent series has the same name, 11279 // because name may be used like in color pallet. 11280 : DUMMY_COMPONENT_NAME_PREFIX + index; 11281 11282 if (existCpt) { 11283 keyInfo.id = existCpt.id; 11284 } 11285 else if (opt.id != null) { 11286 keyInfo.id = opt.id + ''; 11287 } 11288 else { 11289 // Consider this situatoin: 11290 // optionA: [{name: 'a'}, {name: 'a'}, {..}] 11291 // optionB [{..}, {name: 'a'}, {name: 'a'}] 11292 // Series with the same name between optionA and optionB 11293 // should be mapped. 11294 var idNum = 0; 11295 do { 11296 keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++; 11297 } 11298 while (idMap.get(keyInfo.id)); 11299 } 11300 11301 idMap.set(keyInfo.id, item); 11302 }); 11303 } 11304 11305 function isNameSpecified(componentModel) { 11306 var name = componentModel.name; 11307 // Is specified when `indexOf` get -1 or > 0. 11308 return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX)); 11309 } 11310 11311 /** 11312 * @public 11313 * @param {Object} cptOption 11314 * @return {boolean} 11315 */ 11316 function isIdInner(cptOption) { 11317 return isObject$2(cptOption) 11318 && cptOption.id 11319 && (cptOption.id + '').indexOf('\0_ec_\0') === 0; 11320 } 11321 11322 /** 11323 * A helper for removing duplicate items between batchA and batchB, 11324 * and in themselves, and categorize by series. 11325 * 11326 * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] 11327 * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] 11328 * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB] 11329 */ 11330 function compressBatches(batchA, batchB) { 11331 var mapA = {}; 11332 var mapB = {}; 11333 11334 makeMap(batchA || [], mapA); 11335 makeMap(batchB || [], mapB, mapA); 11336 11337 return [mapToArray(mapA), mapToArray(mapB)]; 11338 11339 function makeMap(sourceBatch, map$$1, otherMap) { 11340 for (var i = 0, len = sourceBatch.length; i < len; i++) { 11341 var seriesId = sourceBatch[i].seriesId; 11342 var dataIndices = normalizeToArray(sourceBatch[i].dataIndex); 11343 var otherDataIndices = otherMap && otherMap[seriesId]; 11344 11345 for (var j = 0, lenj = dataIndices.length; j < lenj; j++) { 11346 var dataIndex = dataIndices[j]; 11347 11348 if (otherDataIndices && otherDataIndices[dataIndex]) { 11349 otherDataIndices[dataIndex] = null; 11350 } 11351 else { 11352 (map$$1[seriesId] || (map$$1[seriesId] = {}))[dataIndex] = 1; 11353 } 11354 } 11355 } 11356 } 11357 11358 function mapToArray(map$$1, isData) { 11359 var result = []; 11360 for (var i in map$$1) { 11361 if (map$$1.hasOwnProperty(i) && map$$1[i] != null) { 11362 if (isData) { 11363 result.push(+i); 11364 } 11365 else { 11366 var dataIndices = mapToArray(map$$1[i], true); 11367 dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices}); 11368 } 11369 } 11370 } 11371 return result; 11372 } 11373 } 11374 11375 /**
11376 * @param {module:echarts/data/List} data 11377 * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name 11378 * each of which can be Array or primary type. 11379 * @return {number|Array.<number>} dataIndex If not found, return undefined/null. 11380 */ 11381 function queryDataIndex(data, payload) { 11382 if (payload.dataIndexInside != null) { 11383 return payload.dataIndexInside; 11384 } 11385 else if (payload.dataIndex != null) { 11386 return isArray(payload.dataIndex) 11387 ? map(payload.dataIndex, function (value) { 11388 return data.indexOfRawIndex(value); 11389 }) 11390 : data.indexOfRawIndex(payload.dataIndex); 11391 } 11392 else if (payload.name != null) { 11393 return isArray(payload.name) 11394 ? map(payload.name, function (value) { 11395 return data.indexOfName(value); 11396 }) 11397 : data.indexOfName(payload.name); 11398 } 11399 } 11400 11401 /** 11402 * Enable property storage to any host object. 11403 * Notice: Serialization is not supported. 11404 * 11405 * For example: 11406 * var inner = zrUitl.makeInner(); 11407 * 11408 * function some1(hostObj) { 11409 * inner(hostObj).someProperty = 1212; 11410 * ... 11411 * } 11412 * function some2() { 11413 * var fields = inner(this); 11414 * fields.someProperty1 = 1212; 11415 * fields.someProperty2 = 'xx'; 11416 * ... 11417 * } 11418 * 11419 * @return {Function} 11420 */ 11421 function makeInner() { 11422 // Consider different scope by es module import. 11423 var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5); 11424 return function (hostObj) { 11425 return hostObj[key] || (hostObj[key] = {}); 11426 }; 11427 } 11428 var innerUniqueIndex = 0; 11429 11430 /** 11431 * @param {module:echarts/model/Global} ecModel 11432 * @param {string|Object} finder 11433 * If string, e.g., 'geo', means {geoIndex: 0}. 11434 * If Object, could contain some of these properties below: 11435 * { 11436 * seriesIndex, seriesId, seriesName, 11437 * geoIndex, geoId, geoName, 11438 * bmapIndex, bmapId, bmapName, 11439 * xAxisIndex, xAxisId, xAxisName, 11440 * yAxisIndex, yAxisId, yAxisName, 11441 * gridIndex, gridId, gridName, 11442 * ... (can be extended) 11443 * } 11444 * Each properties can be number|string|Array.<number>|Array.<string> 11445 * For example, a finder could be 11446 * { 11447 * seriesIndex: 3, 11448 * geoId: ['aa', 'cc'], 11449 * gridName: ['xx', 'rr'] 11450 * } 11451 * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify) 11452 * If nothing or null/undefined specified, return nothing. 11453 * @param {Object} [opt] 11454 * @param {string} [opt.defaultMainType] 11455 * @param {Array.<string>} [opt.includeMainTypes] 11456 * @return {Object} result like: 11457 * { 11458 * seriesModels: [seriesModel1, seriesModel2], 11459 * seriesModel: seriesModel1, // The first model 11460 * geoModels: [geoModel1, geoModel2], 11461 * geoModel: geoModel1, // The first model 11462 * ... 11463 * } 11464 */ 11465 function parseFinder(ecModel, finder, opt) { 11466 if (isString(finder)) { 11467 var obj = {}; 11468 obj[finder + 'Index'] = 0; 11469 finder = obj; 11470 } 11471 11472 var defaultMainType = opt && opt.defaultMainType; 11473 if (defaultMainType 11474 && !has(finder, defaultMainType + 'Index') 11475 && !has(finder, defaultMainType + 'Id') 11476 && !has(finder, defaultMainType + 'Name') 11477 ) { 11478 finder[defaultMainType + 'Index'] = 0; 11479 } 11480 11481 var result = {}; 11482 11483 each$2(finder, function (value, key) { 11484 var value = finder[key]; 11485 11486 // Exclude 'dataIndex' and other illgal keys. 11487 if (key === 'dataIndex' || key === 'dataIndexInside') { 11488 result[key] = value; 11489 return; 11490 } 11491 11492 var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || []; 11493 var mainType = parsedKey[1]; 11494 var queryType = (parsedKey[2] || '').toLowerCase(); 11495 11496 if (!mainType 11497 || !queryType 11498 || value == null 11499 || (queryType === 'index' && value === 'none') 11500 || (opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) 11501 ) { 11502 return; 11503 } 11504 11505 var queryParam = {mainType: mainType};
11506 if (queryType !== 'index' || value !== 'all') { 11507 queryParam[queryType] = value; 11508 } 11509 11510 var models = ecModel.queryComponents(queryParam); 11511 result[mainType + 'Models'] = models; 11512 result[mainType + 'Model'] = models[0]; 11513 }); 11514 11515 return result; 11516 } 11517 11518 function has(obj, prop) { 11519 return obj && obj.hasOwnProperty(prop); 11520 } 11521 11522 function setAttribute(dom, key, value) { 11523 dom.setAttribute 11524 ? dom.setAttribute(key, value) 11525 : (dom[key] = value); 11526 } 11527 11528 function getAttribute(dom, key) { 11529 return dom.getAttribute 11530 ? dom.getAttribute(key) 11531 : dom[key]; 11532 } 11533 11534 /* 11535* Licensed to the Apache Software Foundation (ASF) under one 11536* or more contributor license agreements. See the NOTICE file 11537* distributed with this work for additional information 11538* regarding copyright ownership. The ASF licenses this file 11539* to you under the Apache License, Version 2.0 (the 11540* "License"); you may not use this file except in compliance 11541* with the License. You may obtain a copy of the License at 11542* 11543* http://www.apache.org/licenses/LICENSE-2.0 11544* 11545* Unless required by applicable law or agreed to in writing, 11546* software distributed under the License is distributed on an 11547* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11548* KIND, either express or implied. See the License for the 11549* specific language governing permissions and limitations 11550* under the License. 11551*/ 11552 11553 var TYPE_DELIMITER = '.'; 11554 var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; 11555 11556 /** 11557 * Notice, parseClassType('') should returns {main: '', sub: ''} 11558 * @public 11559 */ 11560 function parseClassType$1(componentType) { 11561 var ret = {main: '', sub: ''}; 11562 if (componentType) { 11563 componentType = componentType.split(TYPE_DELIMITER); 11564 ret.main = componentType[0] || ''; 11565 ret.sub = componentType[1] || ''; 11566 } 11567 return ret; 11568 } 11569 11570 /** 11571 * @public 11572 */ 11573 function checkClassType(componentType) { 11574 assert$1( 11575 /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 11576 'componentType "' + componentType + '" illegal' 11577 ); 11578 } 11579 11580 /** 11581 * @public 11582 */ 11583 function enableClassExtend(RootClass, mandatoryMethods) { 11584 11585 RootClass.$constructor = RootClass; 11586 RootClass.extend = function (proto) { 11587 11588 if (__DEV__) { 11589 each$1(mandatoryMethods, function (method) { 11590 if (!proto[method]) { 11591 console.warn( 11592 'Method `' + method + '` should be implemented' 11593 + (proto.type ? ' in ' + proto.type : '') + '.' 11594 ); 11595 } 11596 }); 11597 } 11598 11599 var superClass = this; 11600 var ExtendedClass = function () { 11601 if (!proto.$constructor) { 11602 superClass.apply(this, arguments); 11603 } 11604 else { 11605 proto.$constructor.apply(this, arguments); 11606 } 11607 }; 11608 11609 extend(ExtendedClass.prototype, proto); 11610 11611 ExtendedClass.extend = this.extend; 11612 ExtendedClass.superCall = superCall; 11613 ExtendedClass.superApply = superApply; 11614 inherits(ExtendedClass, this); 11615 ExtendedClass.superClass = superClass; 11616 11617 return ExtendedClass; 11618 }; 11619 } 11620 11621 var classBase = 0; 11622 11623 /** 11624 * Can not use instanceof, consider different scope by 11625 * cross domain or es module import in ec extensions. 11626 * Mount a method "isInstance()" to Clz. 11627 */ 11628 function enableClassCheck(Clz) { 11629 var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_'); 11630 Clz.prototype[classAttr] = true; 11631 11632 if (__DEV__) { 11633 assert$1(!Clz.isInstance, 'The method "is" can not be defined.'); 11634 } 11635 11636 Clz.isInstance = function (obj) { 11637 return !!(obj && obj[classAttr]); 11638 }; 11639 } 11640 11641// superCall should have class info, which can not be fetch from 'this'. 11642// Consider this case: 11643// class A has method f, 11644// class B inherits class A, overrides method f, f call superApply('f'), 11645// class C inherits class B, do not overrides method f, 11646// then when method of class C is called, dead loop occured. 11647 function superCall(context, methodName) { 11648 var args = slice(arguments, 2); 11649 return this.superClass.prototype[methodName].apply(context, args
11649); 11650 } 11651 11652 function superApply(context, methodName, args) { 11653 return this.superClass.prototype[methodName].apply(context, args); 11654 } 11655 11656 /** 11657 * @param {Object} entity 11658 * @param {Object} options 11659 * @param {boolean} [options.registerWhenExtend] 11660 * @public 11661 */ 11662 function enableClassManagement(entity, options) { 11663 options = options || {}; 11664 11665 /** 11666 * Component model classes 11667 * key: componentType, 11668 * value: 11669 * componentClass, when componentType is 'xxx' 11670 * or Object.<subKey, componentClass>, when componentType is 'xxx.yy' 11671 * @type {Object} 11672 */ 11673 var storage = {}; 11674 11675 entity.registerClass = function (Clazz, componentType) { 11676 if (componentType) { 11677 checkClassType(componentType); 11678 componentType = parseClassType$1(componentType); 11679 11680 if (!componentType.sub) { 11681 if (__DEV__) { 11682 if (storage[componentType.main]) { 11683 console.warn(componentType.main + ' exists.'); 11684 } 11685 } 11686 storage[componentType.main] = Clazz; 11687 } 11688 else if (componentType.sub !== IS_CONTAINER) { 11689 var container = makeContainer(componentType); 11690 container[componentType.sub] = Clazz; 11691 } 11692 } 11693 return Clazz; 11694 }; 11695 11696 entity.getClass = function (componentMainType, subType, throwWhenNotFound) { 11697 var Clazz = storage[componentMainType]; 11698 11699 if (Clazz && Clazz[IS_CONTAINER]) { 11700 Clazz = subType ? Clazz[subType] : null; 11701 } 11702 11703 if (throwWhenNotFound && !Clazz) { 11704 throw new Error( 11705 !subType 11706 ? componentMainType + '.' + 'type should be specified.' 11707 : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.' 11708 ); 11709 } 11710 11711 return Clazz; 11712 }; 11713 11714 entity.getClassesByMainType = function (componentType) { 11715 componentType = parseClassType$1(componentType); 11716 11717 var result = []; 11718 var obj = storage[componentType.main]; 11719 11720 if (obj && obj[IS_CONTAINER]) { 11721 each$1(obj, function (o, type) { 11722 type !== IS_CONTAINER && result.push(o); 11723 }); 11724 } 11725 else { 11726 result.push(obj); 11727 } 11728 11729 return result; 11730 }; 11731 11732 entity.hasClass = function (componentType) { 11733 // Just consider componentType.main. 11734 componentType = parseClassType$1(componentType); 11735 return !!storage[componentType.main]; 11736 }; 11737 11738 /** 11739 * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx'] 11740 */ 11741 entity.getAllClassMainTypes = function () { 11742 var types = []; 11743 each$1(storage, function (obj, type) { 11744 types.push(type); 11745 }); 11746 return types; 11747 }; 11748 11749 /** 11750 * If a main type is container and has sub types 11751 * @param {string} mainType 11752 * @return {boolean} 11753 */ 11754 entity.hasSubTypes = function (componentType) { 11755 componentType = parseClassType$1(componentType); 11756 var obj = storage[componentType.main]; 11757 return obj && obj[IS_CONTAINER]; 11758 }; 11759 11760 entity.parseClassType = parseClassType$1; 11761 11762 function makeContainer(componentType) { 11763 var container = storage[componentType.main]; 11764 if (!container || !container[IS_CONTAINER]) { 11765 container = storage[componentType.main] = {}; 11766 container[IS_CONTAINER] = true; 11767 } 11768 return container; 11769 } 11770 11771 if (options.registerWhenExtend) { 11772 var originalExtend = entity.extend; 11773 if (originalExtend) { 11774 entity.extend = function (proto) { 11775 var ExtendedClass = originalExtend.call(this, proto); 11776 return entity.registerClass(ExtendedClass, proto.type); 11777 }; 11778 } 11779 } 11780 11781 return entity; 11782 } 11783 11784 /** 11785 * @param {string|Array.<string>} properties 11786 */ 11787 11788 /* 11789* Licensed to the Apache Software Foundation (ASF) under one 11790* or more contributor license agreements. See the NOTICE file 11791* distributed with this work for additional information 11792* regarding copyright ownership. The ASF licenses this file 11793* to you under the Apache License, Version 2.0 (the 11794* "License");
11794 you may not use this file except in compliance 11795* with the License. You may obtain a copy of the License at 11796* 11797* http://www.apache.org/licenses/LICENSE-2.0 11798* 11799* Unless required by applicable law or agreed to in writing, 11800* software distributed under the License is distributed on an 11801* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11802* KIND, either express or implied. See the License for the 11803* specific language governing permissions and limitations 11804* under the License. 11805*/ 11806 11807// TODO Parse shadow style 11808// TODO Only shallow path support 11809 var makeStyleMapper = function (properties) { 11810 // Normalize 11811 for (var i = 0; i < properties.length; i++) { 11812 if (!properties[i][1]) { 11813 properties[i][1] = properties[i][0]; 11814 } 11815 } 11816 return function (model, excludes, includes) { 11817 var style = {}; 11818 for (var i = 0; i < properties.length; i++) { 11819 var propName = properties[i][1]; 11820 if ((excludes && indexOf(excludes, propName) >= 0) 11821 || (includes && indexOf(includes, propName) < 0) 11822 ) { 11823 continue; 11824 } 11825 var val = model.getShallow(propName); 11826 if (val != null) { 11827 style[properties[i][0]] = val; 11828 } 11829 } 11830 return style; 11831 }; 11832 }; 11833 11834 /* 11835* Licensed to the Apache Software Foundation (ASF) under one 11836* or more contributor license agreements. See the NOTICE file 11837* distributed with this work for additional information 11838* regarding copyright ownership. The ASF licenses this file 11839* to you under the Apache License, Version 2.0 (the 11840* "License"); you may not use this file except in compliance 11841* with the License. You may obtain a copy of the License at 11842* 11843* http://www.apache.org/licenses/LICENSE-2.0 11844* 11845* Unless required by applicable law or agreed to in writing, 11846* software distributed under the License is distributed on an 11847* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11848* KIND, either express or implied. See the License for the 11849* specific language governing permissions and limitations 11850* under the License. 11851*/ 11852 11853 var getLineStyle = makeStyleMapper( 11854 [ 11855 ['lineWidth', 'width'], 11856 ['stroke', 'color'], 11857 ['opacity'], 11858 ['shadowBlur'], 11859 ['shadowOffsetX'], 11860 ['shadowOffsetY'], 11861 ['shadowColor'] 11862 ] 11863 ); 11864 11865 var lineStyleMixin = { 11866 getLineStyle: function (excludes) { 11867 var style = getLineStyle(this, excludes); 11868 var lineDash = this.getLineDash(style.lineWidth); 11869 lineDash && (style.lineDash = lineDash); 11870 return style; 11871 }, 11872 11873 getLineDash: function (lineWidth) { 11874 if (lineWidth == null) { 11875 lineWidth = 1; 11876 } 11877 var lineType = this.get('type'); 11878 var dotSize = Math.max(lineWidth, 2); 11879 var dashSize = lineWidth * 4; 11880 return (lineType === 'solid' || lineType == null) ? null 11881 : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]); 11882 } 11883 }; 11884 11885 /* 11886* Licensed to the Apache Software Foundation (ASF) under one 11887* or more contributor license agreements. See the NOTICE file 11888* distributed with this work for additional information 11889* regarding copyright ownership. The ASF licenses this file 11890* to you under the Apache License, Version 2.0 (the 11891* "License"); you may not use this file except in compliance 11892* with the License. You may obtain a copy of the License at 11893* 11894* http://www.apache.org/licenses/LICENSE-2.0 11895* 11896* Unless required by applicable law or agreed to in writing, 11897* software distributed under the License is distributed on an 11898* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11899* KIND, either express or implied. See the License for the 11900* specific language governing permissions and limitations 11901* under the License. 11902*/ 11903 11904 var getAreaStyle = makeStyleMapper( 11905 [ 11906 ['fill', 'color'], 11907 ['shadowBlur'], 11908 ['shadowOffsetX'], 11909 ['shadowOffsetY'], 11910 ['opacity'], 11911 ['shadowColor'] 11912 ] 11913 ); 11914 11915 var areaStyleMixin = { 11916 getAreaStyle: function (excludes, includes) { 11917 return getAreaStyle(this, excludes, includes); 11918 } 11919 }; 11920 11921 /** 11922 * éè¬åæåå§ªå¦¯â³æ½¡ 11923 * @module zrender/core/curve 11924 * @author pissang(https://www.github.com/pissang) 11925 */ 11926 11927 var mathPow = Math.pow; 11928 var mathSqrt$2 = Math.sqrt; 11929 11930 var EPSILON$1 = 1e-8; 11931 var EPSILON_NUMERIC = 1e-4; 11932 11933 var THREE_SQRT = mathSqrt$2(3); 11934 var ONE_THIRD = 1 / 3; 11935 11936// æ¶åæ¤éæ©åº 11937 var _v0 = create(); 11938 var _v1 = create(); 11939 var _v2 = create(); 11940 11941 function isAroundZero(val) { 11942 return val > -EPSILON$1 && val < EPSILON$1; 11943 } 11944 function isNotAroundZero$1(val) { 11945 return val > EPSILON$1 || val < -EPSILON$1; 11946 } 11947 /** 11948 * çï¼ç»æ¶å¤î¼çæ¿î£çæ¿â¬ï¿½ 11949 * @memberOf module:zrender/core/curve 11950 * @param {number} p0 11951 * @param {number} p1 11952 * @param {number} p2 11953 * @param {number} p3 11954 * @param {number} t 11955 * @return {number} 11956 */ 11957 function cubicAt(p0, p1, p2, p3, t) { 11958 var onet = 1 - t; 11959 return onet * onet * (onet * p0 + 3 * t * p1) 11960 + t * t * (t * p3 + 3 * onet * p2); 11961 } 11962 11963 /** 11964 * çï¼ç»æ¶å¤î¼çæ¿î£çæ¿î±éæ¿â¬ï¿½ 11965 * @memberOf module:zrender/core/curve 11966 * @param {number} p0 11967 * @param {number} p1 11968 * @param {number} p2 11969 * @param {number} p3 11970 * @param {number} t 11971 * @return {number} 11972 */ 11973 function cubicDerivativeAt(p0, p1, p2, p3, t) { 11974 var onet = 1 - t; 11975 return 3 * ( 11976 ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet 11977 + (p3 - p2) * t * t 11978 ); 11979 } 11980 11981 /** 11982 * çï¼ç»æ¶å¤î¼çæ¿î£çææç»å¬«ç´éå±¼å¨é¢ã§æ´é²æå寮� 11983 * @memberOf module:zrender/core/curve 11984 * @param {number} p0 11985 * @param {number} p1 11986 * @param {number} p2 11987 * @param {number} p3 11988 * @param {number} val 11989 * @param {Array.<number>} roots 11990 * @return {number} é夿¥éè§æé©ï¿½ 11991 */ 11992 function cubicRootAt(p0, p1, p2, p3, val, roots) { 11993 // Evaluate roots of cubic functions 11994 var a = p3 + 3 * (p1 - p2) - p0; 11995 var b = 3 * (p2 - p1 * 2 + p0); 11996 var c = 3 * (p1 - p0); 11997 var d = p0 - val; 11998 11999 var A = b * b - 3 * a * c; 12000 var B = b * c - 9 * a * d; 12001 var C = c * c - 3 * b * d; 12002 12003 var n = 0; 12004 12005 if (isAroundZero(A) && isAroundZero(B)) { 12006 if (isAroundZero(b)) { 12007 roots[0] = 0; 12008 } 12009 else { 12010 var t1 = -c / b; //t1, t2, t3, b is not zero 12011 if (t1 >= 0 && t1 <= 1) { 12012 roots[n++] = t1; 12013 } 12014 } 12015 } 12016 else { 12017 var disc = B * B - 4 * A * C; 12018 12019 if (isAroundZero(disc)) { 12020 var K = B / A; 12021 var t1 = -b / a + K; // t1, a is not zero 12022 var t2 = -K / 2; // t2, t3 12023 if (t1 >= 0 && t1 <= 1) { 12024 roots[n++] = t1; 12025 } 12026 if (t2 >= 0 && t2 <= 1) { 12027 roots[n++] = t2; 12028 } 12029 } 12030 else if (disc > 0) { 12031 var discSqrt = mathSqrt$2(disc); 12032 var Y1 = A * b + 1.5 * a * (-B + discSqrt); 12033 var Y2 = A * b + 1.5 * a * (-B - discSqrt); 12034 if (Y1 < 0) {
vendor: 11,967 bytes, lines 12035-12435
12035 Y1 = -mathPow(-Y1, ONE_THIRD); 12036 } 12037 else { 12038 Y1 = mathPow(Y1, ONE_THIRD); 12039 } 12040 if (Y2 < 0) { 12041 Y2 = -mathPow(-Y2, ONE_THIRD); 12042 } 12043 else { 12044 Y2 = mathPow(Y2, ONE_THIRD); 12045 } 12046 var t1 = (-b - (Y1 + Y2)) / (3 * a); 12047 if (t1 >= 0 && t1 <= 1) { 12048 roots[n++] = t1; 12049 } 12050 } 12051 else { 12052 var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt$2(A * A * A)); 12053 var theta = Math.acos(T) / 3; 12054 var ASqrt = mathSqrt$2(A); 12055 var tmp = Math.cos(theta); 12056 12057 var t1 = (-b - 2 * ASqrt * tmp) / (3 * a); 12058 var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a); 12059 var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a); 12060 if (t1 >= 0 && t1 <= 1) { 12061 roots[n++] = t1; 12062 } 12063 if (t2 >= 0 && t2 <= 1) { 12064 roots[n++] = t2; 12065 } 12066 if (t3 >= 0 && t3 <= 1) { 12067 roots[n++] = t3; 12068 } 12069 } 12070 } 12071 return n; 12072 } 12073 12074 /** 12075 * çï¼ç»æ¶å¤î¼çæ¿î£çææç»å¬«ç¬éæ¬â¬è©æ®æµ£å¶ç 12076 * @memberOf module:zrender/core/curve 12077 * @param {number} p0 12078 * @param {number} p1 12079 * @param {number} p2 12080 * @param {number} p3 12081 * @param {Array.<number>} extrema 12082 * @return {number} é夿¥éæ®æ´° 12083 */ 12084 function cubicExtrema(p0, p1, p2, p3, extrema) { 12085 var b = 6 * p2 - 12 * p1 + 6 * p0; 12086 var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2; 12087 var c = 3 * p1 - 3 * p0; 12088 12089 var n = 0; 12090 if (isAroundZero(a)) { 12091 if (isNotAroundZero$1(b)) { 12092 var t1 = -c / b; 12093 if (t1 >= 0 && t1 <=1) { 12094 extrema[n++] = t1; 12095 } 12096 } 12097 } 12098 else { 12099 var disc = b * b - 4 * a * c; 12100 if (isAroundZero(disc)) { 12101 extrema[0] = -b / (2 * a); 12102 } 12103 else if (disc > 0) { 12104 var discSqrt = mathSqrt$2(disc); 12105 var t1 = (-b + discSqrt) / (2 * a); 12106 var t2 = (-b - discSqrt) / (2 * a); 12107 if (t1 >= 0 && t1 <= 1) { 12108 extrema[n++] = t1; 12109 } 12110 if (t2 >= 0 && t2 <= 1) { 12111 extrema[n++] = t2; 12112 } 12113 } 12114 } 12115 return n; 12116 } 12117 12118 /** 12119 * ç¼ååæ¶å¤î¼çæ¿î£çææ´¸ç»¾ï¿½ 12120 * @memberOf module:zrender/core/curve 12121 * @param {number} p0 12122 * @param {number} p1 12123 * @param {number} p2 12124 * @param {number} p3 12125 * @param {number} t 12126 * @param {Array.<number>} out 12127 */ 12128 function cubicSubdivide(p0, p1, p2, p3, t, out) { 12129 var p01 = (p1 - p0) * t + p0; 12130 var p12 = (p2 - p1) * t + p1; 12131 var p23 = (p3 - p2) * t + p2; 12132 12133 var p012 = (p12 - p01) * t + p01; 12134 var p123 = (p23 - p12) * t + p12; 12135 12136 var p0123 = (p123 - p012) * t + p012; 12137 // Seg0 12138 out[0] = p0; 12139 out[1] = p01; 12140 out[2] = p012; 12141 out[3] = p0123; 12142 // Seg1 12143 out[4] = p0123; 12144 out[5] = p123; 12145 out[6] = p23; 12146 out[7] = p3; 12147 } 12148 12149 /** 12150 * é¶æç éç°åæ¶å¤î¼çæ¿î£çææ´¸ç»¾å¤¸ç¬éå²ç¹é¥ç´å§çå®çªç»æ±â¬ï¿½ 12151 * é¶æç éè§æ¹éîå æµ¼æ°æ¹æ¶â¬æ¶î å¨é°åî¿æ¶îç´æ©æ¬å·éîç¹é¥ç²å¾æ¶î ¡çªç»ç»æ¸¶éî æ®æ¶â¬æ¶îâ¬ï¿½ 12152 * @param {number} x0 12153 * @param {number} y0 12154 * @param {number} x1 12155 * @param {number} y1 12156 * @param {number} x2 12157 * @param {number} y2 12158 * @param {number} x3 12159 * @param {number} y3 12160 * @param {number} x 12161 * @param {number} y 12162 * @param {Array.<number>} [out] é¶æç é� 12163 * @return {number} 12164 */ 12165 function cubicProjectPoint( 12166 x0, y0, x1, y1, x2, y2, x3, y3, 12167 x, y, out 12168 ) { 12169 // http://pomax.github.io/bezierinfo/#projections 12170 var t; 12171 var interval = 0.005; 12172 var d = Infinity; 12173 var prev; 12174 var next; 12175 var d1; 12176 var d2; 12177 12178 _v0[0] = x; 12179 _v0[1] = y; 12180 12181 // éå ¢çé£ã¤åçâ²ç«´æ¶å¬ªå½²é³çæ®éâ¬çå¿çªç»è¤æ® t é� 12182 // PENDING 12183 for (var _t = 0; _t < 1; _t += 0.05) { 12184 _v1[0] = cubicAt(x0, x1, x2, x3, _t); 12185 _v1[1] = cubicAt(y0, y1, y2, y3, _t); 12186 d1 = distSquare(_v0, _v1); 12187 if (d1 < d) { 12188 t = _t; 12189 d = d1; 12190 } 12191 } 12192 d = Infinity; 12193 12194 // At most 32 iteration 12195 for (var i = 0; i < 32; i++) { 12196 if (interval < EPSILON_NUMERIC) { 12197 break; 12198 } 12199 prev = t - interval; 12200 next = t + interval; 12201 // t - interval 12202 _v1[0] = cubicAt(x0, x1, x2, x3, prev); 12203 _v1[1] = cubicAt(y0, y1, y2, y3, prev); 12204 12205 d1 = distSquare(_v1, _v0); 12206 12207 if (prev >= 0 && d1 < d) { 12208 t = prev; 12209 d = d1; 12210 } 12211 else { 12212 // t + interval 12213 _v2[0] = cubicAt(x0, x1, x2, x3, next); 12214 _v2[1] = cubicAt(y0, y1, y2, y3, next); 12215 d2 = distSquare(_v2, _v0); 12216 12217 if (next <= 1 && d2 < d) { 12218 t = next; 12219 d = d2; 12220 } 12221 else { 12222 interval *= 0.5; 12223 } 12224 } 12225 } 12226 // t 12227 if (out) { 12228 out[0] = cubicAt(x0, x1, x2, x3, t); 12229 out[1] = cubicAt(y0, y1, y2, y3, t); 12230 } 12231 // console.log(interval, i); 12232 return mathSqrt$2(d); 12233 } 12234 12235 /** 12236 * çï¼ç»æµå±¾î¼éç¡ ç¤æ¿ç²çµé� 12237 * @param {number} p0 12238 * @param {number} p1 12239 * @param {number} p2 12240 * @param {number} t 12241 * @return {number} 12242 */ 12243 function quadraticAt(p0, p1, p2, t) { 12244 var onet = 1 - t; 12245 return onet * (onet * p0 + 2 * t * p1) + t * t * p2; 12246 } 12247 12248 /** 12249 * çï¼ç»æµå±¾î¼éç¡ ç¤æ¿ç²çµçµå ¼æé� 12250 * @param {number} p0 12251 * @param {number} p1 12252 * @param {number} p2 12253 * @param {number} t 12254 * @return {number} 12255 */ 12256 function quadraticDerivativeAt(p0, p1, p2, t) { 12257 return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1)); 12258 } 12259 12260 /** 12261 * çï¼ç»æµå±¾î¼éç¡ ç¤æ¿ç²çµéåâ¼é� 12262 * @param {number} p0 12263 * @param {number} p1 12264 * @param {number} p2 12265 * @param {number} t 12266 * @param {Array.<number>} roots 12267 * @return {number} é夿¥éè§æé©ï¿½ 12268 */ 12269 function quadraticRootAt(p0, p1, p2, val, roots) { 12270 var a = p0 - 2 * p1 + p2; 12271 var b = 2 * (p1 - p0); 12272 var c = p0 - val; 12273 12274 var n = 0; 12275 if (isAroundZero(a)) { 12276 if (isNotAroundZero$1(b)) { 12277 var t1 = -c / b; 12278 if (t1 >= 0 && t1 <= 1) { 12279 roots[n++] = t1; 12280 } 12281 } 12282 } 12283 else { 12284 var disc = b * b - 4 * a * c; 12285 if (isAroundZero(disc)) { 12286 var t1 = -b / (2 * a); 12287 if (t1 >= 0 && t1 <= 1) { 12288 roots[n++] = t1; 12289 } 12290 } 12291 else if (disc > 0) { 12292 var discSqrt = mathSqrt$2(disc); 12293 var t1 = (-b + discSqrt) / (2 * a); 12294 var t2 = (-b - discSqrt) / (2 * a); 12295 if (t1 >= 0 && t1 <= 1) { 12296 roots[n++] = t1; 12297 } 12298 if (t2 >= 0 && t2 <= 1) { 12299 roots[n++] = t2; 12300 } 12301 } 12302 } 12303 return n; 12304 } 12305 12306 /** 12307 * çï¼ç»æµå±¾î¼çæ¿î£çææç»å¬«ç¬éæ¬â¬ï¿½ 12308 * @memberOf module:zrender/core/curve 12309 * @param {number} p0 12310 * @param {number} p1 12311 * @param {number} p2 12312 * @return {number} 12313 */ 12314 function quadraticExtremum(p0, p1, p2) { 12315 var divider = p0 + p2 - 2 * p1; 12316 if (divider === 0) { 12317 // p1 is center of p0 and p2 12318 return 0.5; 12319 } 12320 else { 12321 return (p0 - p1) / divider; 12322 } 12323 } 12324 12325 /** 12326 * ç¼å念屾î¼çæ¿î£çææ´¸ç»¾ï¿½ 12327 * @memberOf module:zrender/core/curve 12328 * @param {number} p0 12329 * @param {number} p1 12330 * @param {number} p2 12331 * @param {number} t 12332 * @param {Array.<number>} out 12333 */ 12334 function quadraticSubdivide(p0, p1, p2, t, out) { 12335 var p01 = (p1 - p0) * t + p0; 12336 var p12 = (p2 - p1) * t + p1; 12337 var p012 = (p12 - p01) * t + p01; 12338 12339 // Seg0 12340 out[0] = p0; 12341 out[1] = p01; 12342 out[2] = p012; 12343 12344 // Seg1 12345 out[3] = p012; 12346 out[4] = p12; 12347 out[5] = p2; 12348 } 12349 12350 /** 12351 * é¶æç éç°åæµå±¾î¼çæ¿î£çææ´¸ç»¾å¤¸ç¬éå²ç¹é¥ç´å§çå®çªç»æ±â¬ï¿½ 12352 * é¶æç éè§æ¹éîå æµ¼æ°æ¹æ¶â¬æ¶î å¨é°åî¿æ¶îç´æ©æ¬å·éîç¹é¥ç²å¾æ¶î ¡çªç»ç»æ¸¶éî æ®æ¶â¬æ¶îâ¬ï¿½ 12353 * @param {number} x0 12354 * @param {number} y0 12355 * @param {number} x1 12356 * @param {number} y1 12357 * @param {number} x2 12358 * @param {number} y2 12359 * @param {number} x 12360 * @param {number} y 12361 * @param {Array.<number>} out é¶æç é� 12362 * @return {number} 12363 */ 12364 function quadraticProjectPoint( 12365 x0, y0, x1, y1, x2, y2, 12366 x, y, out 12367 ) { 12368 // http://pomax.github.io/bezierinfo/#projections 12369 var t; 12370 var interval = 0.005; 12371 var d = Infinity; 12372 12373 _v0[0] = x; 12374 _v0[1] = y; 12375 12376 // éå ¢çé£ã¤åçâ²ç«´æ¶å¬ªå½²é³çæ®éâ¬çå¿çªç»è¤æ® t é� 12377 // PENDING 12378 for (var _t = 0; _t < 1; _t += 0.05) { 12379 _v1[0] = quadraticAt(x0, x1, x2, _t); 12380 _v1[1] = quadraticAt(y0, y1, y2, _t); 12381 var d1 = distSquare(_v0, _v1); 12382 if (d1 < d) { 12383 t = _t; 12384 d = d1; 12385 } 12386 } 12387 d = Infinity; 12388 12389 // At most 32 iteration 12390 for (var i = 0; i < 32; i++) { 12391 if (interval < EPSILON_NUMERIC) { 12392 break; 12393 } 12394 var prev = t - interval; 12395 var next = t + interval; 12396 // t - interval 12397 _v1[0] = quadraticAt(x0, x1, x2, prev); 12398 _v1[1] = quadraticAt(y0, y1, y2, prev); 12399 12400 var d1 = distSquare(_v1, _v0); 12401 12402 if (prev >= 0 && d1 < d) { 12403 t = prev; 12404 d = d1; 12405 } 12406 else { 12407 // t + interval 12408 _v2[0] = quadraticAt(x0, x1, x2, next); 12409 _v2[1] = quadraticAt(y0, y1, y2, next); 12410 var d2 = distSquare(_v2, _v0); 12411 if (next <= 1 && d2 < d) { 12412 t = next; 12413 d = d2; 12414 } 12415 else { 12416 interval *= 0.5; 12417 } 12418 } 12419 } 12420 // t 12421 if (out) { 12422 out[0] = quadraticAt(x0, x1, x2, t); 12423 out[1] = quadraticAt(y0, y1, y2, t); 12424 } 12425 // console.log(interval, i); 12426 return mathSqrt$2(d); 12427 } 12428 12429 /** 12430 * @author Yi Shen(https://github.com/pissang) 12431 */ 12432 12433 var mathMin$3 = Math.min; 12434 var mathMax$3 = Math.max; 12435 var mathSin$2 = Math.sin;
vendor: 5,463 bytes, lines 12436-12608
12436 var mathCos$2 = Math.cos; 12437 var PI2 = Math.PI * 2; 12438 12439 var start = create(); 12440 var end = create(); 12441 var extremity = create(); 12442 12443 /** 12444 * æµ åº¨ãéè§æç¼åªè çï¼ç»éçæ¸¶çå¿å¯é¥å¯¸æ´ éå±½åéî¦min`éå®max`æ¶ï¿½ 12445 * @module zrender/core/bbox 12446 * @param {Array<Object>} points 椤åå£éæ®ç² 12447 * @param {number} min 12448 * @param {number} max 12449 */ 12450 function fromPoints(points, min$$1, max$$1) { 12451 if (points.length === 0) { 12452 return; 12453 } 12454 var p = points[0]; 12455 var left = p[0]; 12456 var right = p[0]; 12457 var top = p[1]; 12458 var bottom = p[1]; 12459 var i; 12460 12461 for (i = 1; i < points.length; i++) { 12462 p = points[i]; 12463 left = mathMin$3(left, p[0]); 12464 right = mathMax$3(right, p[0]); 12465 top = mathMin$3(top, p[1]); 12466 bottom = mathMax$3(bottom, p[1]); 12467 } 12468 12469 min$$1[0] = left; 12470 min$$1[1] = top; 12471 max$$1[0] = right; 12472 max$$1[1] = bottom; 12473 } 12474 12475 /** 12476 * @memberOf module:zrender/core/bbox 12477 * @param {number} x0 12478 * @param {number} y0 12479 * @param {number} x1 12480 * @param {number} y1 12481 * @param {Array.<number>} min 12482 * @param {Array.<number>} max 12483 */ 12484 function fromLine(x0, y0, x1, y1, min$$1, max$$1) { 12485 min$$1[0] = mathMin$3(x0, x1); 12486 min$$1[1] = mathMin$3(y0, y1); 12487 max$$1[0] = mathMax$3(x0, x1); 12488 max$$1[1] = mathMax$3(y0, y1); 12489 } 12490 12491 var xDim = []; 12492 var yDim = []; 12493 /** 12494 * æµ åºç¬éæ°ç¤æ¿ç²çµéè¬å(p0, p1, p2, p3)æ¶î ¡î ¸ç» æ¥åéâ¬çå¿å¯é¥å¯¸æ´ éå±½åéî¦min`éå®max`æ¶ï¿½ 12495 * @memberOf module:zrender/core/bbox 12496 * @param {number} x0 12497 * @param {number} y0 12498 * @param {number} x1 12499 * @param {number} y1 12500 * @param {number} x2 12501 * @param {number} y2 12502 * @param {number} x3 12503 * @param {number} y3 12504 * @param {Array.<number>} min 12505 * @param {Array.<number>} max 12506 */ 12507 function fromCubic( 12508 x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1 12509 ) { 12510 var cubicExtrema$$1 = cubicExtrema; 12511 var cubicAt$$1 = cubicAt; 12512 var i; 12513 var n = cubicExtrema$$1(x0, x1, x2, x3, xDim); 12514 min$$1[0] = Infinity; 12515 min$$1[1] = Infinity; 12516 max$$1[0] = -Infinity; 12517 max$$1[1] = -Infinity; 12518 12519 for (i = 0; i < n; i++) { 12520 var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]); 12521 min$$1[0] = mathMin$3(x, min$$1[0]); 12522 max$$1[0] = mathMax$3(x, max$$1[0]); 12523 } 12524 n = cubicExtrema$$1(y0, y1, y2, y3, yDim); 12525 for (i = 0; i < n; i++) { 12526 var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]); 12527 min$$1[1] = mathMin$3(y, min$$1[1]); 12528 max$$1[1] = mathMax$3(y, max$$1[1]); 12529 } 12530 12531 min$$1[0] = mathMin$3(x0, min$$1[0]); 12532 max$$1[0] = mathMax$3(x0, max$$1[0]); 12533 min$$1[0] = mathMin$3(x3, min$$1[0]); 12534 max$$1[0] = mathMax$3(x3, max$$1[0]); 12535 12536 min$$1[1] = mathMin$3(y0, min$$1[1]); 12537 max$$1[1] = mathMax$3(y0, max$$1[1]); 12538 min$$1[1] = mathMin$3(y3, min$$1[1]); 12539 max$$1[1] = mathMax$3(y3, max$$1[1]); 12540 } 12541 12542 /** 12543 * æµ åºç°©éæ°ç¤æ¿ç²çµéè¬å(p0, p1, p2)æ¶î ¡î ¸ç» æ¥åéâ¬çå¿å¯é¥å¯¸æ´ éå±½åéî¦min`éå®max`æ¶ï¿½ 12544 * @memberOf module:zrender/core/bbox 12545 * @param {number} x0 12546 * @param {number} y0 12547 * @param {number} x1 12548 * @param {number} y1 12549 * @param {number} x2 12550 * @param {number} y2 12551 * @param {Array.<number>} min 12552 * @param {Array.<number>} max 12553 */ 12554 function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) { 12555 var quadraticExtremum$$1 = quadraticExtremum; 12556 var quadraticAt$$1 = quadraticAt; 12557 // Find extremities, where derivative in x dim or y dim is zero 12558 var tx = 12559 mathMax$3( 12560 mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0 12561 ); 12562 var ty = 12563 mathMax$3( 12564 mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0 12565 ); 12566 12567 var x = quadraticAt$$1(x0, x1, x2, tx); 12568 var y = quadraticAt$$1(y0, y1, y2, ty); 12569 12570 min$$1[0] = mathMin$3(x0, x2, x); 12571 min$$1[1] = mathMin$3(y0, y2, y); 12572 max$$1[0] = mathMax$3(x0, x2, x); 12573 max$$1[1] = mathMax$3(y0, y2, y); 12574 } 12575 12576 /** 12577 * æµ åº¡æ¸¾å¯®Ñè çï¼ç»éçæ¸¶çå¿å¯é¥å¯¸æ´ éå±½åéî¦min`éå®max`æ¶ï¿½ 12578 * @method 12579 * @memberOf module:zrender/core/bbox 12580 * @param {number} x 12581 * @param {number} y 12582 * @param {number} rx 12583 * @param {number} ry 12584 * @param {number} startAngle 12585 * @param {number} endAngle 12586 * @param {number} anticlockwise 12587 * @param {Array.<number>} min 12588 * @param {Array.<number>} max 12589 */ 12590 function fromArc( 12591 x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1 12592 ) { 12593 var vec2Min = min; 12594 var vec2Max = max; 12595 12596 var diff = Math.abs(startAngle - endAngle); 12597 12598 12599 if (diff % PI2 < 1e-4 && diff > 1e-4) { 12600 // Is a circle 12601 min$$1[0] = x - rx; 12602 min$$1[1] = y - ry; 12603 max$$1[0] = x + rx; 12604 max$$1[1] = y + ry; 12605 return; 12606 } 12607 12608 start[0] = mathCos$2(startAngle) * rx + x;
12609 start[1] = mathSin$2(startAngle) * ry + y; 12610 12611 end[0] = mathCos$2(endAngle) * rx + x; 12612 end[1] = mathSin$2(endAngle) * ry + y; 12613 12614 vec2Min(min$$1, start, end); 12615 vec2Max(max$$1, start, end); 12616 12617 // Thresh to [0, Math.PI * 2] 12618 startAngle = startAngle % (PI2); 12619 if (startAngle < 0) { 12620 startAngle = startAngle + PI2; 12621 } 12622 endAngle = endAngle % (PI2); 12623 if (endAngle < 0) { 12624 endAngle = endAngle + PI2; 12625 } 12626 12627 if (startAngle > endAngle && !anticlockwise) { 12628 endAngle += PI2; 12629 } 12630 else if (startAngle < endAngle && anticlockwise) { 12631 startAngle += PI2; 12632 } 12633 if (anticlockwise) { 12634 var tmp = endAngle; 12635 endAngle = startAngle; 12636 startAngle = tmp; 12637 } 12638 12639 // var number = 0; 12640 // var step = (anticlockwise ? -Math.PI : Math.PI) / 2; 12641 for (var angle = 0; angle < endAngle; angle += Math.PI / 2) { 12642 if (angle > startAngle) { 12643 extremity[0] = mathCos$2(angle) * rx + x; 12644 extremity[1] = mathSin$2(angle) * ry + y; 12645 12646 vec2Min(min$$1, extremity, min$$1); 12647 vec2Max(max$$1, extremity, max$$1); 12648 } 12649 } 12650 } 12651 12652 /** 12653 * Path æµ ï½æéå±½å½²æµ ã¥æ¹ª`buildPath`æ¶î æ¤æµåº¢æµæµ î¦ctx`, 浼氫ç¹çæ¨»ç¡æ¶çµathé¿å¶ç¶é¨å«æ¡æµ ã åpathCommandsçç´â¬Ñè 12654 * éîäºé¢ã¤ç°¬ isInsidePath éãææµ ã¥å¼·é¾å³°å½boundingRect 12655 * 12656 * @module zrender/core/PathProxy 12657 * @author Yi Shen (http://www.github.com/pissang) 12658 */ 12659 12660// TODO getTotalLength, getPointAtLength 12661 12662 var CMD = { 12663 M: 1, 12664 L: 2, 12665 C: 3, 12666 Q: 4, 12667 A: 5, 12668 Z: 6, 12669 // Rect 12670 R: 7 12671 }; 12672 12673// var CMD_MEM_SIZE = { 12674// M: 3, 12675// L: 3, 12676// C: 7, 12677// Q: 5, 12678// A: 9, 12679// R: 5, 12680// Z: 1 12681// }; 12682 12683 var min$1 = []; 12684 var max$1 = []; 12685 var min2 = []; 12686 var max2 = []; 12687 var mathMin$2 = Math.min; 12688 var mathMax$2 = Math.max; 12689 var mathCos$1 = Math.cos; 12690 var mathSin$1 = Math.sin; 12691 var mathSqrt$1 = Math.sqrt; 12692 var mathAbs = Math.abs; 12693 12694 var hasTypedArray = typeof Float32Array != 'undefined'; 12695 12696 /** 12697 * @alias module:zrender/core/PathProxy 12698 * @constructor 12699 */ 12700 var PathProxy = function (notSaveData) { 12701 12702 this._saveData = !(notSaveData || false); 12703 12704 if (this._saveData) { 12705 /** 12706 * Path data. Stored as flat array 12707 * @type {Array.<Object>} 12708 */ 12709 this.data = []; 12710 } 12711 12712 this._ctx = null; 12713 }; 12714 12715 /** 12716 * è¹î¦â¬ç»î ¸ç» 桺athéåæ´¿é©æç´éªæµç¬éîæ¸¶çå¿å¯é¥å¯¸æ´ é� 12717 * @return {Object} 12718 */ 12719 PathProxy.prototype = { 12720 12721 constructor: PathProxy, 12722 12723 _xi: 0, 12724 _yi: 0, 12725 12726 _x0: 0, 12727 _y0: 0, 12728 // Unit x, Unit y. Provide for avoiding drawing that too short line segment 12729 _ux: 0, 12730 _uy: 0, 12731 12732 _len: 0, 12733 12734 _lineDash: null, 12735 12736 _dashOffset: 0, 12737 12738 _dashIdx: 0, 12739 12740 _dashSum: 0, 12741 12742 /** 12743 * @readOnly 12744 */ 12745 setScale: function (sx, sy) { 12746 this._ux = mathAbs(1 / devicePixelRatio / sx) || 0; 12747 this._uy = mathAbs(1 / devicePixelRatio / sy) || 0; 12748 }, 12749 12750 getContext: function () { 12751 return this._ctx; 12752 }, 12753 12754 /** 12755 * @param {CanvasRenderingContext2D} ctx 12756 * @return {module:zrender/core/PathProxy} 12757 */ 12758 beginPath: function (ctx) { 12759 12760 this._ctx = ctx; 12761 12762 ctx && ctx.beginPath(); 12763 12764 ctx && (this.dpr = ctx.dpr); 12765 12766 // Reset 12767 if (this._saveData) { 12768 this._len = 0; 12769 } 12770 12771 if (this._lineDash) { 12772 this._lineDash = null; 12773 12774 this._dashOffset = 0; 12775 } 12776 12777 return this; 12778 }, 12779 12780 /** 12781 * @param {number} x 12782 * @param {number} y 12783 * @return {module:zrender/core/PathProxy} 12784 */ 12785 moveTo: function (x, y) { 12786 this.addData(CMD.M, x, y); 12787 this._ctx && this._ctx.moveTo(x, y); 12788 12789 // x0, y0, xi, yi éîîè¤°ææ¹ª _dashedXXXXTo éè§ç¡¶æ¶î å¨é¢ï¿½ 12790 // xi, yi çæ¿ç¶è¤°æ³å¢ é�, x0, y0 é¦ï¿½ closePath é¨å¬æ¤éæ¬æ´éææ£æ¿®å¬¬å£é� 12791 // éå¤å½²é³è湪 beginPath æ¶å¬ªæé©å帴çåªæ¤ lineToéå²ç¹éè·ºâ¬ï¿½ x0, y0 éâ¬ç� 12792 // é¦ï¿½ lineTo éè§ç¡¶æ¶î ¡î褰æªç´æ©æ¬å·éå ç¬é°åæª»æ©æ¬îé¯ååéå®ashed line æ¶ç·å½§é¦ï¿½ IE10- æ¶î ç¬éîå¯ 12793 this._x0 = x; 12794 this._y0 = y; 12795 12796 this._xi = x; 12797 this._yi = y; 12798 12799 return this; 12800 }, 12801 12802 /** 12803 * @param {number} x 12804 * @param {number} y 12805 * @return {module:zrender/core/PathProxy} 12806 */ 12807 lineTo: function (x, y) { 12808 var exceedUnit = mathAbs(x - this._xi) > this._ux 12809 || mathAbs(y - this._yi) > this._uy 12810 // Force draw the first segment 12811 || this._len < 5; 12812 12813 this.addData(CMD.L, x, y); 12814 12815 if (this._ctx && exceedUnit) { 12816 this._needsDash() ? this._dashedLineTo(x, y) 12817 : this._ctx.lineTo(x, y); 12818 } 12819 if (exceedUnit) { 12820 this._xi = x; 12821 this._yi = y; 12822 } 12823 12824 return this; 12825 }, 12826 12827 /** 12828 * @param {number} x1 12829 * @param {number} y1 12830 * @param {number} x2 12831 * @param {number} y2 12832 * @param {number} x3 12833 * @param {number} y3 12834 * @return {module:zrender/core/PathProxy} 12835 */ 12836 bezierCurveTo: function (x1, y1, x2, y2, x3, y3) { 12837 this.addData(CMD.C, x1, y1, x2, y2, x3, y3); 12838 if (this._ctx) { 12839 this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) 12840 : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); 12841 } 12842 this._xi = x3; 12843 this._yi = y3; 12844 return this; 12845 }, 12846 12847 /** 12848 * @param {number} x1 12849 * @param {number} y1 12850 * @param {number} x2 12851 * @param {number} y2 12852 * @return {module:zrender/core/PathProxy} 12853 */ 12854 quadraticCurveTo: function (x1, y1, x2, y2) { 12855 this.addData(CMD.Q, x1, y1, x2, y2); 12856 if (this._ctx) { 12857 this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) 12858 : this._ctx.quadraticCurveTo(x1, y1, x2, y2); 12859 } 12860 this._xi = x2; 12861 this._yi = y2; 12862 return this; 12863 }, 12864 12865 /** 12866 * @param {number} cx 12867 * @param {number} cy 12868 * @param {number} r 12869 * @param {number} startAngle 12870 * @param {number} endAngle 12871 * @param {boolean} anticlockwise 12872 * @return {module:zrender/core/PathProxy} 12873 */ 12874 arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) { 12875 this.addData( 12876 CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1 12877 ); 12878 this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise); 12879 12880 this._xi = mathCos$1(endAngle) * r + cx;
12881 this._yi = mathSin$1(endAngle) * r + cx; 12882 return this; 12883 }, 12884 12885 // TODO 12886 arcTo: function (x1, y1, x2, y2, radius) { 12887 if (this._ctx) { 12888 this._ctx.arcTo(x1, y1, x2, y2, radius); 12889 } 12890 return this; 12891 }, 12892 12893 // TODO 12894 rect: function (x, y, w, h) { 12895 this._ctx && this._ctx.rect(x, y, w, h); 12896 this.addData(CMD.R, x, y, w, h); 12897 return this; 12898 }, 12899 12900 /** 12901 * @return {module:zrender/core/PathProxy} 12902 */ 12903 closePath: function () { 12904 this.addData(CMD.Z); 12905 12906 var ctx = this._ctx; 12907 var x0 = this._x0; 12908 var y0 = this._y0; 12909 if (ctx) { 12910 this._needsDash() && this._dashedLineTo(x0, y0); 12911 ctx.closePath(); 12912 } 12913 12914 this._xi = x0; 12915 this._yi = y0; 12916 return this; 12917 }, 12918 12919 /** 12920 * Context æµ åº¡î»é®ã¤ç´¶éã¯ç´é¥ç±è´éå¤å½²é³è¥æ§¸ rebuildPath ç¹å±¼ç®£é庡å fillé� 12921 * stroke éå±¾ç± 12922 * @param {CanvasRenderingContext2D} ctx 12923 * @return {module:zrender/core/PathProxy} 12924 */ 12925 fill: function (ctx) { 12926 ctx && ctx.fill(); 12927 this.toStatic(); 12928 }, 12929 12930 /** 12931 * @param {CanvasRenderingContext2D} ctx 12932 * @return {module:zrender/core/PathProxy} 12933 */ 12934 stroke: function (ctx) { 12935 ctx && ctx.stroke(); 12936 this.toStatic(); 12937 }, 12938 12939 /** 12940 * è¹å´ãé¦ã¥å¾ç¹åªç²¯éè·ºæ¡æµ ã å¢ çåªæ¤ 12941 * Must be invoked before all other path drawing methods 12942 * @return {module:zrender/core/PathProxy} 12943 */ 12944 setLineDash: function (lineDash) { 12945 if (lineDash instanceof Array) { 12946 this._lineDash = lineDash; 12947 12948 this._dashIdx = 0; 12949 12950 var lineDashSum = 0; 12951 for (var i = 0; i < lineDash.length; i++) { 12952 lineDashSum += lineDash[i]; 12953 } 12954 this._dashSum = lineDashSum; 12955 } 12956 return this; 12957 }, 12958 12959 /** 12960 * è¹å´ãé¦ã¥å¾ç¹åªç²¯éè·ºæ¡æµ ã å¢ çåªæ¤ 12961 * Must be invoked before all other path drawing methods 12962 * @return {module:zrender/core/PathProxy} 12963 */ 12964 setLineDashOffset: function (offset) { 12965 this._dashOffset = offset; 12966 return this; 12967 }, 12968 12969 /** 12970 * 12971 * @return {boolean} 12972 */ 12973 len: function () { 12974 return this._len; 12975 }, 12976 12977 /** 12978 * é©å帴çå§ç Path éçåµ 12979 */ 12980 setData: function (data) { 12981 12982 var len$$1 = data.length; 12983 12984 if (! (this.data && this.data.length == len$$1) && hasTypedArray) { 12985 this.data = new Float32Array(len$$1); 12986 } 12987 12988 for (var i = 0; i < len$$1; i++) { 12989 this.data[i] = data[i]; 12990 } 12991 12992 this._len = len$$1; 12993 }, 12994 12995 /** 12996 * 娣诲å§çæ¯ç¾å¯°ï¿½ 12997 * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path 12998 */ 12999 appendPath: function (path) { 13000 if (!(path instanceof Array)) { 13001 path = [path]; 13002 } 13003 var len$$1 = path.length; 13004 var appendSize = 0; 13005 var offset = this._len; 13006 for (var i = 0; i < len$$1; i++) { 13007 appendSize += path[i].len(); 13008 } 13009 if (hasTypedArray && (this.data instanceof Float32Array)) { 13010 this.data = new Float32Array(offset + appendSize); 13011 } 13012 for (var i = 0; i < len$$1; i++) { 13013 var appendPathData = path[i].data; 13014 for (var k = 0; k < appendPathData.length; k++) { 13015 this.data[offset++] = appendPathData[k]; 13016 } 13017 } 13018 this._len = offset; 13019 }, 13020 13021 /** 13022 * æ¿î¢å Path éçåµé� 13023 * çä»åºæ¾¶å¶æ¤é°å±¼ç¬é¢è«æ§éæ®æ®éæ®ç²éåãé®ã¥å饿§è°é²å¶ç²¯é¨å¬å¯æµ ãæé¹îæ±æ´ï¹å éîç¬é樼æ®é� 13024 */ 13025 addData: function (cmd) { 13026 if (!this._saveData) { 13027 return; 13028 } 13029 13030 var data = this.data; 13031 if (this._len + arguments.length > data.length) { 13032 // é¥ç±è´æ¶å¬ªå¢ é¨å¬æç¼å«å¡ç¼å¿æµé¹ã¡å鿬â¬ä½ºæ® Float32Array 13033 // éµâ¬æµ ã¤ç¬æ¾¶çºæ¤éå æ¸¶ç佹墿çæç«´æ¶î æé¨å«å§©é¬ä½¹æç¼ï¿½ 13034 this._expandData(); 13035 data = this.data; 13036 } 13037 for (var i = 0; i < arguments.length; i++) { 13038 data[this._len++] = arguments[i]; 13039 } 13040 13041 this._prevCmd = cmd; 13042 }, 13043 13044 _expandData: function () { 13045 // Only if data is Float32Array 13046 if (!(this.data instanceof Array)) { 13047 var newData = []; 13048 for (var i = 0; i < this._len; i++) { 13049 newData[i] = this.data[i]; 13050 } 13051 this.data = newData; 13052 } 13053 }, 13054 13055 /** 13056 * If needs js implemented dashed line 13057 * @return {boolean} 13058 * @private 13059 */ 13060 _needsDash: function () { 13061 return this._lineDash; 13062 }, 13063 13064 _dashedLineTo: function (x1, y1) { 13065 var dashSum = this._dashSum; 13066 var offset = this._dashOffset; 13067 var lineDash = this._lineDash; 13068 var ctx = this._ctx; 13069 13070 var x0 = this._xi; 13071 var y0 = this._yi; 13072 var dx = x1 - x0; 13073 var dy = y1 - y0; 13074 var dist$$1 = mathSqrt$1(dx * dx + dy * dy); 13075 var x = x0; 13076 var y = y0; 13077 var dash; 13078 var nDash = lineDash.length; 13079 var idx; 13080 dx /= dist$$1; 13081 dy /= dist$$1; 13082 13083 if (offset < 0) { 13084 // Convert to positive offset 13085 offset = dashSum + offset; 13086 } 13087 offset %= dashSum; 13088 x -= offset * dx; 13089 y -= offset * dy; 13090 13091 while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1) 13092 || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) { 13093 idx = this._dashIdx; 13094 dash = lineDash[idx]; 13095 x += dx * dash; 13096 y += dy * dash; 13097 this._dashIdx = (idx + 1) % nDash; 13098 // Skip positive offset 13099 if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) { 13100 continue; 13101 } 13102 ctx[idx % 2 ? 'moveTo' : 'lineTo']( 13103 dx >= 0 ? mathMin$2(x, x1) : mathMax$2(x, x1), 13104 dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1) 13105 ); 13106 } 13107 // Offset for next lineTo 13108 dx = x - x1; 13109 dy = y - y1; 13110 this._dashOffset = -mathSqrt$1(dx * dx + dy * dy); 13111 }, 13112 13113 // Not accurate dashed line to 13114 _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) { 13115 var dashSum = this._dashSum; 13116 var offset = this._dashOffset; 13117 var lineDash = this._lineDash; 13118 var ctx = this._ctx; 13119 13120 var x0 = this._xi; 13121 var y0 = this._yi; 13122 var t; 13123 var dx; 13124 var dy; 13125 var cubicAt$$1 = cubicAt; 13126 var bezierLen = 0; 13127 var idx = this._dashIdx; 13128 var nDash = lineDash.length; 13129 13130 var x; 13131 var y; 13132 13133 var tmpLen = 0; 13134 13135 if (offset < 0) { 13136 // Convert to positive offset 13137 offset = dashSum + offset; 13138 } 13139 offset %= dashSum; 13140 // Bezier approx length 13141 for (t = 0; t < 1; t += 0.1) { 13142 dx = cubicAt$$1(x0, x1, x2, x3, t + 0.1) 13143 - cubicAt$$1(x0, x1, x2, x3, t); 13144 dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1) 13145 - cubicAt$$1(y0, y1, y2, y3, t); 13146 bezierLen += mathSqrt$1(dx * dx + dy * dy); 13147 } 13148 13149 // Find idx after add offset 13150 for (; idx < nDash; idx++) { 13151 tmpLen += lineDash[idx]; 13152 if (tmpLen > offset) { 13153 break; 13154 } 13155 } 13156 t = (tmpLen - offset) / bezierLen; 13157 13158 while (t <= 1) { 13159 13160 x = cubicAt$$1(x0, x1, x2, x3, t); 13161 y = cubicAt$$1(y0, y1, y2, y3, t); 13162 13163 // Use line to approximate dashed bezier 13164 // Bad result if dash is long 13165 idx % 2 ? ctx.moveTo(x, y) 13166 : ctx.lineTo(x, y); 13167 13168 t += lineDash[idx] / bezierLen; 13169 13170 idx = (idx + 1) % nDash; 13171 } 13172
13173 // Finish the last segment and calculate the new offset 13174 (idx % 2 !== 0) && ctx.lineTo(x3, y3); 13175 dx = x3 - x; 13176 dy = y3 - y; 13177 this._dashOffset = -mathSqrt$1(dx * dx + dy * dy); 13178 }, 13179 13180 _dashedQuadraticTo: function (x1, y1, x2, y2) { 13181 // Convert quadratic to cubic using degree elevation 13182 var x3 = x2; 13183 var y3 = y2; 13184 x2 = (x2 + 2 * x1) / 3; 13185 y2 = (y2 + 2 * y1) / 3; 13186 x1 = (this._xi + 2 * x1) / 3; 13187 y1 = (this._yi + 2 * y1) / 3; 13188 13189 this._dashedBezierTo(x1, y1, x2, y2, x3, y3); 13190 }, 13191 13192 /** 13193 * æîå鿬â¬ä½ºæ® Float32Array éå¿ç¯é«åå´ç樺崰é¢ï¿½ 13194 * Convert dynamic array to static Float32Array 13195 */ 13196 toStatic: function () { 13197 var data = this.data; 13198 if (data instanceof Array) { 13199 data.length = this._len; 13200 if (hasTypedArray) { 13201 this.data = new Float32Array(data); 13202 } 13203 } 13204 }, 13205 13206 /** 13207 * @return {module:zrender/core/BoundingRect} 13208 */ 13209 getBoundingRect: function () { 13210 min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE; 13211 max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE; 13212 13213 var data = this.data; 13214 var xi = 0; 13215 var yi = 0; 13216 var x0 = 0; 13217 var y0 = 0; 13218 13219 for (var i = 0; i < data.length;) { 13220 var cmd = data[i++]; 13221 13222 if (i == 1) { 13223 // æ¿¡åçç»î¿ç«´æ¶îæ¡æµ ãæ§¸ L, C, Q 13224 // é� previous point éå²ç²¯éè·ºæ¡æµ ãæ®ç»î¿ç«´æ¶ï¿½ point 13225 // 13226 // ç»î¿ç«´æ¶îæ¡æµ ãè´ Arc é¨å¬åéå ¸ç¬ æµ¼æ°¬æ¹ªé庨潰éè§ç©æ¾¶åæ 13227 xi = data[i]; 13228 yi = data[i + 1]; 13229 13230 x0 = xi; 13231 y0 = yi; 13232 } 13233 13234 switch (cmd) { 13235 case CMD.M: 13236 // moveTo éææ¤é²å¶æéæ¶ç¼æ¶â¬æ¶î æé¨ï¿½ subpath, éªæµç¬éåæéæ®æ®ç§é£å£ 13237 // é¦ï¿½ closePath é¨å¬æ¤éæ¬å¨é¢ï¿½ 13238 x0 = data[i++]; 13239 y0 = data[i++]; 13240 xi = x0; 13241 yi = y0; 13242 min2[0] = x0; 13243 min2[1] = y0; 13244 max2[0] = x0; 13245 max2[1] = y0; 13246 break; 13247 case CMD.L: 13248 fromLine(xi, yi, data[i], data[i + 1], min2, max2); 13249 xi = data[i++]; 13250 yi = data[i++]; 13251 break; 13252 case CMD.C: 13253 fromCubic( 13254 xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], 13255 min2, max2 13256 ); 13257 xi = data[i++]; 13258 yi = data[i++]; 13259 break; 13260 case CMD.Q: 13261 fromQuadratic( 13262 xi, yi, data[i++], data[i++], data[i], data[i + 1], 13263 min2, max2 13264 ); 13265 xi = data[i++]; 13266 yi = data[i++]; 13267 break; 13268 case CMD.A: 13269 // TODO Arc éãæé¨å«ç´é¿â¬å§£æç·æ¾¶ï¿½ 13270 var cx = data[i++]; 13271 var cy = data[i++]; 13272 var rx = data[i++]; 13273 var ry = data[i++]; 13274 var startAngle = data[i++]; 13275 var endAngle = data[i++] + startAngle; 13276 // TODO Arc é嬿µ 13277 var psi = data[i++]; 13278 var anticlockwise = 1 - data[i++]; 13279 13280 if (i == 1) { 13281 // é©åå¸´æµ£è·¨æ¤ arc éææ¤
13282 // ç»î¿ç«´æ¶îæ¡æµ ãæ£éç¡ ç¹éîç¾æ¶ï¿½ 13283 x0 = mathCos$1(startAngle) * rx + cx; 13284 y0 = mathSin$1(startAngle) * ry + cy; 13285 } 13286 13287 fromArc( 13288 cx, cy, rx, ry, startAngle, endAngle, 13289 anticlockwise, min2, max2 13290 ); 13291 13292 xi = mathCos$1(endAngle) * rx + cx; 13293 yi = mathSin$1(endAngle) * ry + cy; 13294 break; 13295 case CMD.R: 13296 x0 = xi = data[i++]; 13297 y0 = yi = data[i++]; 13298 var width = data[i++]; 13299 var height = data[i++]; 13300 // Use fromLine 13301 fromLine(x0, y0, x0 + width, y0 + height, min2, max2); 13302 break; 13303 case CMD.Z: 13304 xi = x0; 13305 yi = y0; 13306 break; 13307 } 13308 13309 // Union 13310 min(min$1, min$1, min2); 13311 max(max$1, max$1, max2); 13312 } 13313 13314 // No data 13315 if (i === 0) { 13316 min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0; 13317 } 13318 13319 return new BoundingRect( 13320 min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1] 13321 ); 13322 }, 13323 13324 /** 13325 * Rebuild path from current data 13326 * Rebuild path will not consider javascript implemented line dash. 13327 * @param {CanvasRenderingContext2D} ctx 13328 */ 13329 rebuildPath: function (ctx) { 13330 var d = this.data; 13331 var x0, y0; 13332 var xi, yi; 13333 var x, y; 13334 var ux = this._ux; 13335 var uy = this._uy; 13336 var len$$1 = this._len; 13337 for (var i = 0; i < len$$1;) { 13338 var cmd = d[i++]; 13339 13340 if (i == 1) { 13341 // æ¿¡åçç»î¿ç«´æ¶îæ¡æµ ãæ§¸ L, C, Q 13342 // é� previous point éå²ç²¯éè·ºæ¡æµ ãæ®ç»î¿ç«´æ¶ï¿½ point 13343 // 13344 // ç»î¿ç«´æ¶îæ¡æµ ãè´ Arc é¨å¬åéå ¸ç¬ æµ¼æ°¬æ¹ªé庨潰éè§ç©æ¾¶åæ 13345 xi = d[i]; 13346 yi = d[i + 1]; 13347 13348 x0 = xi; 13349 y0 = yi; 13350 } 13351 switch (cmd) { 13352 case CMD.M: 13353 x0 = xi = d[i++]; 13354 y0 = yi = d[i++]; 13355 ctx.moveTo(xi, yi); 13356 break; 13357 case CMD.L: 13358 x = d[i++]; 13359 y = d[i++]; 13360 // Not draw too small seg between 13361 if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len$$1 - 1) { 13362 ctx.lineTo(x, y); 13363 xi = x; 13364 yi = y; 13365 } 13366 break; 13367 case CMD.C: 13368 ctx.bezierCurveTo( 13369 d[i++], d[i++], d[i++], d[i++], d[i++], d[i++] 13370 ); 13371 xi = d[i - 2]; 13372 yi = d[i - 1]; 13373 break; 13374 case CMD.Q: 13375 ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]); 13376 xi = d[i - 2]; 13377 yi = d[i - 1]; 13378 break; 13379 case CMD.A: 13380 var cx = d[i++]; 13381 var cy = d[i++]; 13382 var rx = d[i++]; 13383 var ry = d[i++]; 13384 var theta = d[i++]; 13385 var dTheta = d[i++]; 13386 var psi = d[i++]; 13387 var fs = d[i++]; 13388 var r = (rx > ry) ? rx : ry; 13389 var scaleX = (rx > ry) ? 1 : rx / ry; 13390 var scaleY = (rx > ry) ? ry / rx : 1; 13391 var isEllipse = Math.abs(rx - ry) > 1e-3; 13392 var endAngle = theta + dTheta; 13393 if (isEllipse) { 13394 ctx.translate(cx, cy); 13395 ctx.rotate(psi); 13396 ctx.scale(scaleX, scaleY); 13397 ctx.arc(0, 0, r, theta, endAngle, 1 - fs); 13398 ctx.scale(1 / scaleX, 1 / scaleY); 13399 ctx.rotate(-psi); 13400 ctx.translate(-cx, -cy); 13401 } 13402 else { 13403 ctx.arc(cx, cy, r, theta, endAngle, 1 - fs); 13404 } 13405 13406 if (i == 1) { 13407 // é©åå¸´æµ£è·¨æ¤ arc éææ¤
vendor: 13,297 bytes, lines 13408-13843
13408 // ç»î¿ç«´æ¶îæ¡æµ ãæ£éç¡ ç¹éîç¾æ¶ï¿½ 13409 x0 = mathCos$1(theta) * rx + cx; 13410 y0 = mathSin$1(theta) * ry + cy; 13411 } 13412 xi = mathCos$1(endAngle) * rx + cx; 13413 yi = mathSin$1(endAngle) * ry + cy; 13414 break; 13415 case CMD.R: 13416 x0 = xi = d[i]; 13417 y0 = yi = d[i + 1]; 13418 ctx.rect(d[i++], d[i++], d[i++], d[i++]); 13419 break; 13420 case CMD.Z: 13421 ctx.closePath(); 13422 xi = x0; 13423 yi = y0; 13424 } 13425 } 13426 } 13427 }; 13428 13429 PathProxy.CMD = CMD; 13430 13431 /** 13432 * 绾æîéåæéãæ 13433 * @param {number} x0 13434 * @param {number} y0 13435 * @param {number} x1 13436 * @param {number} y1 13437 * @param {number} lineWidth 13438 * @param {number} x 13439 * @param {number} y 13440 * @return {boolean} 13441 */ 13442 function containStroke$1(x0, y0, x1, y1, lineWidth, x, y) { 13443 if (lineWidth === 0) { 13444 return false; 13445 } 13446 var _l = lineWidth; 13447 var _a = 0; 13448 var _b = x0; 13449 // Quick reject 13450 if ( 13451 (y > y0 + _l && y > y1 + _l) 13452 || (y < y0 - _l && y < y1 - _l) 13453 || (x > x0 + _l && x > x1 + _l) 13454 || (x < x0 - _l && x < x1 - _l) 13455 ) { 13456 return false; 13457 } 13458 13459 if (x0 !== x1) { 13460 _a = (y0 - y1) / (x0 - x1); 13461 _b = (x0 * y1 - x1 * y0) / (x0 - x1) ; 13462 } 13463 else { 13464 return Math.abs(x - x0) <= _l / 2; 13465 } 13466 var tmp = _a * x - y + _b; 13467 var _s = tmp * tmp / (_a * _a + 1); 13468 return _s <= _l / 2 * _l / 2; 13469 } 13470 13471 /** 13472 * æ¶å¤î¼çæ¿î£çææ´¸ç»¾æå¼¿æç°å¯éî¢å½é� 13473 * @param {number} x0 13474 * @param {number} y0 13475 * @param {number} x1 13476 * @param {number} y1 13477 * @param {number} x2 13478 * @param {number} y2 13479 * @param {number} x3 13480 * @param {number} y3 13481 * @param {number} lineWidth 13482 * @param {number} x 13483 * @param {number} y 13484 * @return {boolean} 13485 */ 13486 function containStroke$2(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) { 13487 if (lineWidth === 0) { 13488 return false; 13489 } 13490 var _l = lineWidth; 13491 // Quick reject 13492 if ( 13493 (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l) 13494 || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l) 13495 || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l) 13496 || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l) 13497 ) { 13498 return false; 13499 } 13500 var d = cubicProjectPoint( 13501 x0, y0, x1, y1, x2, y2, x3, y3, 13502 x, y, null 13503 ); 13504 return d <= _l / 2; 13505 } 13506 13507 /** 13508 * æµå±¾î¼çæ¿î£çææ´¸ç»¾æå¼¿æç°å¯éî¢å½é� 13509 * @param {number} x0 13510 * @param {number} y0 13511 * @param {number} x1 13512 * @param {number} y1 13513 * @param {number} x2 13514 * @param {number} y2 13515 * @param {number} lineWidth 13516 * @param {number} x 13517 * @param {number} y 13518 * @return {boolean} 13519 */ 13520 function containStroke$3(x0, y0, x1, y1, x2, y2, lineWidth, x, y) { 13521 if (lineWidth === 0) { 13522 return false; 13523 } 13524 var _l = lineWidth; 13525 // Quick reject 13526 if ( 13527 (y > y0 + _l && y > y1 + _l && y > y2 + _l) 13528 || (y < y0 - _l && y < y1 - _l && y < y2 - _l) 13529 || (x > x0 + _l && x > x1 + _l && x > x2 + _l) 13530 || (x < x0 - _l && x < x1 - _l && x < x2 - _l) 13531 ) { 13532 return false; 13533 } 13534 var d = quadraticProjectPoint( 13535 x0, y0, x1, y1, x2, y2, 13536 x, y, null 13537 ); 13538 return d <= _l / 2; 13539 } 13540 13541 var PI2$3 = Math.PI * 2; 13542 13543 function normalizeRadian(angle) { 13544 angle %= PI2$3; 13545 if (angle < 0) { 13546 angle += PI2$3; 13547 } 13548 return angle; 13549 } 13550 13551 var PI2$2 = Math.PI * 2; 13552 13553 /** 13554 * é¦å姬é»å¿ç«éåæéãæ 13555 * @param {number} cx 13556 * @param {number} cy 13557 * @param {number} r 13558 * @param {number} startAngle 13559 * @param {number} endAngle 13560 * @param {boolean} anticlockwise 13561 * @param {number} lineWidth 13562 * @param {number} x 13563 * @param {number} y 13564 * @return {Boolean} 13565 */ 13566 function containStroke$4( 13567 cx, cy, r, startAngle, endAngle, anticlockwise, 13568 lineWidth, x, y 13569 ) { 13570 13571 if (lineWidth === 0) { 13572 return false; 13573 } 13574 var _l = lineWidth; 13575 13576 x -= cx; 13577 y -= cy; 13578 var d = Math.sqrt(x * x + y * y); 13579 13580 if ((d - _l > r) || (d + _l < r)) { 13581 return false; 13582 } 13583 if (Math.abs(startAngle - endAngle) % PI2$2 < 1e-4) { 13584 // Is a circle 13585 return true; 13586 } 13587 if (anticlockwise) { 13588 var tmp = startAngle; 13589 startAngle = normalizeRadian(endAngle); 13590 endAngle = normalizeRadian(tmp); 13591 } else { 13592 startAngle = normalizeRadian(startAngle); 13593 endAngle = normalizeRadian(endAngle); 13594 } 13595 if (startAngle > endAngle) { 13596 endAngle += PI2$2; 13597 } 13598 13599 var angle = Math.atan2(y, x); 13600 if (angle < 0) { 13601 angle += PI2$2; 13602 } 13603 return (angle >= startAngle && angle <= endAngle) 13604 || (angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle); 13605 } 13606 13607 function windingLine(x0, y0, x1, y1, x, y) { 13608 if ((y > y0 && y > y1) || (y < y0 && y < y1)) { 13609 return 0; 13610 } 13611 // Ignore horizontal line 13612 if (y1 === y0) { 13613 return 0; 13614 } 13615 var dir = y1 < y0 ? 1 : -1; 13616 var t = (y - y0) / (y1 - y0); 13617 13618 // Avoid winding error when intersection point is the connect point of two line of polygon 13619 if (t === 1 || t === 0) { 13620 dir = y1 < y0 ? 0.5 : -0.5; 13621 } 13622 13623 var x_ = t * (x1 - x0) + x0; 13624 13625 // If (x, y) on the line, considered as "contain". 13626 return x_ === x ? Infinity : x_ > x ? dir : 0; 13627 } 13628 13629 var CMD$1 = PathProxy.CMD; 13630 var PI2$1 = Math.PI * 2; 13631 13632 var EPSILON$2 = 1e-4; 13633 13634 function isAroundEqual(a, b) { 13635 return Math.abs(a - b) < EPSILON$2; 13636 } 13637 13638// æ¶åæ¤éæ®ç² 13639 var roots = [-1, -1, -1]; 13640 var extrema = [-1, -1]; 13641 13642 function swapExtrema() { 13643 var tmp = extrema[0]; 13644 extrema[0] = extrema[1]; 13645 extrema[1] = tmp; 13646 } 13647 13648 function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) { 13649 // Quick reject 13650 if ( 13651 (y > y0 && y > y1 && y > y2 && y > y3) 13652 || (y < y0 && y < y1 && y < y2 && y < y3) 13653 ) { 13654 return 0; 13655 } 13656 var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots); 13657 if (nRoots === 0) { 13658 return 0; 13659 } 13660 else { 13661 var w = 0; 13662 var nExtrema = -1; 13663 var y0_, y1_; 13664 for (var i = 0; i < nRoots; i++) { 13665 var t = roots[i]; 13666 13667 // Avoid winding error when intersection point is the connect point of two line of polygon 13668 var unit = (t === 0 || t === 1) ? 0.5 : 1; 13669 13670 var x_ = cubicAt(x0, x1, x2, x3, t); 13671 if (x_ < x) { // Quick reject 13672 continue; 13673 } 13674 if (nExtrema < 0) { 13675 nExtrema = cubicExtrema(y0, y1, y2, y3, extrema); 13676 if (extrema[1] < extrema[0] && nExtrema > 1) { 13677 swapExtrema(); 13678 } 13679 y0_ = cubicAt(y0, y1, y2, y3, extrema[0]); 13680 if (nExtrema > 1) { 13681 y1_ = cubicAt(y0, y1, y2, y3, extrema[1]); 13682 } 13683 } 13684 if (nExtrema == 2) { 13685 // éååæ¶å¤îéæ¡çéè¥æ 13686 if (t < extrema[0]) { 13687 w += y0_ < y0 ? unit : -unit; 13688 } 13689 else if (t < extrema[1]) { 13690 w += y1_ < y0_ ? unit : -unit; 13691 } 13692 else { 13693 w += y3 < y1_ ? unit : -unit; 13694 } 13695 } 13696 else { 13697 // éååæ¶ãîéæ¡çéè¥æ 13698 if (t < extrema[0]) { 13699 w += y0_ < y0 ? unit : -unit; 13700 } 13701 else { 13702 w += y3 < y0_ ? unit : -unit; 13703 } 13704 } 13705 } 13706 return w; 13707 } 13708 } 13709 13710 function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) { 13711 // Quick reject 13712 if ( 13713 (y > y0 && y > y1 && y > y2) 13714 || (y < y0 && y < y1 && y < y2) 13715 ) { 13716 return 0; 13717 } 13718 var nRoots = quadraticRootAt(y0, y1, y2, y, roots); 13719 if (nRoots === 0) { 13720 return 0; 13721 } 13722 else { 13723 var t = quadraticExtremum(y0, y1, y2); 13724 if (t >= 0 && t <= 1) { 13725 var w = 0; 13726 var y_ = quadraticAt(y0, y1, y2, t); 13727 for (var i = 0; i < nRoots; i++) { 13728 // Remove one endpoint. 13729 var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1; 13730 13731 var x_ = quadraticAt(x0, x1, x2, roots[i]); 13732 if (x_ < x) { // Quick reject 13733 continue; 13734 } 13735 if (roots[i] < t) { 13736 w += y_ < y0 ? unit : -unit; 13737 } 13738 else { 13739 w += y2 < y_ ? unit : -unit; 13740 } 13741 } 13742 return w; 13743 } 13744 else { 13745 // Remove one endpoint. 13746 var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1; 13747 13748 var x_ = quadraticAt(x0, x1, x2, roots[0]); 13749 if (x_ < x) { // Quick reject 13750 return 0; 13751 } 13752 return y2 < y0 ? unit : -unit; 13753 } 13754 } 13755 } 13756 13757// TODO 13758// Arc é嬿µ 13759 function windingArc( 13760 cx, cy, r, startAngle, endAngle, anticlockwise, x, y 13761 ) { 13762 y -= cy; 13763 if (y > r || y < -r) { 13764 return 0; 13765 } 13766 var tmp = Math.sqrt(r * r - y * y); 13767 roots[0] = -tmp; 13768 roots[1] = tmp; 13769 13770 var diff = Math.abs(startAngle - endAngle); 13771 if (diff < 1e-4) { 13772 return 0; 13773 } 13774 if (diff % PI2$1 < 1e-4) { 13775 // Is a circle 13776 startAngle = 0; 13777 endAngle = PI2$1; 13778 var dir = anticlockwise ? 1 : -1; 13779 if (x >= roots[0] + cx && x <= roots[1] + cx) { 13780 return dir; 13781 } else { 13782 return 0; 13783 } 13784 } 13785 13786 if (anticlockwise) { 13787 var tmp = startAngle; 13788 startAngle = normalizeRadian(endAngle); 13789 endAngle = normalizeRadian(tmp); 13790 } 13791 else { 13792 startAngle = normalizeRadian(startAngle); 13793 endAngle = normalizeRadian(endAngle); 13794 } 13795 if (startAngle > endAngle) { 13796 endAngle += PI2$1; 13797 } 13798 13799 var w = 0; 13800 for (var i = 0; i < 2; i++) { 13801 var x_ = roots[i]; 13802 if (x_ + cx > x) { 13803 var angle = Math.atan2(y, x_); 13804 var dir = anticlockwise ? 1 : -1; 13805 if (angle < 0) { 13806 angle = PI2$1 + angle; 13807 } 13808 if ( 13809 (angle >= startAngle && angle <= endAngle) 13810 || (angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle) 13811 ) { 13812 if (angle > Math.PI / 2 && angle < Math.PI * 1.5) { 13813 dir = -dir; 13814 } 13815 w += dir; 13816 } 13817 } 13818 } 13819 return w; 13820 } 13821 13822 function containPath(data, lineWidth, isStroke, x, y) { 13823 var w = 0; 13824 var xi = 0; 13825 var yi = 0; 13826 var x0 = 0; 13827 var y0 = 0; 13828 13829 for (var i = 0; i < data.length;) { 13830 var cmd = data[i++]; 13831 // Begin a new subpath 13832 if (cmd === CMD$1.M && i > 1) { 13833 // Close previous subpath 13834 if (!isStroke) { 13835 w += windingLine(xi, yi, x0, y0, x, y); 13836 } 13837 // æ¿¡åççî¡æ¢æµ£æç«´æ¶ï¿½ subpath éåæ 13838 // if (w !== 0) { 13839 // return true; 13840 // } 13841 } 13842 13843 if (i == 1) {
13844 // æ¿¡åçç»î¿ç«´æ¶îæ¡æµ ãæ§¸ L, C, Q 13845 // é� previous point éå²ç²¯éè·ºæ¡æµ ãæ®ç»î¿ç«´æ¶ï¿½ point 13846 // 13847 // ç»î¿ç«´æ¶îæ¡æµ ãè´ Arc é¨å¬åéå ¸ç¬ æµ¼æ°¬æ¹ªé庨潰éè§ç©æ¾¶åæ 13848 xi = data[i]; 13849 yi = data[i + 1]; 13850 13851 x0 = xi; 13852 y0 = yi; 13853 } 13854 13855 switch (cmd) { 13856 case CMD$1.M: 13857 // moveTo éææ¤é²å¶æéæ¶ç¼æ¶â¬æ¶î æé¨ï¿½ subpath, éªæµç¬éåæéæ®æ®ç§é£å£ 13858 // é¦ï¿½ closePath é¨å¬æ¤éæ¬å¨é¢ï¿½ 13859 x0 = data[i++]; 13860 y0 = data[i++]; 13861 xi = x0; 13862 yi = y0; 13863 break; 13864 case CMD$1.L: 13865 if (isStroke) { 13866 if (containStroke$1(xi, yi, data[i], data[i + 1], lineWidth, x, y)) { 13867 return true; 13868 } 13869 } 13870 else { 13871 // NOTE é¦ã§îæ¶â¬æ¶îæ¡æµ ãè´ L, C, Q é¨å¬æ¤éæ¬ç´°çï¼ç»é� NaN 13872 w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0; 13873 } 13874 xi = data[i++]; 13875 yi = data[i++]; 13876 break; 13877 case CMD$1.C: 13878 if (isStroke) { 13879 if (containStroke$2(xi, yi, 13880 data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], 13881 lineWidth, x, y 13882 )) { 13883 return true; 13884 } 13885 } 13886 else { 13887 w += windingCubic( 13888 xi, yi, 13889 data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], 13890 x, y 13891 ) || 0; 13892 } 13893 xi = data[i++]; 13894 yi = data[i++]; 13895 break; 13896 case CMD$1.Q: 13897 if (isStroke) { 13898 if (containStroke$3(xi, yi, 13899 data[i++], data[i++], data[i], data[i + 1], 13900 lineWidth, x, y 13901 )) { 13902 return true; 13903 } 13904 } 13905 else { 13906 w += windingQuadratic( 13907 xi, yi, 13908 data[i++], data[i++], data[i], data[i + 1], 13909 x, y 13910 ) || 0; 13911 } 13912 xi = data[i++]; 13913 yi = data[i++]; 13914 break; 13915 case CMD$1.A: 13916 // TODO Arc éãæé¨å«ç´é¿â¬å§£æç·æ¾¶ï¿½ 13917 var cx = data[i++]; 13918 var cy = data[i++]; 13919 var rx = data[i++]; 13920 var ry = data[i++]; 13921 var theta = data[i++]; 13922 var dTheta = data[i++]; 13923 // TODO Arc é嬿µ 13924 var psi = data[i++]; 13925 var anticlockwise = 1 - data[i++]; 13926 var x1 = Math.cos(theta) * rx + cx; 13927 var y1 = Math.sin(theta) * ry + cy; 13928 // æ¶å¶æ§¸é©åå¸´æµ£è·¨æ¤ arc éææ¤ 13929 if (i > 1) { 13930 w += windingLine(xi, yi, x1, y1, x, y); 13931 } 13932 else { 13933 // ç»î¿ç«´æ¶îæ¡æµ ãæ£éç¡ ç¹éîç¾æ¶ï¿½ 13934 x0 = x1; 13935 y0 = y1; 13936 } 13937 // zr 浣跨æ¤scaleéã¦Äé·ç¸ãé¦ï¿½, æ©æ¬å·æ¶ç·î®xéæ°«ç«´ç¹æ°±æ®ç¼âæ 13938 var _x = (x - cx) * ry / rx + cx; 13939 if (isStroke) { 13940 if (containStroke$4( 13941 cx, cy, ry, theta, theta + dTheta, anticlockwise, 13942 lineWidth, _x, y 13943 )) { 13944 return true; 13945 } 13946 } 13947 else { 13948 w += windingArc( 13949 cx, cy, ry, theta, theta + dTheta, anticlockwise, 13950 _x, y 13951 ); 13952 } 13953 xi = Math.cos(theta + dTheta) * rx + cx; 13954 yi = Math.sin(theta + dTheta) * ry + cy; 13955 break; 13956 case CMD$1.R: 13957 x0 = xi = data[i++]; 13958 y0 = yi = data[i++]; 13959 var width = data[i++]; 13960 var height = data[i++]; 13961 var x1 = x0 + width; 13962 var y1 = y0 + height; 13963 if (isStroke) { 13964 if (containStroke$1(x0, y0, x1, y0, lineWidth, x, y) 13965 || containStroke$1(x1, y0, x1, y1, lineWidth, x, y) 13966 || containStroke$1(x1, y1, x0, y1, lineWidth, x, y) 13967 || containStroke$1(x0, y1, x0, y0, lineWidth, x, y) 13968 ) { 13969 return true; 13970 } 13971 } 13972 else { 13973 // FIXME Clockwise ? 13974 w += windingLine(x1, y0, x1, y1, x, y); 13975 w += windingLine(x0, y1, x0, y0, x, y); 13976 } 13977 break; 13978 case CMD$1.Z: 13979 if (isStroke) { 13980 if (containStroke$1( 13981 xi, yi, x0, y0, lineWidth, x, y 13982 )) { 13983 return true; 13984 } 13985 } 13986 else { 13987 // Close a subpath 13988 w += windingLine(xi, yi, x0, y0, x, y); 13989 // æ¿¡åççî¡æ¢æµ£æç«´æ¶ï¿½ subpath éåæ 13990 // FIXME subpaths may overlap 13991 // if (w !== 0) { 13992 // return true; 13993 // } 13994 } 13995 xi = x0; 13996 yi = y0; 13997 break; 13998 } 13999 } 14000 if (!isStroke && !isAroundEqual(yi, y0)) { 14001 w += windingLine(xi, yi, x0, y0, x, y) || 0; 14002 } 14003 return w !== 0; 14004 } 14005 14006 function contain(pathData, x, y) { 14007 return containPath(pathData, 0, false, x, y); 14008 } 14009 14010 function containStroke(pathData, lineWidth, x, y) { 14011 return containPath(pathData, lineWidth, true, x, y); 14012 } 14013 14014 var getCanvasPattern = Pattern.prototype.getCanvasPattern; 14015 14016 var abs = Math.abs; 14017 14018 var pathProxyForDraw = new PathProxy(true); 14019 /** 14020 * @alias module:zrender/graphic/Path 14021 * @extends module:zrender/graphic/Displayable 14022 * @constructor 14023 * @param {Object} opts 14024 */ 14025 function Path(opts) { 14026 Displayable.call(this, opts); 14027 14028 /** 14029 * @type {module:zrender/core/PathProxy} 14030 * @readOnly 14031 */ 14032 this.path = null; 14033 } 14034 14035 Path.prototype = { 14036 14037 constructor: Path, 14038 14039 type: 'path', 14040 14041 __dirtyPath: true, 14042 14043 strokeContainThreshold: 5, 14044 14045 brush: function (ctx, prevEl) { 14046 var style = this.style; 14047 var path = this.path || pathProxyForDraw; 14048 var hasStroke = style.hasStroke(); 14049 var hasFill = style.hasFill(); 14050 var fill = style.fill; 14051 var stroke = style.stroke; 14052 var hasFillGradient = hasFill && !!(fill.colorStops); 14053 var hasStrokeGradient = hasStroke && !!(stroke.colorStops); 14054 var hasFillPattern = hasFill && !!(fill.image); 14055 var hasStrokePattern = hasStroke && !!(stroke.image); 14056 14057 style.bind(ctx, this, prevEl); 14058 this.setTransform(ctx); 14059 14060 if (this.__dirty) { 14061 var rect; 14062 // Update gradient because bounding rect may changed 14063 if (hasFillGradient) {
14064 rect = rect || this.getBoundingRect(); 14065 this._fillGradient = style.getGradient(ctx, fill, rect); 14066 } 14067 if (hasStrokeGradient) { 14068 rect = rect || this.getBoundingRect(); 14069 this._strokeGradient = style.getGradient(ctx, stroke, rect); 14070 } 14071 } 14072 // Use the gradient or pattern 14073 if (hasFillGradient) { 14074 // PENDING If may have affect the state 14075 ctx.fillStyle = this._fillGradient; 14076 } 14077 else if (hasFillPattern) { 14078 ctx.fillStyle = getCanvasPattern.call(fill, ctx); 14079 } 14080 if (hasStrokeGradient) { 14081 ctx.strokeStyle = this._strokeGradient; 14082 } 14083 else if (hasStrokePattern) { 14084 ctx.strokeStyle = getCanvasPattern.call(stroke, ctx); 14085 } 14086 14087 var lineDash = style.lineDash; 14088 var lineDashOffset = style.lineDashOffset; 14089 14090 var ctxLineDash = !!ctx.setLineDash; 14091 14092 // Update path sx, sy 14093 var scale = this.getGlobalScale(); 14094 path.setScale(scale[0], scale[1]); 14095 14096 // Proxy context 14097 // Rebuild path in following 2 cases 14098 // 1. Path is dirty 14099 // 2. Path needs javascript implemented lineDash stroking. 14100 // In this case, lineDash information will not be saved in PathProxy 14101 if (this.__dirtyPath 14102 || (lineDash && !ctxLineDash && hasStroke) 14103 ) { 14104 path.beginPath(ctx); 14105 14106 // Setting line dash before build path 14107 if (lineDash && !ctxLineDash) { 14108 path.setLineDash(lineDash); 14109 path.setLineDashOffset(lineDashOffset); 14110 } 14111 14112 this.buildPath(path, this.shape, false); 14113 14114 // Clear path dirty flag 14115 if (this.path) { 14116 this.__dirtyPath = false; 14117 } 14118 } 14119 else { 14120 // Replay path building 14121 ctx.beginPath(); 14122 this.path.rebuildPath(ctx); 14123 } 14124 14125 hasFill && path.fill(ctx); 14126 14127 if (lineDash && ctxLineDash) { 14128 ctx.setLineDash(lineDash); 14129 ctx.lineDashOffset = lineDashOffset; 14130 } 14131 14132 hasStroke && path.stroke(ctx); 14133 14134 if (lineDash && ctxLineDash) { 14135 // PENDING 14136 // Remove lineDash 14137 ctx.setLineDash([]); 14138 } 14139 14140 // Draw rect text 14141 if (style.text != null) { 14142 // Only restore transform when needs draw text. 14143 this.restoreTransform(ctx); 14144 this.drawRectText(ctx, this.getBoundingRect()); 14145 } 14146 }, 14147 14148 // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath 14149 // Like in circle 14150 buildPath: function (ctx, shapeCfg, inBundle) {}, 14151 14152 createPathProxy: function () { 14153 this.path = new PathProxy(); 14154 }, 14155 14156 getBoundingRect: function () { 14157 var rect = this._rect; 14158 var style = this.style; 14159 var needsUpdateRect = !rect; 14160 if (needsUpdateRect) { 14161 var path = this.path; 14162 if (!path) { 14163 // Create path on demand. 14164 path = this.path = new PathProxy(); 14165 } 14166 if (this.__dirtyPath) { 14167 path.beginPath(); 14168 this.buildPath(path, this.shape, false); 14169 } 14170 rect = path.getBoundingRect(); 14171 } 14172 this._rect = rect; 14173 14174 if (style.hasStroke()) { 14175 // Needs update rect with stroke lineWidth when 14176 // 1. Element changes scale or lineWidth 14177 // 2. Shape is changed 14178 var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone()); 14179 if (this.__dirty || needsUpdateRect) { 14180 rectWithStroke.copy(rect); 14181 // FIXME Must after updateTransform 14182 var w = style.lineWidth; 14183 // PENDING, Min line width is needed when line is horizontal or vertical 14184 var lineScale = style.strokeNoScale ? this.getLineScale() : 1; 14185 14186 // Only add extra hover lineWidth when there are no fill 14187 if (!style.hasFill()) { 14188 w = Math.max(w, this.strokeContainThreshold || 4); 14189 } 14190 // Consider line width 14191 // Line scale can't be 0; 14192 if (lineScale > 1e-10) { 14193 rectWithStroke.width += w / lineScale; 14194 rectWithStroke.height += w / lineScale; 14195 rectWithStroke.x -= w / lineScale / 2; 14196 rectWithStroke.y -= w / lineScale / 2; 14197 } 14198 } 14199 14200 // Return rect with stroke 14201 return rectWithStroke; 14202 } 14203 14204 return rect; 14205 }, 14206 14207 contain: function (x, y) { 14208 var localPos = this.transformCoordToLocal(x, y);
14209 var rect = this.getBoundingRect(); 14210 var style = this.style; 14211 x = localPos[0]; 14212 y = localPos[1]; 14213 14214 if (rect.contain(x, y)) { 14215 var pathData = this.path.data; 14216 if (style.hasStroke()) { 14217 var lineWidth = style.lineWidth; 14218 var lineScale = style.strokeNoScale ? this.getLineScale() : 1; 14219 // Line scale can't be 0; 14220 if (lineScale > 1e-10) { 14221 // Only add extra hover lineWidth when there are no fill 14222 if (!style.hasFill()) { 14223 lineWidth = Math.max(lineWidth, this.strokeContainThreshold); 14224 } 14225 if (containStroke( 14226 pathData, lineWidth / lineScale, x, y 14227 )) { 14228 return true; 14229 } 14230 } 14231 } 14232 if (style.hasFill()) { 14233 return contain(pathData, x, y); 14234 } 14235 } 14236 return false; 14237 }, 14238 14239 /** 14240 * @param {boolean} dirtyPath 14241 */ 14242 dirty: function (dirtyPath) { 14243 if (dirtyPath == null) { 14244 dirtyPath = true; 14245 } 14246 // Only mark dirty, not mark clean 14247 if (dirtyPath) { 14248 this.__dirtyPath = dirtyPath; 14249 this._rect = null; 14250 } 14251 14252 this.__dirty = true; 14253 14254 this.__zr && this.__zr.refresh(); 14255 14256 // Used as a clipping path 14257 if (this.__clipTarget) { 14258 this.__clipTarget.dirty(); 14259 } 14260 }, 14261 14262 /** 14263 * Alias for animate('shape') 14264 * @param {boolean} loop 14265 */ 14266 animateShape: function (loop) { 14267 return this.animate('shape', loop); 14268 }, 14269 14270 // Overwrite attrKV 14271 attrKV: function (key, value) { 14272 // FIXME 14273 if (key === 'shape') { 14274 this.setShape(value); 14275 this.__dirtyPath = true; 14276 this._rect = null; 14277 } 14278 else { 14279 Displayable.prototype.attrKV.call(this, key, value); 14280 } 14281 }, 14282 14283 /** 14284 * @param {Object|string} key 14285 * @param {*} value 14286 */ 14287 setShape: function (key, value) { 14288 var shape = this.shape; 14289 // Path from string may not have shape 14290 if (shape) { 14291 if (isObject$1(key)) { 14292 for (var name in key) { 14293 if (key.hasOwnProperty(name)) { 14294 shape[name] = key[name]; 14295 } 14296 } 14297 } 14298 else { 14299 shape[key] = value; 14300 } 14301 this.dirty(true); 14302 } 14303 return this; 14304 }, 14305 14306 getLineScale: function () { 14307 var m = this.transform; 14308 // Get the line scale. 14309 // Determinant of `m` means how much the area is enlarged by the 14310 // transformation. So its square root can be used as a scale factor 14311 // for width. 14312 return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10 14313 ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1])) 14314 : 1; 14315 } 14316 }; 14317 14318 /** 14319 * éµâçæ¶â¬æ¶ï¿½ Path element, å§£æ¿î§éç·è°é屽渾ç»å¤â¬ï¿½ 14320 * Extend a path element 14321 * @param {Object} props 14322 * @param {string} props.type Path type 14323 * @param {Function} props.init Initialize 14324 * @param {Function} props.buildPath Overwrite buildPath method 14325 * @param {Object} [props.style] Extended default style config 14326 * @param {Object} [props.shape] Extended default shape config 14327 */ 14328 Path.extend = function (defaults$$1) { 14329 var Sub = function (opts) { 14330 Path.call(this, opts); 14331 14332 if (defaults$$1.style) { 14333 // Extend default style 14334 this.style.extendFrom(defaults$$1.style, false); 14335 } 14336 14337 // Extend default shape 14338 var defaultShape = defaults$$1.shape; 14339 if (defaultShape) { 14340 this.shape = this.shape || {}; 14341 var thisShape = this.shape; 14342 for (var name in defaultShape) { 14343 if ( 14344 ! thisShape.hasOwnProperty(name) 14345 && defaultShape.hasOwnProperty(name) 14346 ) { 14347 thisShape[name] = defaultShape[name]; 14348 } 14349 } 14350 } 14351 14352 defaults$$1.init && defaults$$1.init.call(this, opts); 14353 }; 14354 14355 inherits(Sub, Path); 14356 14357 // FIXME æ¶å¶å extend position, rotation ç»å¤ç´©é¢ã¥î®ç� 14358 for (var name in defaults$$1) { 14359 // Extending prototype values and methods 14360 if (name !== 'style' && name !== 'shape') { 14361 Sub.prototype[name] = defaults$$1[name]; 14362 } 14363 } 14364 14365 return Sub; 14366 }; 14367 14368 inherits(Path, Displayable); 14369 14370 var CMD$2 = PathProxy.CMD; 14371 14372 var points = [[], [], []]; 14373 var mathSqrt$3 = Math.sqrt; 14374 var mathAtan2 = Math.atan2; 14375 14376 var transformPath = function (path, m) { 14377 var data = path.data; 14378 var cmd; 14379 var nPoint; 14380 var i; 14381 var j; 14382 var k; 14383 var p; 14384 14385 var M = CMD$2.M; 14386 var C = CMD$2.C; 14387 var L = CMD$2.L; 14388 var R = CMD$2.R; 14389 var A = CMD$2.A; 14390 var Q = CMD$2.Q; 14391 14392 for (i = 0, j = 0; i < data.length;) { 14393 cmd = data[i++]; 14394 j = i; 14395 nPoint = 0; 14396 14397 switch (cmd) { 14398 case M: 14399 nPoint = 1; 14400 break; 14401 case L: 14402 nPoint = 1; 14403 break; 14404 case C: 14405 nPoint = 3; 14406 break; 14407 case Q: 14408 nPoint = 2; 14409 break; 14410 case A: 14411 var x = m[4]; 14412 var y = m[5]; 14413 var sx = mathSqrt$3(m[0] * m[0] + m[1] * m[1]); 14414 var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]); 14415 var angle = mathAtan2(-m[1] / sy, m[0] / sx); 14416 // cx 14417 data[i] *= sx; 14418 data[i++] += x; 14419 // cy 14420 data[i] *= sy; 14421 data[i++] += y; 14422 // Scale rx and ry 14423 // FIXME Assume psi is 0 here 14424 data[i++] *= sx; 14425 data[i++] *= sy; 14426 14427 // Start angle 14428 data[i++] += angle; 14429 // end angle 14430 data[i++] += angle; 14431 // FIXME psi 14432 i += 2; 14433 j = i; 14434 break; 14435 case R: 14436 // x0, y0 14437 p[0] = data[i++]; 14438 p[1] = data[i++]; 14439 applyTransform(p, p, m); 14440 data[j++] = p[0]; 14441 data[j++] = p[1]; 14442 // x1, y1 14443 p[0] += data[i++]; 14444 p[1] += data[i++]; 14445 applyTransform(p, p, m); 14446 data[j++] = p[0]; 14447 data[j++] = p[1]; 14448 } 14449 14450 for (k = 0; k < nPoint; k++) { 14451 var p = points[k]; 14452 p[0] = data[i++]; 14453 p[1] = data[i++]; 14454 14455 applyTransform(p, p, m); 14456 // Write back 14457 data[j++] = p[0]; 14458 data[j++] = p[1]; 14459 } 14460 } 14461 }; 14462 14463// command chars 14464 var cc = [ 14465 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 14466 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' 14467 ]; 14468 14469 var mathSqrt = Math.sqrt; 14470 var mathSin = Math.sin; 14471 var mathCos = Math.cos;
vendor: 1,418 bytes, lines 14472-14515
14472 var PI = Math.PI; 14473 14474 var vMag = function(v) { 14475 return Math.sqrt(v[0] * v[0] + v[1] * v[1]); 14476 }; 14477 var vRatio = function(u, v) { 14478 return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v)); 14479 }; 14480 var vAngle = function(u, v) { 14481 return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) 14482 * Math.acos(vRatio(u, v)); 14483 }; 14484 14485 function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) { 14486 var psi = psiDeg * (PI / 180.0); 14487 var xp = mathCos(psi) * (x1 - x2) / 2.0 14488 + mathSin(psi) * (y1 - y2) / 2.0; 14489 var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0 14490 + mathCos(psi) * (y1 - y2) / 2.0; 14491 14492 var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry); 14493 14494 if (lambda > 1) { 14495 rx *= mathSqrt(lambda); 14496 ry *= mathSqrt(lambda); 14497 } 14498 14499 var f = (fa === fs ? -1 : 1) 14500 * mathSqrt((((rx * rx) * (ry * ry)) 14501 - ((rx * rx) * (yp * yp)) 14502 - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) 14503 + (ry * ry) * (xp * xp)) 14504 ) || 0; 14505 14506 var cxp = f * rx * yp / ry; 14507 var cyp = f * -ry * xp / rx; 14508 14509 var cx = (x1 + x2) / 2.0 14510 + mathCos(psi) * cxp 14511 - mathSin(psi) * cyp; 14512 var cy = (y1 + y2) / 2.0 14513 + mathSin(psi) * cxp 14514 + mathCos(psi) * cyp; 14515
14516 var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]); 14517 var u = [ (xp - cxp) / rx, (yp - cyp) / ry ]; 14518 var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ]; 14519 var dTheta = vAngle(u, v); 14520 14521 if (vRatio(u, v) <= -1) { 14522 dTheta = PI; 14523 } 14524 if (vRatio(u, v) >= 1) { 14525 dTheta = 0; 14526 } 14527 if (fs === 0 && dTheta > 0) { 14528 dTheta = dTheta - 2 * PI; 14529 } 14530 if (fs === 1 && dTheta < 0) { 14531 dTheta = dTheta + 2 * PI; 14532 } 14533 14534 path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs); 14535 } 14536 14537 function createPathProxyFromString(data) { 14538 if (!data) { 14539 return []; 14540 } 14541 14542 // command string 14543 var cs = data.replace(/-/g, ' -') 14544 .replace(/ /g, ' ') 14545 .replace(/ /g, ',') 14546 .replace(/,,/g, ','); 14547 14548 var n; 14549 // create pipes so that we can split the data 14550 for (n = 0; n < cc.length; n++) { 14551 cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); 14552 } 14553 14554 // create array 14555 var arr = cs.split('|'); 14556 // init context point 14557 var cpx = 0; 14558 var cpy = 0; 14559 14560 var path = new PathProxy(); 14561 var CMD = PathProxy.CMD; 14562 14563 var prevCmd; 14564 for (n = 1; n < arr.length; n++) { 14565 var str = arr[n]; 14566 var c = str.charAt(0);
14567 var off = 0; 14568 var p = str.slice(1).replace(/e,-/g, 'e-').split(','); 14569 var cmd; 14570 14571 if (p.length > 0 && p[0] === '') { 14572 p.shift(); 14573 } 14574 14575 for (var i = 0; i < p.length; i++) { 14576 p[i] = parseFloat(p[i]); 14577 } 14578 while (off < p.length && !isNaN(p[off])) { 14579 if (isNaN(p[0])) { 14580 break; 14581 } 14582 var ctlPtx; 14583 var ctlPty; 14584 14585 var rx; 14586 var ry; 14587 var psi; 14588 var fa; 14589 var fs; 14590 14591 var x1 = cpx; 14592 var y1 = cpy; 14593 14594 // convert l, H, h, V, and v to L 14595 switch (c) { 14596 case 'l': 14597 cpx += p[off++]; 14598 cpy += p[off++]; 14599 cmd = CMD.L; 14600 path.addData(cmd, cpx, cpy); 14601 break; 14602 case 'L': 14603 cpx = p[off++]; 14604 cpy = p[off++]; 14605 cmd = CMD.L; 14606 path.addData(cmd, cpx, cpy); 14607 break; 14608 case 'm': 14609 cpx += p[off++]; 14610 cpy += p[off++]; 14611 cmd = CMD.M; 14612 path.addData(cmd, cpx, cpy); 14613 c = 'l'; 14614 break; 14615 case 'M': 14616 cpx = p[off++]; 14617 cpy = p[off++]; 14618 cmd = CMD.M; 14619 path.addData(cmd, cpx, cpy); 14620 c = 'L'; 14621 break; 14622 case 'h': 14623 cpx += p[off++]; 14624 cmd = CMD.L; 14625 path.addData(cmd, cpx, cpy); 14626 break; 14627 case 'H': 14628 cpx = p[off++]; 14629 cmd = CMD.L; 14630 path.addData(cmd, cpx, cpy); 14631 break; 14632 case 'v': 14633 cpy += p[off++]; 14634 cmd = CMD.L; 14635 path.addData(cmd, cpx, cpy); 14636 break; 14637 case 'V': 14638 cpy = p[off++]; 14639 cmd = CMD.L; 14640 path.addData(cmd, cpx, cpy); 14641 break; 14642 case 'C': 14643 cmd = CMD.C; 14644 path.addData( 14645 cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++] 14646 ); 14647 cpx = p[off - 2]; 14648 cpy = p[off - 1]; 14649 break; 14650 case 'c': 14651 cmd = CMD.C; 14652 path.addData( 14653 cmd, 14654 p[off++] + cpx, p[off++] + cpy, 14655 p[off++] + cpx, p[off++] + cpy, 14656 p[off++] + cpx, p[off++] + cpy 14657 ); 14658 cpx += p[off - 2]; 14659 cpy += p[off - 1]; 14660 break; 14661 case 'S': 14662 ctlPtx = cpx; 14663 ctlPty = cpy; 14664 var len = path.len(); 14665 var pathData = path.data; 14666 if (prevCmd === CMD.C) { 14667 ctlPtx += cpx - pathData[len - 4]; 14668 ctlPty += cpy - pathData[len - 3]; 14669 } 14670 cmd = CMD.C; 14671 x1 = p[off++]; 14672 y1 = p[off++]; 14673 cpx = p[off++]; 14674 cpy = p[off++]; 14675 path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); 14676 break; 14677 case 's': 14678 ctlPtx = cpx; 14679 ctlPty = cpy; 14680 var len = path.len(); 14681 var pathData = path.data; 14682 if (prevCmd === CMD.C) { 14683 ctlPtx += cpx - pathData[len - 4]; 14684 ctlPty += cpy - pathData[len - 3]; 14685 } 14686 cmd = CMD.C; 14687 x1 = cpx + p[off++]; 14688 y1 = cpy + p[off++]; 14689 cpx += p[off++]; 14690 cpy += p[off++]; 14691 path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); 14692 break; 14693 case 'Q': 14694 x1 = p[off++]; 14695 y1 = p[off++]; 14696 cpx = p[off++]; 14697 cpy = p[off++]; 14698 cmd = CMD.Q; 14699 path.addData(cmd, x1, y1, cpx, cpy); 14700 break; 14701 case 'q': 14702 x1 = p[off++] + cpx; 14703 y1 = p[off++] + cpy; 14704 cpx += p[off++]; 14705 cpy += p[off++]; 14706 cmd = CMD.Q; 14707 path.addData(cmd, x1, y1, cpx, cpy); 14708 break; 14709 case 'T': 14710 ctlPtx = cpx; 14711 ctlPty = cpy; 14712 var len = path.len(); 14713 var pathData = path.data; 14714 if (prevCmd === CMD.Q) { 14715 ctlPtx += cpx - pathData[len - 4]; 14716 ctlPty += cpy - pathData[len - 3]; 14717 } 14718 cpx = p[off++]; 14719 cpy = p[off++]; 14720 cmd = CMD.Q; 14721 path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); 14722 break; 14723 case 't': 14724 ctlPtx = cpx; 14725 ctlPty = cpy; 14726 var len = path.len(); 14727 var pathData = path.data; 14728 if (prevCmd === CMD.Q) { 14729 ctlPtx += cpx - pathData[len - 4]; 14730 ctlPty += cpy - pathData[len - 3]; 14731 } 14732 cpx += p[off++]; 14733 cpy += p[off++]; 14734 cmd = CMD.Q; 14735 path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); 14736 break; 14737 case 'A': 14738 rx = p[off++]; 14739 ry = p[off++]; 14740 psi = p[off++]; 14741 fa = p[off++]; 14742 fs = p[off++]; 14743 14744 x1 = cpx, y1 = cpy; 14745 cpx = p[off++]; 14746 cpy = p[off++]; 14747 cmd = CMD.A; 14748 processArc( 14749 x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path 14750 ); 14751 break; 14752 case 'a': 14753 rx = p[off++]; 14754 ry = p[off++]; 14755 psi = p[off++]; 14756 fa = p[off++]; 14757 fs = p[off++]; 14758 14759 x1 = cpx, y1 = cpy; 14760 cpx += p[off++]; 14761 cpy += p[off++]; 14762 cmd = CMD.A; 14763 processArc( 14764 x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path 14765 ); 14766 break; 14767 } 14768 } 14769 14770 if (c === 'z' || c === 'Z') { 14771 cmd = CMD.Z; 14772 path.addData(cmd); 14773 } 14774 14775 prevCmd = cmd; 14776 } 14777 14778 path.toStatic(); 14779 14780 return path; 14781 } 14782 14783// TODO Optimize double memory cost problem 14784 function createPathOptions(str, opts) { 14785 var pathProxy = createPathProxyFromString(str); 14786 opts = opts || {}; 14787 opts.buildPath = function (path) { 14788 if (path.setData) { 14789 path.setData(pathProxy.data); 14790 // Svg and vml renderer don't have context 14791 var ctx = path.getContext(); 14792 if (ctx) { 14793 path.rebuildPath(ctx); 14794 } 14795 } 14796 else { 14797 var ctx = path; 14798 pathProxy.rebuildPath(ctx); 14799 } 14800 }; 14801 14802 opts.applyTransform = function (m) { 14803 transformPath(pathProxy, m); 14804 this.dirty(true); 14805 }; 14806 14807 return opts; 14808 } 14809 14810 /** 14811 * Create a Path object from path string data 14812 * http://www.w3.org/TR/SVG/paths.html#PathData 14813 * @param {Object} opts Other options 14814 */ 14815 function createFromString(str, opts) { 14816 return new Path(createPathOptions(str, opts)); 14817 } 14818 14819 /** 14820 * Create a Path class from path string data 14821 * @param {string} str 14822 * @param {Object} opts Other options 14823 */ 14824 function extendFromString(str, opts) { 14825 return Path.extend(createPathOptions(str, opts)); 14826 } 14827 14828 /** 14829 * Merge multiple paths 14830 */ 14831// TODO Apply transform 14832// TODO stroke dash 14833// TODO Optimize double memory cost problem 14834 function mergePath$1(pathEls, opts) { 14835 var pathList = []; 14836 var len = pathEls.length; 14837 for (var i = 0; i < len; i++) { 14838 var pathEl = pathEls[i]; 14839 if (!pathEl.path) { 14840 pathEl.createPathProxy(); 14841 } 14842 if (pathEl.__dirtyPath) { 14843 pathEl.buildPath(pathEl.path, pathEl.shape, true); 14844 } 14845 pathList.push(pathEl.path); 14846 } 14847 14848 var pathBundle = new Path(opts); 14849 // Need path proxy. 14850 pathBundle.createPathProxy(); 14851 pathBundle.buildPath = function (path) { 14852 path.appendPath(pathList); 14853 // Svg and vml renderer don't have context 14854 var ctx = path.getContext(); 14855 if (ctx) { 14856 path.rebuildPath(ctx); 14857 } 14858 }; 14859 14860 return pathBundle; 14861 } 14862 14863 /** 14864 * @alias zrender/graphic/Text 14865 * @extends module:zrender/graphic/Displayable 14866 * @constructor 14867 * @param {Object} opts 14868 */ 14869 var Text = function (opts) { // jshint ignore:line 14870 Displayable.call(this, opts); 14871 }; 14872 14873 Text.prototype = { 14874 14875 constructor: Text, 14876 14877 type: 'text', 14878 14879 brush: function (ctx, prevEl) { 14880 var style = this.style; 14881 14882 // Optimize, avoid normalize every time. 14883 this.__dirty && normalizeTextStyle(style, true); 14884 14885 // Use props with prefix 'text'. 14886 style.fill = style.stroke = style.shadowBlur = style.shadowColor = 14887 style.shadowOffsetX = style.shadowOffsetY = null; 14888 14889 var text = style.text; 14890 // Convert to string 14891 text != null && (text += ''); 14892 14893 // Always bind style 14894 style.bind(ctx, this, prevEl); 14895 14896 if (!needDrawText(text, style)) { 14897 return; 14898 } 14899 14900 this.setTransform(ctx); 14901 14902 renderText(this, ctx, text, style); 14903 14904 this.restoreTransform(ctx); 14905 }, 14906 14907 getBoundingRect: function () { 14908 var style = this.style; 14909 14910 // Optimize, avoid normalize every time. 14911 this.__dirty && normalizeTextStyle(style, true); 14912 14913 if (!this._rect) { 14914 var text = style.text; 14915 text != null ? (text += '') : (text = ''); 14916
14917 var rect = getBoundingRect( 14918 style.text + '', 14919 style.font, 14920 style.textAlign, 14921 style.textVerticalAlign, 14922 style.textPadding, 14923 style.rich 14924 ); 14925 14926 rect.x += style.x || 0; 14927 rect.y += style.y || 0; 14928 14929 if (getStroke(style.textStroke, style.textStrokeWidth)) { 14930 var w = style.textStrokeWidth; 14931 rect.x -= w / 2; 14932 rect.y -= w / 2; 14933 rect.width += w; 14934 rect.height += w; 14935 } 14936 14937 this._rect = rect; 14938 } 14939 14940 return this._rect; 14941 } 14942 }; 14943 14944 inherits(Text, Displayable); 14945 14946 /** 14947 * é¦åè° 14948 * @module zrender/shape/Circle 14949 */ 14950 14951 var Circle = Path.extend({ 14952 14953 type: 'circle', 14954 14955 shape: { 14956 cx: 0, 14957 cy: 0, 14958 r: 0 14959 }, 14960 14961 14962 buildPath : function (ctx, shape, inBundle) { 14963 // Better stroking in ShapeBundle 14964 // Always do it may have performence issue ( fill may be 2x more cost) 14965 if (inBundle) { 14966 ctx.moveTo(shape.cx + shape.r, shape.cy); 14967 } 14968 // else { 14969 // if (ctx.allocate && !ctx.data.length) { 14970 // ctx.allocate(ctx.CMD_MEM_SIZE.A); 14971 // } 14972 // } 14973 // Better stroking in ShapeBundle 14974 // ctx.moveTo(shape.cx + shape.r, shape.cy); 14975 ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true); 14976 } 14977 }); 14978 14979// Fix weird bug in some version of IE11 (like 11.0.9600.178**), 14980// where exception "unexpected call to method or property access" 14981// might be thrown when calling ctx.fill or ctx.stroke after a path 14982// whose area size is zero is drawn and ctx.clip() is called and 14983// shadowBlur is set. See #4572, #3112, #5777. 14984// (e.g., 14985// ctx.moveTo(10, 10); 14986// ctx.lineTo(20, 10); 14987// ctx.closePath(); 14988// ctx.clip(); 14989// ctx.shadowBlur = 10; 14990// ... 14991// ctx.fill(); 14992// ) 14993 14994 var shadowTemp = [ 14995 ['shadowBlur', 0], 14996 ['shadowColor', '#000'], 14997 ['shadowOffsetX', 0], 14998 ['shadowOffsetY', 0] 14999 ]; 15000 15001 var fixClipWithShadow = function (orignalBrush) { 15002 15003 // version string can be: '11.0' 15004 return (env$1.browser.ie && env$1.browser.version >= 11) 15005 15006 ? function () { 15007 var clipPaths = this.__clipPaths; 15008 var style = this.style; 15009 var modified; 15010 15011 if (clipPaths) { 15012 for (var i = 0; i < clipPaths.length; i++) { 15013 var clipPath = clipPaths[i]; 15014 var shape = clipPath && clipPath.shape; 15015 var type = clipPath && clipPath.type; 15016 15017 if (shape && ( 15018 (type === 'sector' && shape.startAngle === shape.endAngle) 15019 || (type === 'rect' && (!shape.width || !shape.height)) 15020 )) { 15021 for (var j = 0; j < shadowTemp.length; j++) { 15022 // It is save to put shadowTemp static, because shadowTemp 15023 // will be all modified each item brush called. 15024 shadowTemp[j][2] = style[shadowTemp[j][0]]; 15025 style[shadowTemp[j][0]] = shadowTemp[j][1]; 15026 } 15027 modified = true; 15028 break; 15029 } 15030 } 15031 } 15032 15033 orignalBrush.apply(this, arguments); 15034 15035 if (modified) { 15036 for (var j = 0; j < shadowTemp.length; j++) { 15037 style[shadowTemp[j][0]] = shadowTemp[j][2]; 15038 } 15039 } 15040 } 15041 15042 : orignalBrush; 15043 }; 15044 15045 /** 15046 * éµå§è° 15047 * @module zrender/graphic/shape/Sector 15048 */ 15049 15050 var Sector = Path.extend({ 15051 15052 type: 'sector', 15053 15054 shape: { 15055 15056 cx: 0, 15057 15058 cy: 0, 15059 15060 r0: 0, 15061 15062 r: 0, 15063 15064 startAngle: 0, 15065 15066 endAngle: Math.PI * 2, 15067 15068 clockwise: true 15069 }, 15070 15071 brush: fixClipWithShadow(Path.prototype.brush), 15072 15073 buildPath: function (ctx, shape) { 15074 15075 var x = shape.cx; 15076 var y = shape.cy; 15077 var r0 = Math.max(shape.r0 || 0, 0); 15078 var r = Math.max(shape.r, 0); 15079 var startAngle = shape.startAngle; 15080 var endAngle = shape.endAngle; 15081 var clockwise = shape.clockwise; 15082 15083 var unitX = Math.cos(startAngle); 15084 var unitY = Math.sin(startAngle); 15085 15086 ctx.moveTo(unitX * r0 + x, unitY * r0 + y); 15087 15088 ctx.lineTo(unitX * r + x, unitY * r + y); 15089 15090 ctx.arc(x, y, r, startAngle, endAngle, !clockwise); 15091 15092 ctx.lineTo( 15093 Math.cos(endAngle) * r0 + x, 15094 Math.sin(endAngle) * r0 + y 15095 ); 15096 15097 if (r0 !== 0) { 15098 ctx.arc(x, y, r0, endAngle, startAngle, clockwise); 15099 } 15100 15101 ctx.closePath(); 15102 } 15103 });
15104 15105 /** 15106 * é¦åå¹ 15107 * @module zrender/graphic/shape/Ring 15108 */ 15109 15110 var Ring = Path.extend({ 15111 15112 type: 'ring', 15113 15114 shape: { 15115 cx: 0, 15116 cy: 0, 15117 r: 0, 15118 r0: 0 15119 }, 15120 15121 buildPath: function (ctx, shape) { 15122 var x = shape.cx; 15123 var y = shape.cy; 15124 var PI2 = Math.PI * 2; 15125 ctx.moveTo(x + shape.r, y); 15126 ctx.arc(x, y, shape.r, 0, PI2, false); 15127 ctx.moveTo(x + shape.r0, y); 15128 ctx.arc(x, y, shape.r0, 0, PI2, true); 15129 } 15130 }); 15131 15132 /** 15133 * Catmull-Rom spline 黿â¬å ¼å§ç»¾ï¿½ 15134 * @module zrender/shape/util/smoothSpline 15135 * @author pissang (https://www.github.com/pissang) 15136 * Kener (@Kener-鿥å², [email protected]) 15137 * errorrik ([email protected]) 15138 */ 15139 15140 /** 15141 * @inner 15142 */ 15143 function interpolate(p0, p1, p2, p3, t, t2, t3) { 15144 var v0 = (p2 - p0) * 0.5; 15145 var v1 = (p3 - p1) * 0.5; 15146 return (2 * (p1 - p2) + v0 + v1) * t3 15147 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 15148 + v0 * t + p1; 15149 } 15150 15151 /** 15152 * @alias module:zrender/shape/util/smoothSpline 15153 * @param {Array} points 绾æî椤åå£éæ®ç² 15154 * @param {boolean} isLoop 15155 * @return {Array} 15156 */ 15157 var smoothSpline = function (points, isLoop) { 15158 var len$$1 = points.length; 15159 var ret = []; 15160 15161 var distance$$1 = 0; 15162 for (var i = 1; i < len$$1; i++) { 15163 distance$$1 += distance(points[i - 1], points[i]); 15164 } 15165 15166 var segs = distance$$1 / 2; 15167 segs = segs < len$$1 ? len$$1 : segs; 15168 for (var i = 0; i < segs; i++) { 15169 var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1); 15170 var idx = Math.floor(pos); 15171 15172 var w = pos - idx; 15173 15174 var p0; 15175 var p1 = points[idx % len$$1]; 15176 var p2; 15177 var p3; 15178 if (!isLoop) { 15179 p0 = points[idx === 0 ? idx : idx - 1]; 15180 p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1];
vendor: 5,398 bytes, lines 15181-15348
15181 p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2]; 15182 } 15183 else { 15184 p0 = points[(idx - 1 + len$$1) % len$$1]; 15185 p2 = points[(idx + 1) % len$$1]; 15186 p3 = points[(idx + 2) % len$$1]; 15187 } 15188 15189 var w2 = w * w; 15190 var w3 = w * w2; 15191 15192 ret.push([ 15193 interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3), 15194 interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3) 15195 ]); 15196 } 15197 return ret; 15198 }; 15199 15200 /** 15201 * çæ¿î£çæ¿é©å©ææ´¸ç»¾ï¿½ 15202 * @module zrender/shape/util/smoothBezier 15203 * @author pissang (https://www.github.com/pissang) 15204 * Kener (@Kener-鿥å², [email protected]) 15205 * errorrik ([email protected]) 15206 */ 15207 15208 /** 15209 * çæ¿î£çæ¿é©å©ææ´¸ç»¾ï¿½ 15210 * @alias module:zrender/shape/util/smoothBezier 15211 * @param {Array} points 绾æî椤åå£éæ®ç² 15212 * @param {number} smooth éªè«ç²¦ç»å¤éª, 0-1 15213 * @param {boolean} isLoop 15214 * @param {Array} constraint çåî ¸ç» æ¥åéã§æ®éºÑåéå害éç·æ¹ªæ¶â¬æ¶îå¯é¥å¯¸æ´ é� 15215 * å§£æ¿î§ [[0, 0], [100, 100]], æ©æ¬ééåæ´¿é©æç´°æ¶ï¿½ 15216 * éç¿ éé¶æ¨¼åé¨å«å¯é¥å¯¸æ´ éæ°«ç«´æ¶îèé忤éã§å®³éç¸å¸¶éåå£é� 15217 * @param {Array} çï¼ç»éçæ½µé¨å¬å¸¶éåå£éæ®ç² 15218 */ 15219 var smoothBezier = function (points, smooth, isLoop, constraint) { 15220 var cps = []; 15221 15222 var v = []; 15223 var v1 = []; 15224 var v2 = []; 15225 var prevPoint; 15226 var nextPoint; 15227 15228 var min$$1, max$$1; 15229 if (constraint) { 15230 min$$1 = [Infinity, Infinity]; 15231 max$$1 = [-Infinity, -Infinity]; 15232 for (var i = 0, len$$1 = points.length; i < len$$1; i++) { 15233 min(min$$1, min$$1, points[i]); 15234 max(max$$1, max$$1, points[i]); 15235 } 15236 // æ¶åº¢å¯ç¹æ°±æ®éåæ´¿é©æä»éªå 泦 15237 min(min$$1, min$$1, constraint[0]); 15238 max(max$$1, max$$1, constraint[1]); 15239 } 15240 15241 for (var i = 0, len$$1 = points.length; i < len$$1; i++) { 15242 var point = points[i]; 15243 15244 if (isLoop) { 15245 prevPoint = points[i ? i - 1 : len$$1 - 1]; 15246 nextPoint = points[(i + 1) % len$$1]; 15247 } 15248 else { 15249 if (i === 0 || i === len$$1 - 1) { 15250 cps.push(clone$1(points[i])); 15251 continue; 15252 } 15253 else { 15254 prevPoint = points[i - 1]; 15255 nextPoint = points[i + 1]; 15256 } 15257 } 15258 15259 sub(v, nextPoint, prevPoint); 15260 15261 // use degree to scale the handle length 15262 scale(v, v, smooth); 15263 15264 var d0 = distance(point, prevPoint); 15265 var d1 = distance(point, nextPoint); 15266 var sum = d0 + d1; 15267 if (sum !== 0) { 15268 d0 /= sum; 15269 d1 /= sum; 15270 } 15271 15272 scale(v1, v, -d0); 15273 scale(v2, v, d1); 15274 var cp0 = add([], point, v1); 15275 var cp1 = add([], point, v2); 15276 if (constraint) { 15277 max(cp0, cp0, min$$1); 15278 min(cp0, cp0, max$$1); 15279 max(cp1, cp1, min$$1); 15280 min(cp1, cp1, max$$1); 15281 } 15282 cps.push(cp0); 15283 cps.push(cp1); 15284 } 15285 15286 if (isLoop) { 15287 cps.push(cps.shift()); 15288 } 15289 15290 return cps; 15291 }; 15292 15293 function buildPath$1(ctx, shape, closePath) { 15294 var points = shape.points; 15295 var smooth = shape.smooth; 15296 if (points && points.length >= 2) { 15297 if (smooth && smooth !== 'spline') { 15298 var controlPoints = smoothBezier( 15299 points, smooth, closePath, shape.smoothConstraint 15300 ); 15301 15302 ctx.moveTo(points[0][0], points[0][1]); 15303 var len = points.length; 15304 for (var i = 0; i < (closePath ? len : len - 1); i++) { 15305 var cp1 = controlPoints[i * 2]; 15306 var cp2 = controlPoints[i * 2 + 1]; 15307 var p = points[(i + 1) % len]; 15308 ctx.bezierCurveTo( 15309 cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1] 15310 ); 15311 } 15312 } 15313 else { 15314 if (smooth === 'spline') { 15315 points = smoothSpline(points, closePath); 15316 } 15317 15318 ctx.moveTo(points[0][0], points[0][1]); 15319 for (var i = 1, l = points.length; i < l; i++) { 15320 ctx.lineTo(points[i][0], points[i][1]); 15321 } 15322 } 15323 15324 closePath && ctx.closePath(); 15325 } 15326 } 15327 15328 /** 15329 * æ¾¶æ°³ç«è¤°ï¿½ 15330 * @module zrender/shape/Polygon 15331 */ 15332 15333 var Polygon = Path.extend({ 15334 15335 type: 'polygon', 15336 15337 shape: { 15338 points: null, 15339 15340 smooth: false, 15341 15342 smoothConstraint: null 15343 }, 15344 15345 buildPath: function (ctx, shape) { 15346 buildPath$1(ctx, shape, true); 15347 } 15348 });
15349 15350 /** 15351 * @module zrender/graphic/shape/Polyline 15352 */ 15353 15354 var Polyline = Path.extend({ 15355 15356 type: 'polyline', 15357 15358 shape: { 15359 points: null, 15360 15361 smooth: false, 15362 15363 smoothConstraint: null 15364 }, 15365 15366 style: { 15367 stroke: '#000', 15368 15369 fill: null 15370 }, 15371 15372 buildPath: function (ctx, shape) { 15373 buildPath$1(ctx, shape, false); 15374 } 15375 }); 15376 15377 /** 15378 * éâè° 15379 * @module zrender/graphic/shape/Rect 15380 */ 15381 15382 var Rect = Path.extend({ 15383 15384 type: 'rect', 15385 15386 shape: { 15387 // 宸︿ç¬é佸彸æ¶å¨¿â¬ä½¸å½¸æ¶å¬¨â¬ä½¸ä¹æ¶å¬îé¨å«å´å¯°åªç··å¨â²è´r1é乺2é乺3é乺4 15388 // rç¼âåæ¶ï¿½1 é©ç¨¿ç¶æµï¿½ [1, 1, 1, 1] 15389 // rç¼âåæ¶ç¯¬1] é©ç¨¿ç¶æµï¿½ [1, 1, 1, 1] 15390 // rç¼âåæ¶ç¯¬1, 2] é©ç¨¿ç¶æµï¿½ [1, 2, 1, 2] 15391 // rç¼âåæ¶ç¯¬1, 2, 3] é©ç¨¿ç¶æµï¿½ [1, 2, 3, 2] 15392 r: 0, 15393 15394 x: 0, 15395 y: 0, 15396 width: 0, 15397 height: 0 15398 }, 15399 15400 buildPath: function (ctx, shape) { 15401 var x = shape.x; 15402 var y = shape.y; 15403 var width = shape.width; 15404 var height = shape.height; 15405 if (!shape.r) { 15406 ctx.rect(x, y, width, height); 15407 } 15408 else { 15409 buildPath(ctx, shape); 15410 } 15411 ctx.closePath(); 15412 return; 15413 } 15414 }); 15415 15416 /** 15417 * é©å¯¸å 15418 * @module zrender/graphic/shape/Line 15419 */ 15420 15421 var Line = Path.extend({ 15422 15423 type: 'line', 15424 15425 shape: { 15426 // Start point 15427 x1: 0, 15428 y1: 0, 15429 // End point 15430 x2: 0, 15431 y2: 0, 15432 15433 percent: 1 15434 }, 15435 15436 style: { 15437 stroke: '#000', 15438 fill: null 15439 }, 15440 15441 buildPath: function (ctx, shape) { 15442 var x1 = shape.x1; 15443 var y1 = shape.y1; 15444 var x2 = shape.x2; 15445 var y2 = shape.y2; 15446 var percent = shape.percent; 15447 15448 if (percent === 0) { 15449 return; 15450 } 15451 15452 ctx.moveTo(x1, y1); 15453 15454 if (percent < 1) { 15455 x2 = x1 * (1 - percent) + x2 * percent; 15456 y2 = y1 * (1 - percent) + y2 * percent; 15457 } 15458 ctx.lineTo(x2, y2); 15459 }, 15460 15461 /** 15462 * Get point at percent 15463 * @param {number} percent 15464 * @return {Array.<number>} 15465 */ 15466 pointAt: function (p) { 15467 var shape = this.shape; 15468 return [ 15469 shape.x1 * (1 - p) + shape.x2 * p, 15470 shape.y1 * (1 - p) + shape.y2 * p 15471 ]; 15472 } 15473 }); 15474 15475 /** 15476 * çæ¿î£çææ´¸ç»¾ï¿½ 15477 * @module zrender/shape/BezierCurve 15478 */ 15479 15480 var out = []; 15481 15482 function someVectorAt(shape, t, isTangent) { 15483 var cpx2 = shape.cpx2; 15484 var cpy2 = shape.cpy2; 15485 if (cpx2 === null || cpy2 === null) { 15486 return [ 15487 (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t), 15488 (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t) 15489 ]; 15490 } 15491 else { 15492 return [ 15493 (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t), 15494 (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t) 15495 ]; 15496 } 15497 } 15498 15499 var BezierCurve = Path.extend({ 15500 15501 type: 'bezier-curve', 15502 15503 shape: { 15504 x1: 0, 15505 y1: 0, 15506 x2: 0, 15507 y2: 0, 15508 cpx1: 0, 15509 cpy1: 0, 15510 // cpx2: 0, 15511 // cpy2: 0 15512 15513 // Curve show percent, for animating 15514 percent: 1 15515 }, 15516 15517 style: { 15518 stroke: '#000', 15519 fill: null 15520 }, 15521 15522 buildPath: function (ctx, shape) { 15523 var x1 = shape.x1; 15524 var y1 = shape.y1; 15525 var x2 = shape.x2; 15526 var y2 = shape.y2; 15527 var cpx1 = shape.cpx1; 15528 var cpy1 = shape.cpy1; 15529 var cpx2 = shape.cpx2; 15530 var cpy2 = shape.cpy2; 15531 var percent = shape.percent; 15532 if (percent === 0) { 15533 return; 15534 } 15535 15536 ctx.moveTo(x1, y1);
15537 15538 if (cpx2 == null || cpy2 == null) { 15539 if (percent < 1) { 15540 quadraticSubdivide( 15541 x1, cpx1, x2, percent, out 15542 ); 15543 cpx1 = out[1]; 15544 x2 = out[2]; 15545 quadraticSubdivide( 15546 y1, cpy1, y2, percent, out 15547 ); 15548 cpy1 = out[1]; 15549 y2 = out[2]; 15550 } 15551 15552 ctx.quadraticCurveTo( 15553 cpx1, cpy1, 15554 x2, y2 15555 ); 15556 } 15557 else { 15558 if (percent < 1) { 15559 cubicSubdivide( 15560 x1, cpx1, cpx2, x2, percent, out 15561 ); 15562 cpx1 = out[1]; 15563 cpx2 = out[2]; 15564 x2 = out[3]; 15565 cubicSubdivide( 15566 y1, cpy1, cpy2, y2, percent, out 15567 ); 15568 cpy1 = out[1]; 15569 cpy2 = out[2]; 15570 y2 = out[3]; 15571 } 15572 ctx.bezierCurveTo( 15573 cpx1, cpy1, 15574 cpx2, cpy2, 15575 x2, y2 15576 ); 15577 } 15578 }, 15579 15580 /** 15581 * Get point at percent 15582 * @param {number} t 15583 * @return {Array.<number>} 15584 */ 15585 pointAt: function (t) { 15586 return someVectorAt(this.shape, t, false); 15587 }, 15588 15589 /** 15590 * Get tangent at percent 15591 * @param {number} t 15592 * @return {Array.<number>} 15593 */ 15594 tangentAt: function (t) { 15595 var p = someVectorAt(this.shape, t, true); 15596 return normalize(p, p); 15597 } 15598 }); 15599 15600 /** 15601 * é¦å姬 15602 * @module zrender/graphic/shape/Arc 15603 */ 15604 15605 var Arc = Path.extend({ 15606 15607 type: 'arc', 15608 15609 shape: { 15610 15611 cx: 0, 15612 15613 cy: 0, 15614 15615 r: 0, 15616 15617 startAngle: 0, 15618 15619 endAngle: Math.PI * 2, 15620 15621 clockwise: true 15622 }, 15623 15624 style: { 15625 15626 stroke: '#000', 15627 15628 fill: null 15629 }, 15630 15631 buildPath: function (ctx, shape) { 15632 15633 var x = shape.cx; 15634 var y = shape.cy; 15635 var r = Math.max(shape.r, 0); 15636 var startAngle = shape.startAngle; 15637 var endAngle = shape.endAngle; 15638 var clockwise = shape.clockwise; 15639 15640 var unitX = Math.cos(startAngle); 15641 var unitY = Math.sin(startAngle); 15642 15643 ctx.moveTo(unitX * r + x, unitY * r + y); 15644 ctx.arc(x, y, r, startAngle, endAngle, !clockwise); 15645 } 15646 }); 15647 15648// CompoundPath to improve performance 15649 15650 var CompoundPath = Path.extend({ 15651 15652 type: 'compound', 15653 15654 shape: { 15655 15656 paths: null 15657 }, 15658 15659 _updatePathDirty: function () { 15660 var dirtyPath = this.__dirtyPath; 15661 var paths = this.shape.paths; 15662 for (var i = 0; i < paths.length; i++) { 15663 // Mark as dirty if any subpath is dirty 15664 dirtyPath = dirtyPath || paths[i].__dirtyPath; 15665 } 15666 this.__dirtyPath = dirtyPath; 15667 this.__dirty = this.__dirty || dirtyPath; 15668 }, 15669 15670 beforeBrush: function () { 15671 this._updatePathDirty(); 15672 var paths = this.shape.paths || []; 15673 var scale = this.getGlobalScale(); 15674 // Update path scale 15675 for (var i = 0; i < paths.length; i++) { 15676 if (!paths[i].path) { 15677 paths[i].createPathProxy(); 15678 } 15679 paths[i].path.setScale(scale[0], scale[1]); 15680 } 15681 }, 15682 15683 buildPath: function (ctx, shape) { 15684 var paths = shape.paths || []; 15685 for (var i = 0; i < paths.length; i++) { 15686 paths[i].buildPath(ctx, paths[i].shape, true); 15687 } 15688 }, 15689 15690 afterBrush: function () { 15691 var paths = this.shape.paths || []; 15692 for (var i = 0; i < paths.length; i++) { 15693 paths[i].__dirtyPath = false; 15694 } 15695 }, 15696 15697 getBoundingRect: function () { 15698 this._updatePathDirty(); 15699 return Path.prototype.getBoundingRect.call(this); 15700 } 15701 });
15702 15703 /** 15704 * @param {Array.<Object>} colorStops 15705 */ 15706 var Gradient = function (colorStops) { 15707 15708 this.colorStops = colorStops || []; 15709 15710 }; 15711 15712 Gradient.prototype = { 15713 15714 constructor: Gradient, 15715 15716 addColorStop: function (offset, color) { 15717 this.colorStops.push({ 15718 15719 offset: offset, 15720 15721 color: color 15722 }); 15723 } 15724 15725 }; 15726 15727 /** 15728 * x, y, x2, y2 are all percent from 0 to 1 15729 * @param {number} [x=0] 15730 * @param {number} [y=0] 15731 * @param {number} [x2=1] 15732 * @param {number} [y2=0] 15733 * @param {Array.<Object>} colorStops 15734 * @param {boolean} [globalCoord=false] 15735 */ 15736 var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) { 15737 // Should do nothing more in this constructor. Because gradient can be 15738 // declard by `color: {type: 'linear', colorStops: ...}`, where 15739 // this constructor will not be called. 15740 15741 this.x = x == null ? 0 : x; 15742 15743 this.y = y == null ? 0 : y; 15744 15745 this.x2 = x2 == null ? 1 : x2; 15746 15747 this.y2 = y2 == null ? 0 : y2; 15748 15749 // Can be cloned 15750 this.type = 'linear'; 15751 15752 // If use global coord 15753 this.global = globalCoord || false; 15754 15755 Gradient.call(this, colorStops); 15756 }; 15757 15758 LinearGradient.prototype = { 15759 15760 constructor: LinearGradient 15761 }; 15762 15763 inherits(LinearGradient, Gradient); 15764 15765 /** 15766 * x, y, r are all percent from 0 to 1 15767 * @param {number} [x=0.5] 15768 * @param {number} [y=0.5] 15769 * @param {number} [r=0.5] 15770 * @param {Array.<Object>} [colorStops] 15771 * @param {boolean} [globalCoord=false] 15772 */ 15773 var RadialGradient = function (x, y, r, colorStops, globalCoord) { 15774 // Should do nothing more in this constructor. Because gradient can be 15775 // declard by `color: {type: 'radial', colorStops: ...}`, where 15776 // this constructor will not be called. 15777 15778 this.x = x == null ? 0.5 : x; 15779 15780 this.y = y == null ? 0.5 : y; 15781 15782 this.r = r == null ? 0.5 : r; 15783 15784 // Can be cloned 15785 this.type = 'radial'; 15786 15787 // If use global coord 15788 this.global = globalCoord || false; 15789 15790 Gradient.call(this, colorStops); 15791 }; 15792 15793 RadialGradient.prototype = { 15794 15795 constructor: RadialGradient 15796 }; 15797 15798 inherits(RadialGradient, Gradient); 15799 15800 /** 15801 * Displayable for incremental rendering. It will be rendered in a separate layer 15802 * IncrementalDisplay have too main methods. `clearDisplayables` and `addDisplayables` 15803 * addDisplayables will render the added displayables incremetally. 15804 * 15805 * It use a not clearFlag to tell the painter don't clear the layer if it's the first element. 15806 */ 15807// TODO Style override ? 15808 function IncrementalDisplayble(opts) { 15809 15810 Displayable.call(this, opts); 15811 15812 this._displayables = []; 15813 15814 this._temporaryDisplayables = []; 15815 15816 this._cursor = 0; 15817 15818 this.notClear = true; 15819 } 15820 15821 IncrementalDisplayble.prototype.incremental = true; 15822 15823 IncrementalDisplayble.prototype.clearDisplaybles = function () { 15824 this._displayables = []; 15825 this._temporaryDisplayables = []; 15826 this._cursor = 0; 15827 this.dirty(); 15828 15829 this.notClear = false; 15830 }; 15831 15832 IncrementalDisplayble.prototype.addDisplayable = function (displayable, notPersistent) { 15833 if (notPersistent) { 15834 this._temporaryDisplayables.push(displayable); 15835 } 15836 else { 15837 this._displayables.push(displayable); 15838 } 15839 this.dirty(); 15840 }; 15841 15842 IncrementalDisplayble.prototype.addDisplayables = function (displayables, notPersistent) { 15843 notPersistent = notPersistent || false; 15844 for (var i = 0; i < displayables.length; i++) { 15845 this.addDisplayable(displayables[i], notPersistent); 15846 } 15847 }; 15848 15849 IncrementalDisplayble.prototype.eachPendingDisplayable = function (cb) { 15850 for (var i = this._cursor; i < this._displayables.length; i++) { 15851 cb && cb(this._displayables[i]); 15852 } 15853 for (var i = 0; i < this._temporaryDisplayables.length; i++) { 15854 cb && cb(this._temporaryDisplayables[i]); 15855 } 15856 }; 15857
15858 IncrementalDisplayble.prototype.update = function () { 15859 this.updateTransform(); 15860 for (var i = this._cursor; i < this._displayables.length; i++) { 15861 var displayable = this._displayables[i]; 15862 // PENDING 15863 displayable.parent = this; 15864 displayable.update(); 15865 displayable.parent = null; 15866 } 15867 for (var i = 0; i < this._temporaryDisplayables.length; i++) { 15868 var displayable = this._temporaryDisplayables[i]; 15869 // PENDING 15870 displayable.parent = this; 15871 displayable.update(); 15872 displayable.parent = null; 15873 } 15874 }; 15875 15876 IncrementalDisplayble.prototype.brush = function (ctx, prevEl) { 15877 // Render persistant displayables. 15878 for (var i = this._cursor; i < this._displayables.length; i++) { 15879 var displayable = this._displayables[i]; 15880 displayable.beforeBrush && displayable.beforeBrush(ctx); 15881 displayable.brush(ctx, i === this._cursor ? null : this._displayables[i - 1]); 15882 displayable.afterBrush && displayable.afterBrush(ctx); 15883 } 15884 this._cursor = i; 15885 // Render temporary displayables. 15886 for (var i = 0; i < this._temporaryDisplayables.length; i++) { 15887 var displayable = this._temporaryDisplayables[i]; 15888 displayable.beforeBrush && displayable.beforeBrush(ctx); 15889 displayable.brush(ctx, i === 0 ? null : this._temporaryDisplayables[i - 1]); 15890 displayable.afterBrush && displayable.afterBrush(ctx); 15891 } 15892 15893 this._temporaryDisplayables = []; 15894 15895 this.notClear = true; 15896 }; 15897 15898 var m = []; 15899 IncrementalDisplayble.prototype.getBoundingRect = function () { 15900 if (!this._rect) { 15901 var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity); 15902 for (var i = 0; i < this._displayables.length; i++) { 15903 var displayable = this._displayables[i]; 15904 var childRect = displayable.getBoundingRect().clone(); 15905 if (displayable.needLocalTransform()) { 15906 childRect.applyTransform(displayable.getLocalTransform(m)); 15907 } 15908 rect.union(childRect); 15909 } 15910 this._rect = rect; 15911 } 15912 return this._rect; 15913 }; 15914 15915 IncrementalDisplayble.prototype.contain = function (x, y) { 15916 var localPos = this.transformCoordToLocal(x, y); 15917 var rect = this.getBoundingRect(); 15918 15919 if (rect.contain(localPos[0], localPos[1])) { 15920 for (var i = 0; i < this._displayables.length; i++) { 15921 var displayable = this._displayables[i]; 15922 if (displayable.contain(x, y)) { 15923 return true; 15924 } 15925 } 15926 } 15927 return false; 15928 }; 15929 15930 inherits(IncrementalDisplayble, Displayable); 15931 15932 /* 15933* Licensed to the Apache Software Foundation (ASF) under one 15934* or more contributor license agreements. See the NOTICE file 15935* distributed with this work for additional information 15936* regarding copyright ownership. The ASF licenses this file 15937* to you under the Apache License, Version 2.0 (the 15938* "License"); you may not use this file except in compliance 15939* with the License. You may obtain a copy of the License at 15940* 15941* http://www.apache.org/licenses/LICENSE-2.0 15942* 15943* Unless required by applicable law or agreed to in writing, 15944* software distributed under the License is distributed on an 15945* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15946* KIND, either express or implied. See the License for the 15947* specific language governing permissions and limitations 15948* under the License. 15949*/ 15950 15951 var round = Math.round; 15952 var mathMax$1 = Math.max; 15953 var mathMin$1 = Math.min; 15954 15955 var EMPTY_OBJ = {}; 15956 15957 /** 15958 * Extend shape with parameters 15959 */ 15960 function extendShape(opts) { 15961 return Path.extend(opts); 15962 } 15963 15964 /** 15965 * Extend path 15966 */ 15967 function extendPath(pathData, opts) { 15968 return extendFromString(pathData, opts); 15969 } 15970 15971 /** 15972 * Create a path element from path data string 15973 * @param {string} pathData 15974 * @param {Object} opts 15975 * @param {module:zrender/core/BoundingRect} rect 15976 * @param {string} [layout=cover] 'center' or 'cover' 15977 */ 15978 function makePath(pathData, opts, rect, layout) { 15979 var path = createFromString(pathData, opts);
15980 var boundingRect = path.getBoundingRect(); 15981 if (rect) { 15982 if (layout === 'center') { 15983 rect = centerGraphic(rect, boundingRect); 15984 } 15985 15986 resizePath(path, rect); 15987 } 15988 return path; 15989 } 15990 15991 /** 15992 * Create a image element from image url 15993 * @param {string} imageUrl image url 15994 * @param {Object} opts options 15995 * @param {module:zrender/core/BoundingRect} rect constrain rect 15996 * @param {string} [layout=cover] 'center' or 'cover' 15997 */ 15998 function makeImage(imageUrl, rect, layout) { 15999 var path = new ZImage({ 16000 style: { 16001 image: imageUrl, 16002 x: rect.x, 16003 y: rect.y, 16004 width: rect.width, 16005 height: rect.height 16006 }, 16007 onload: function (img) { 16008 if (layout === 'center') { 16009 var boundingRect = { 16010 width: img.width, 16011 height: img.height 16012 }; 16013 path.setStyle(centerGraphic(rect, boundingRect)); 16014 } 16015 } 16016 }); 16017 return path; 16018 } 16019 16020 /** 16021 * Get position of centered element in bounding box. 16022 * 16023 * @param {Object} rect element local bounding box 16024 * @param {Object} boundingRect constraint bounding box 16025 * @return {Object} element position containing x, y, width, and height 16026 */ 16027 function centerGraphic(rect, boundingRect) { 16028 // Set rect to center, keep width / height ratio. 16029 var aspect = boundingRect.width / boundingRect.height; 16030 var width = rect.height * aspect; 16031 var height; 16032 if (width <= rect.width) { 16033 height = rect.height; 16034 } 16035 else { 16036 width = rect.width; 16037 height = width / aspect; 16038 } 16039 var cx = rect.x + rect.width / 2; 16040 var cy = rect.y + rect.height / 2; 16041 16042 return { 16043 x: cx - width / 2, 16044 y: cy - height / 2, 16045 width: width, 16046 height: height 16047 }; 16048 } 16049 16050 var mergePath = mergePath$1; 16051 16052 /** 16053 * Resize a path to fit the rect 16054 * @param {module:zrender/graphic/Path} path 16055 * @param {Object} rect 16056 */ 16057 function resizePath(path, rect) { 16058 if (!path.applyTransform) { 16059 return; 16060 } 16061 16062 var pathRect = path.getBoundingRect(); 16063 16064 var m = pathRect.calculateTransform(rect); 16065 16066 path.applyTransform(m); 16067 } 16068 16069 /** 16070 * Sub pixel optimize line for canvas 16071 * 16072 * @param {Object} param 16073 * @param {Object} [param.shape] 16074 * @param {number} [param.shape.x1] 16075 * @param {number} [param.shape.y1] 16076 * @param {number} [param.shape.x2] 16077 * @param {number} [param.shape.y2] 16078 * @param {Object} [param.style] 16079 * @param {number} [param.style.lineWidth] 16080 * @return {Object} Modified param 16081 */ 16082 function subPixelOptimizeLine(param) { 16083 var shape = param.shape; 16084 var lineWidth = param.style.lineWidth; 16085 16086 if (round(shape.x1 * 2) === round(shape.x2 * 2)) { 16087 shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true); 16088 } 16089 if (round(shape.y1 * 2) === round(shape.y2 * 2)) { 16090 shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true); 16091 } 16092 return param; 16093 } 16094 16095 /** 16096 * Sub pixel optimize rect for canvas 16097 * 16098 * @param {Object} param 16099 * @param {Object} [param.shape] 16100 * @param {number} [param.shape.x] 16101 * @param {number} [param.shape.y] 16102 * @param {number} [param.shape.width] 16103 * @param {number} [param.shape.height] 16104 * @param {Object} [param.style] 16105 * @param {number} [param.style.lineWidth] 16106 * @return {Object} Modified param 16107 */ 16108 function subPixelOptimizeRect(param) { 16109 var shape = param.shape; 16110 var lineWidth = param.style.lineWidth; 16111 var originX = shape.x; 16112 var originY = shape.y; 16113 var originWidth = shape.width; 16114 var originHeight = shape.height; 16115 shape.x = subPixelOptimize(shape.x, lineWidth, true); 16116 shape.y = subPixelOptimize(shape.y, lineWidth, true); 16117 shape.width = Math.max( 16118 subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x, 16119 originWidth === 0 ? 0 : 1 16120 ); 16121 shape.height = Math.max( 16122 subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y, 16123 originHeight === 0 ? 0 : 1 16124 ); 16125 return param; 16126 } 16127 16128 /** 16129 * Sub pixel optimize for canvas 16130 * 16131 * @param {number} position Coordinate, such as x, y 16132 * @param {number} lineWidth Should be nonnegative integer. 16133 * @param {boolean=} positiveOrNegative Default false (negative). 16134 * @return {number} Optimized position. 16135 */ 16136 function subPixelOptimize(position, lineWidth, positiveOrNegative) { 16137 // Assure that (position + lineWidth / 2) is near integer edge, 16138 // otherwise line will be fuzzy in canvas. 16139 var doubledPosition = round(position * 2); 16140 return (doubledPosition + round(lineWidth)) % 2 === 0 16141 ? doubledPosition / 2 16142 : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2; 16143 } 16144 16145 function hasFillOrStroke(fillOrStroke) { 16146 return fillOrStroke != null && fillOrStroke != 'none'; 16147 } 16148 16149 function liftColor(color) { 16150 return typeof color === 'string' ? lift(color, -0.1) : color; 16151 } 16152 16153 /** 16154 * @private 16155 */ 16156 function cacheElementStl(el) { 16157 if (el.__hoverStlDirty) { 16158 var stroke = el.style.stroke; 16159 var fill = el.style.fill; 16160 16161 // Create hoverStyle on mouseover 16162 var hoverStyle = el.__hoverStl; 16163 hoverStyle.fill = hoverStyle.fill 16164 || (hasFillOrStroke(fill) ? liftColor(fill) : null); 16165 hoverStyle.stroke = hoverStyle.stroke 16166 || (hasFillOrStroke(stroke) ? liftColor(stroke) : null); 16167 16168 var normalStyle = {}; 16169 for (var name in hoverStyle) { 16170 // See comment in `doSingleEnterHover`. 16171 if (hoverStyle[name] != null) { 16172 normalStyle[name] = el.style[name]; 16173 } 16174 } 16175 16176 el.__normalStl = normalStyle; 16177 16178 el.__hoverStlDirty = false; 16179 } 16180 } 16181 16182 /** 16183 * @private 16184 */ 16185 function doSingleEnterHover(el) { 16186 if (el.__isHover) { 16187 return; 16188 } 16189 16190 cacheElementStl(el); 16191 16192 if (el.useHoverLayer) { 16193 el.__zr && el.__zr.addHover(el, el.__hoverStl); 16194 } 16195 else { 16196 var style = el.style; 16197 var insideRollbackOpt = style.insideRollbackOpt; 16198 16199 // Consider case: only `position: 'top'` is set on emphasis, then text 16200 // color should be returned to `autoColor`, rather than remain '#fff'. 16201 // So we should rollback then apply again after style merging. 16202 insideRollbackOpt && rollbackInsideStyle(style); 16203 16204 // styles can be: 16205 // { 16206 // label: { 16207 // show: false, 16208 // position: 'outside', 16209 // fontSize: 18 16210 // }, 16211 // emphasis: { 16212 // label: { 16213 // show: true 16214 // } 16215 // } 16216 // }, 16217 // where properties of `emphasis` may not appear in `normal`. We previously use 16218 // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`. 16219 // But consider rich text and setOption in merge mode, it is impossible to cover 16220 // all properties in merge. So we use merge mode when setting style here, where 16221 // only properties that is not `null/undefined` can be set. The disadventage: 16222 // null/undefined can not be used to remove style any more in `emphasis`. 16223 style.extendFrom(el.__hoverStl); 16224
16225 // Do not save `insideRollback`. 16226 if (insideRollbackOpt) { 16227 applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt); 16228 16229 // textFill may be rollbacked to null. 16230 if (style.textFill == null) { 16231 style.textFill = insideRollbackOpt.autoColor; 16232 } 16233 } 16234 16235 el.dirty(false); 16236 el.z2 += 1; 16237 } 16238 16239 el.__isHover = true; 16240 } 16241 16242 /** 16243 * @inner 16244 */ 16245 function doSingleLeaveHover(el) { 16246 if (!el.__isHover) { 16247 return; 16248 } 16249 16250 var normalStl = el.__normalStl; 16251 if (el.useHoverLayer) { 16252 el.__zr && el.__zr.removeHover(el); 16253 } 16254 else { 16255 // Consider null/undefined value, should use 16256 // `setStyle` but not `extendFrom(stl, true)`. 16257 normalStl && el.setStyle(normalStl); 16258 el.z2 -= 1; 16259 } 16260 16261 el.__isHover = false; 16262 } 16263 16264 /** 16265 * @inner 16266 */ 16267 function doEnterHover(el) { 16268 el.type === 'group' 16269 ? el.traverse(function (child) { 16270 if (child.type !== 'group') { 16271 doSingleEnterHover(child); 16272 } 16273 }) 16274 : doSingleEnterHover(el); 16275 } 16276 16277 function doLeaveHover(el) { 16278 el.type === 'group' 16279 ? el.traverse(function (child) { 16280 if (child.type !== 'group') { 16281 doSingleLeaveHover(child); 16282 } 16283 }) 16284 : doSingleLeaveHover(el); 16285 } 16286 16287 /** 16288 * @inner 16289 */ 16290 function setElementHoverStl(el, hoverStl) { 16291 // If element has sepcified hoverStyle, then use it instead of given hoverStyle 16292 // Often used when item group has a label element and it's hoverStyle is different 16293 el.__hoverStl = el.hoverStyle || hoverStl || {}; 16294 el.__hoverStlDirty = true; 16295 16296 if (el.__isHover) { 16297 cacheElementStl(el); 16298 } 16299 } 16300 16301 /** 16302 * @inner 16303 */ 16304 function onElementMouseOver(e) { 16305 if (this.__hoverSilentOnTouch && e.zrByTouch) { 16306 return; 16307 } 16308 16309 // Only if element is not in emphasis status 16310 !this.__isEmphasis && doEnterHover(this); 16311 } 16312 16313 /** 16314 * @inner 16315 */ 16316 function onElementMouseOut(e) { 16317 if (this.__hoverSilentOnTouch && e.zrByTouch) { 16318 return; 16319 } 16320 16321 // Only if element is not in emphasis status 16322 !this.__isEmphasis && doLeaveHover(this); 16323 } 16324 16325 /** 16326 * @inner 16327 */ 16328 function enterEmphasis() { 16329 this.__isEmphasis = true; 16330 doEnterHover(this); 16331 } 16332 16333 /** 16334 * @inner 16335 */ 16336 function leaveEmphasis() { 16337 this.__isEmphasis = false; 16338 doLeaveHover(this); 16339 } 16340 16341 /** 16342 * Set hover style of element. 16343 * This method can be called repeatly without side-effects. 16344 * @param {module:zrender/Element} el 16345 * @param {Object} [hoverStyle] 16346 * @param {Object} [opt] 16347 * @param {boolean} [opt.hoverSilentOnTouch=false] 16348 * In touch device, mouseover event will be trigger on touchstart event 16349 * (see module:zrender/dom/HandlerProxy). By this mechanism, we can 16350 * conviniently use hoverStyle when tap on touch screen without additional 16351 * code for compatibility. 16352 * But if the chart/component has select feature, which usually also use 16353 * hoverStyle, there might be conflict between 'select-highlight' and 16354 * 'hover-highlight' especially when roam is enabled (see geo for example). 16355 * In this case, hoverSilentOnTouch should be used to disable hover-highlight 16356 * on touch device. 16357 */ 16358 function setHoverStyle(el, hoverStyle, opt) { 16359 el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch; 16360 16361 el.type === 'group' 16362 ? el.traverse(function (child) { 16363 if (child.type !== 'group') { 16364 setElementHoverStl(child, hoverStyle); 16365 } 16366 }) 16367 : setElementHoverStl(el, hoverStyle); 16368 16369 // Duplicated function will be auto-ignored, see Eventful.js. 16370 el.on('mouseover', onElementMouseOver) 16371 .on('mouseout', onElementMouseOut); 16372 16373 // Emphasis, normal can be triggered manually 16374 el.on('emphasis', enterEmphasis) 16375 .on('normal', leaveEmphasis); 16376 } 16377 16378 /** 16379 * @param {Object|module:zrender/graphic/Style} normalStyle 16380 * @param {Object} emphasisStyle 16381 * @param {module:echarts/model/Model} normalModel 16382 * @param {module:echarts/model/Model} emphasisModel 16383 * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props. 16384 * @param {string|Function} [opt.defaultText] 16385 * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by 16386 * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` 16387 * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by 16388 * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` 16389 * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by 16390 * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` 16391 * @param {Object} [normalSpecified] 16392 * @param {Object} [emphasisSpecified] 16393 */ 16394 function setLabelStyle( 16395 normalStyle, emphasisStyle, 16396 normalModel, emphasisModel, 16397 opt, 16398 normalSpecified, emphasisSpecified 16399 ) { 16400 opt = opt || EMPTY_OBJ; 16401 var labelFetcher = opt.labelFetcher; 16402 var labelDataIndex = opt.labelDataIndex; 16403 var labelDimIndex = opt.labelDimIndex; 16404 16405 // This scenario, `label.normal.show = true; label.emphasis.show = false`, 16406 // is not supported util someone requests. 16407 16408 var showNormal = normalModel.getShallow('show');
16409 var showEmphasis = emphasisModel.getShallow('show'); 16410 16411 // Consider performance, only fetch label when necessary. 16412 // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set, 16413 // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`. 16414 var baseText; 16415 if (showNormal || showEmphasis) { 16416 if (labelFetcher) { 16417 baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex); 16418 } 16419 if (baseText == null) { 16420 baseText = isFunction$1(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText; 16421 } 16422 } 16423 var normalStyleText = showNormal ? baseText : null; 16424 var emphasisStyleText = showEmphasis 16425 ? retrieve2( 16426 labelFetcher 16427 ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex) 16428 : null, 16429 baseText 16430 ) 16431 : null; 16432 16433 // Optimize: If style.text is null, text will not be drawn. 16434 if (normalStyleText != null || emphasisStyleText != null) { 16435 // Always set `textStyle` even if `normalStyle.text` is null, because default 16436 // values have to be set on `normalStyle`. 16437 // If we set default values on `emphasisStyle`, consider case: 16438 // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);` 16439 // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);` 16440 // Then the 'red' will not work on emphasis. 16441 setTextStyle(normalStyle, normalModel, normalSpecified, opt); 16442 setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true); 16443 } 16444 16445 normalStyle.text = normalStyleText; 16446 emphasisStyle.text = emphasisStyleText; 16447 } 16448 16449 /** 16450 * Set basic textStyle properties. 16451 * @param {Object|module:zrender/graphic/Style} textStyle 16452 * @param {module:echarts/model/Model} model 16453 * @param {Object} [specifiedTextStyle] Can be overrided by settings in model. 16454 * @param {Object} [opt] See `opt` of `setTextStyleCommon`. 16455 * @param {boolean} [isEmphasis] 16456 */ 16457 function setTextStyle( 16458 textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis 16459 ) { 16460 setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis); 16461 specifiedTextStyle && extend(textStyle, specifiedTextStyle); 16462 textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); 16463 16464 return textStyle; 16465 } 16466 16467 /** 16468 * Set text option in the style. 16469 * @deprecated 16470 * @param {Object} textStyle 16471 * @param {module:echarts/model/Model} labelModel 16472 * @param {string|boolean} defaultColor Default text color. 16473 * If set as false, it will be processed as a emphasis style. 16474 */ 16475 function setText(textStyle, labelModel, defaultColor) { 16476 var opt = {isRectText: true}; 16477 var isEmphasis; 16478 16479 if (defaultColor === false) { 16480 isEmphasis = true; 16481 } 16482 else { 16483 // Support setting color as 'auto' to get visual color. 16484 opt.autoColor = defaultColor; 16485 } 16486 setTextStyleCommon(textStyle, labelModel, opt, isEmphasis); 16487 textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); 16488 } 16489 16490 /** 16491 * { 16492 * disableBox: boolean, Whether diable drawing box of block (outer most). 16493 * isRectText: boolean, 16494 * autoColor: string, specify a color when color is 'auto', 16495 * for textFill, textStroke, textBackgroundColor, and textBorderColor. 16496 * If autoColor specified, it is used as default textFill. 16497 * useInsideStyle: 16498 * `true`: Use inside style (textFill, textStroke, textStrokeWidth) 16499 * if `textFill` is not specified. 16500 * `false`: Do not use inside style. 16501 * `null/undefined`: use inside style if `isRectText` is true and 16502 * `textFill` is not specified and textPosition contains `'inside'`. 16503 * forceRich: boolean 16504 * } 16505 */ 16506 function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) { 16507 // Consider there will be abnormal when merge hover style to normal style if given default value. 16508 opt = opt || EMPTY_OBJ; 16509 16510 if (opt.isRectText) { 16511 var textPosition = textStyleModel.getShallow('position') 16512 || (isEmphasis ? null : 'inside'); 16513 // 'outside' is not a valid zr textPostion value, but used 16514 // in bar series, and magric type should be considered. 16515 textPosition === 'outside' && (textPosition = 'top'); 16516 textStyle.textPosition = textPosition; 16517 textStyle.textOffset = textStyleModel.getShallow('offset'); 16518 var labelRotate = textStyleModel.getShallow('rotate'); 16519 labelRotate != null && (labelRotate *= Math.PI / 180); 16520 textStyle.textRotation = labelRotate; 16521 textStyle.textDistance = retrieve2( 16522 textStyleModel.getShallow('distance'), isEmphasis ? null : 5 16523 ); 16524 } 16525 16526 var ecModel = textStyleModel.ecModel;
16527 var globalTextStyle = ecModel && ecModel.option.textStyle; 16528 16529 // Consider case: 16530 // { 16531 // data: [{ 16532 // value: 12, 16533 // label: { 16534 // rich: { 16535 // // no 'a' here but using parent 'a'. 16536 // } 16537 // } 16538 // }], 16539 // rich: { 16540 // a: { ... } 16541 // } 16542 // } 16543 var richItemNames = getRichItemNames(textStyleModel); 16544 var richResult; 16545 if (richItemNames) { 16546 richResult = {}; 16547 for (var name in richItemNames) { 16548 if (richItemNames.hasOwnProperty(name)) { 16549 // Cascade is supported in rich. 16550 var richTextStyle = textStyleModel.getModel(['rich', name]); 16551 // In rich, never `disableBox`. 16552 setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis); 16553 } 16554 } 16555 } 16556 textStyle.rich = richResult; 16557 16558 setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true); 16559 16560 if (opt.forceRich && !opt.textStyle) { 16561 opt.textStyle = {}; 16562 } 16563 16564 return textStyle; 16565 } 16566 16567// Consider case: 16568// { 16569// data: [{ 16570// value: 12, 16571// label: { 16572// rich: { 16573// // no 'a' here but using parent 'a'. 16574// } 16575// } 16576// }], 16577// rich: { 16578// a: { ... } 16579// } 16580// } 16581 function getRichItemNames(textStyleModel) { 16582 // Use object to remove duplicated names. 16583 var richItemNameMap; 16584 while (textStyleModel && textStyleModel !== textStyleModel.ecModel) { 16585 var rich = (textStyleModel.option || EMPTY_OBJ).rich; 16586 if (rich) { 16587 richItemNameMap = richItemNameMap || {}; 16588 for (var name in rich) { 16589 if (rich.hasOwnProperty(name)) { 16590 richItemNameMap[name] = 1; 16591 } 16592 } 16593 } 16594 textStyleModel = textStyleModel.parentModel; 16595 } 16596 return richItemNameMap; 16597 } 16598 16599 function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) { 16600 // In merge mode, default value should not be given. 16601 globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ; 16602 16603 textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt) 16604 || globalTextStyle.color; 16605 textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt) 16606 || globalTextStyle.textBorderColor; 16607 textStyle.textStrokeWidth = retrieve2( 16608 textStyleModel.getShallow('textBorderWidth'), 16609 globalTextStyle.textBorderWidth 16610 ); 16611 16612 if (!isEmphasis) { 16613 if (isBlock) { 16614 // Always set `insideRollback`, for clearing previous. 16615 var originalTextPosition = textStyle.textPosition; 16616 textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt); 16617 // Save original textPosition, because style.textPosition will be repalced by 16618 // real location (like [10, 30]) in zrender. 16619 textStyle.insideOriginalTextPosition = originalTextPosition; 16620 textStyle.insideRollbackOpt = opt; 16621 } 16622 16623 // Set default finally. 16624 if (textStyle.textFill == null) { 16625 textStyle.textFill = opt.autoColor; 16626 } 16627 } 16628 16629 // Do not use `getFont` here, because merge should be supported, where 16630 // part of these properties may be changed in emphasis style, and the 16631 // others should remain their original value got from normal style. 16632 textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle; 16633 textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight; 16634 textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize; 16635 textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily; 16636 16637 textStyle.textAlign = textStyleModel.getShallow('align'); 16638 textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign') 16639 || textStyleModel.getShallow('baseline'); 16640 16641 textStyle.textLineHeight = textStyleModel.getShallow('lineHeight'); 16642 textStyle.textWidth = textStyleModel.getShallow('width'); 16643 textStyle.textHeight = textStyleModel.getShallow('height'); 16644 textStyle.textTag = textStyleModel.getShallow('tag'); 16645 16646 if (!isBlock || !opt.disableBox) {
16647 textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt); 16648 textStyle.textPadding = textStyleModel.getShallow('padding'); 16649 textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt); 16650 textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth'); 16651 textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius'); 16652 16653 textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor'); 16654 textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur'); 16655 textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX'); 16656 textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY'); 16657 } 16658 16659 textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor') 16660 || globalTextStyle.textShadowColor; 16661 textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur') 16662 || globalTextStyle.textShadowBlur; 16663 textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX') 16664 || globalTextStyle.textShadowOffsetX; 16665 textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY') 16666 || globalTextStyle.textShadowOffsetY; 16667 } 16668 16669 function getAutoColor(color, opt) { 16670 return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null; 16671 } 16672 16673 function applyInsideStyle(textStyle, textPosition, opt) { 16674 var useInsideStyle = opt.useInsideStyle; 16675 var insideRollback; 16676 16677 if (textStyle.textFill == null 16678 && useInsideStyle !== false 16679 && (useInsideStyle === true 16680 || (opt.isRectText 16681 && textPosition 16682 // textPosition can be [10, 30] 16683 && typeof textPosition === 'string' 16684 && textPosition.indexOf('inside') >= 0 16685 ) 16686 ) 16687 ) { 16688 insideRollback = { 16689 textFill: null, 16690 textStroke: textStyle.textStroke, 16691 textStrokeWidth: textStyle.textStrokeWidth 16692 }; 16693 textStyle.textFill = '#fff'; 16694 // Consider text with #fff overflow its container. 16695 if (textStyle.textStroke == null) { 16696 textStyle.textStroke = opt.autoColor; 16697 textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2); 16698 } 16699 } 16700 16701 return insideRollback; 16702 } 16703 16704 function rollbackInsideStyle(style) { 16705 var insideRollback = style.insideRollback; 16706 if (insideRollback) { 16707 style.textFill = insideRollback.textFill; 16708 style.textStroke = insideRollback.textStroke; 16709 style.textStrokeWidth = insideRollback.textStrokeWidth; 16710 } 16711 } 16712 16713 function getFont(opt, ecModel) { 16714 // ecModel or default text style model. 16715 var gTextStyleModel = ecModel || ecModel.getModel('textStyle'); 16716 return trim([ 16717 // FIXME in node-canvas fontWeight is before fontStyle 16718 opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', 16719 opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', 16720 (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', 16721 opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif' 16722 ].join(' ')); 16723 } 16724 16725 function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) { 16726 if (typeof dataIndex === 'function') { 16727 cb = dataIndex; 16728 dataIndex = null; 16729 } 16730 // Do not check 'animation' property directly here. Consider this case: 16731 // animation model is an `itemModel`, whose does not have `isAnimationEnabled` 16732 // but its parent model (`seriesModel`) does. 16733 var animationEnabled = animatableModel && animatableModel.isAnimationEnabled(); 16734 16735 if (animationEnabled) { 16736 var postfix = isUpdate ? 'Update' : ''; 16737 var duration = animatableModel.getShallow('animationDuration' + postfix); 16738 var animationEasing = animatableModel.getShallow('animationEasing' + postfix); 16739 var animationDelay = animatableModel.getShallow('animationDelay' + postfix); 16740 if (typeof animationDelay === 'function') { 16741 animationDelay = animationDelay( 16742 dataIndex, 16743 animatableModel.getAnimationDelayParams 16744 ? animatableModel.getAnimationDelayParams(el, dataIndex) 16745 : null 16746 ); 16747 } 16748 if (typeof duration === 'function') { 16749 duration = duration(dataIndex); 16750 } 16751 16752 duration > 0 16753 ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb) 16754 : (el.stopAnimation(), el.attr(props), cb && cb()); 16755 } 16756 else { 16757 el.stopAnimation(); 16758 el.attr(props); 16759 cb && cb(); 16760 } 16761 } 16762 16763 /** 16764 * Update graphic element properties with or without animation according to the 16765 * configuration in series. 16766 * 16767 * Caution: this method will stop previous animation. 16768 * So if do not use this method to one element twice before
16769 * animation starts, unless you know what you are doing. 16770 * 16771 * @param {module:zrender/Element} el 16772 * @param {Object} props 16773 * @param {module:echarts/model/Model} [animatableModel] 16774 * @param {number} [dataIndex] 16775 * @param {Function} [cb] 16776 * @example 16777 * graphic.updateProps(el, { 16778 * position: [100, 100] 16779 * }, seriesModel, dataIndex, function () { console.log('Animation done!'); }); 16780 * // Or 16781 * graphic.updateProps(el, { 16782 * position: [100, 100] 16783 * }, seriesModel, function () { console.log('Animation done!'); }); 16784 */ 16785 function updateProps(el, props, animatableModel, dataIndex, cb) { 16786 animateOrSetProps(true, el, props, animatableModel, dataIndex, cb); 16787 } 16788 16789 /** 16790 * Init graphic element properties with or without animation according to the 16791 * configuration in series. 16792 * 16793 * Caution: this method will stop previous animation. 16794 * So if do not use this method to one element twice before 16795 * animation starts, unless you know what you are doing. 16796 * 16797 * @param {module:zrender/Element} el 16798 * @param {Object} props 16799 * @param {module:echarts/model/Model} [animatableModel] 16800 * @param {number} [dataIndex] 16801 * @param {Function} cb 16802 */ 16803 function initProps(el, props, animatableModel, dataIndex, cb) { 16804 animateOrSetProps(false, el, props, animatableModel, dataIndex, cb); 16805 } 16806 16807 /** 16808 * Get transform matrix of target (param target), 16809 * in coordinate of its ancestor (param ancestor) 16810 * 16811 * @param {module:zrender/mixin/Transformable} target 16812 * @param {module:zrender/mixin/Transformable} [ancestor] 16813 */ 16814 function getTransform(target, ancestor) { 16815 var mat = identity([]); 16816 16817 while (target && target !== ancestor) { 16818 mul$1(mat, target.getLocalTransform(), mat); 16819 target = target.parent; 16820 } 16821 16822 return mat; 16823 } 16824 16825 /** 16826 * Apply transform to an vertex. 16827 * @param {Array.<number>} target [x, y] 16828 * @param {Array.<number>|TypedArray.<number>|Object} transform Can be: 16829 * + Transform matrix: like [1, 0, 0, 1, 0, 0] 16830 * + {position, rotation, scale}, the same as `zrender/Transformable`. 16831 * @param {boolean=} invert Whether use invert matrix. 16832 * @return {Array.<number>} [x, y] 16833 */ 16834 function applyTransform$1(target, transform, invert$$1) { 16835 if (transform && !isArrayLike(transform)) { 16836 transform = Transformable.getLocalTransform(transform); 16837 } 16838 16839 if (invert$$1) { 16840 transform = invert([], transform); 16841 } 16842 return applyTransform([], target, transform); 16843 } 16844 16845 /** 16846 * @param {string} direction 'left' 'right' 'top' 'bottom' 16847 * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0] 16848 * @param {boolean=} invert Whether use invert matrix. 16849 * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom' 16850 */ 16851 function transformDirection(direction, transform, invert$$1) { 16852 16853 // Pick a base, ensure that transform result will not be (0, 0). 16854 var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0) 16855 ? 1 : Math.abs(2 * transform[4] / transform[0]); 16856 var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0) 16857 ? 1 : Math.abs(2 * transform[4] / transform[2]); 16858 16859 var vertex = [ 16860 direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, 16861 direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0 16862 ]; 16863 16864 vertex = applyTransform$1(vertex, transform, invert$$1); 16865 16866 return Math.abs(vertex[0]) > Math.abs(vertex[1]) 16867 ? (vertex[0] > 0 ? 'right' : 'left') 16868 : (vertex[1] > 0 ? 'bottom' : 'top'); 16869 } 16870 16871 /** 16872 * Apply group transition animation from g1 to g2. 16873 * If no animatableModel, no animation. 16874 */ 16875 function groupTransition(g1, g2, animatableModel, cb) { 16876 if (!g1 || !g2) { 16877 return; 16878 } 16879 16880 function getElMap(g) { 16881 var elMap = {}; 16882 g.traverse(function (el) { 16883 if (!el.isGroup && el.anid) { 16884 elMap[el.anid] = el; 16885 } 16886 }); 16887 return elMap; 16888 } 16889 function getAnimatableProps(el) { 16890 var obj = { 16891 position: clone$1(el.position), 16892 rotation: el.rotation 16893 }; 16894 if (el.shape) { 16895 obj.shape = extend({}, el.shape); 16896 } 16897 return obj;
16898 } 16899 var elMap1 = getElMap(g1); 16900 16901 g2.traverse(function (el) { 16902 if (!el.isGroup && el.anid) { 16903 var oldEl = elMap1[el.anid]; 16904 if (oldEl) { 16905 var newProp = getAnimatableProps(el); 16906 el.attr(getAnimatableProps(oldEl)); 16907 updateProps(el, newProp, animatableModel, el.dataIndex); 16908 } 16909 // else { 16910 // if (el.previousProps) { 16911 // graphic.updateProps 16912 // } 16913 // } 16914 } 16915 }); 16916 } 16917 16918 /** 16919 * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...] 16920 * @param {Object} rect {x, y, width, height} 16921 * @return {Array.<Array.<number>>} A new clipped points. 16922 */ 16923 function clipPointsByRect(points, rect) { 16924 return map(points, function (point) { 16925 var x = point[0]; 16926 x = mathMax$1(x, rect.x); 16927 x = mathMin$1(x, rect.x + rect.width); 16928 var y = point[1]; 16929 y = mathMax$1(y, rect.y); 16930 y = mathMin$1(y, rect.y + rect.height); 16931 return [x, y]; 16932 }); 16933 } 16934 16935 /** 16936 * @param {Object} targetRect {x, y, width, height} 16937 * @param {Object} rect {x, y, width, height} 16938 * @return {Object} A new clipped rect. If rect size are negative, return undefined. 16939 */ 16940 function clipRectByRect(targetRect, rect) { 16941 var x = mathMax$1(targetRect.x, rect.x); 16942 var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width); 16943 var y = mathMax$1(targetRect.y, rect.y); 16944 var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height); 16945 16946 if (x2 >= x && y2 >= y) { 16947 return { 16948 x: x, 16949 y: y, 16950 width: x2 - x, 16951 height: y2 - y 16952 }; 16953 } 16954 } 16955 16956 /** 16957 * @param {string} iconStr Support 'image://' or 'path://' or direct svg path. 16958 * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`. 16959 * @param {Object} [rect] {x, y, width, height} 16960 * @return {module:zrender/Element} Icon path or image element. 16961 */ 16962 function createIcon(iconStr, opt, rect) { 16963 opt = extend({rectHover: true}, opt); 16964 var style = opt.style = {strokeNoScale: true}; 16965 rect = rect || {x: -1, y: -1, width: 2, height: 2}; 16966 16967 if (iconStr) { 16968 return iconStr.indexOf('image://') === 0 16969 ? ( 16970 style.image = iconStr.slice(8), 16971 defaults(style, rect), 16972 new ZImage(opt) 16973 ) 16974 : ( 16975 makePath( 16976 iconStr.replace('path://', ''), 16977 opt, 16978 rect, 16979 'center' 16980 ) 16981 ); 16982 } 16983 } 16984 16985 16986 16987 16988 var graphic = (Object.freeze || Object)({ 16989 extendShape: extendShape, 16990 extendPath: extendPath, 16991 makePath: makePath, 16992 makeImage: makeImage, 16993 mergePath: mergePath, 16994 resizePath: resizePath, 16995 subPixelOptimizeLine: subPixelOptimizeLine, 16996 subPixelOptimizeRect: subPixelOptimizeRect, 16997 subPixelOptimize: subPixelOptimize, 16998 setHoverStyle: setHoverStyle, 16999 setLabelStyle: setLabelStyle, 17000 setTextStyle: setTextStyle, 17001 setText: setText, 17002 getFont: getFont, 17003 updateProps: updateProps, 17004 initProps: initProps, 17005 getTransform: getTransform, 17006 applyTransform: applyTransform$1, 17007 transformDirection: transformDirection, 17008 groupTransition: groupTransition, 17009 clipPointsByRect: clipPointsByRect, 17010 clipRectByRect: clipRectByRect, 17011 createIcon: createIcon, 17012 Group: Group, 17013 Image: ZImage, 17014 Text: Text, 17015 Circle: Circle, 17016 Sector: Sector, 17017 Ring: Ring, 17018 Polygon: Polygon, 17019 Polyline: Polyline, 17020 Rect: Rect, 17021 Line: Line, 17022 BezierCurve: BezierCurve, 17023 Arc: Arc,
17024 IncrementalDisplayable: IncrementalDisplayble, 17025 CompoundPath: CompoundPath, 17026 LinearGradient: LinearGradient, 17027 RadialGradient: RadialGradient, 17028 BoundingRect: BoundingRect 17029 }); 17030 17031 /* 17032* Licensed to the Apache Software Foundation (ASF) under one 17033* or more contributor license agreements. See the NOTICE file 17034* distributed with this work for additional information 17035* regarding copyright ownership. The ASF licenses this file 17036* to you under the Apache License, Version 2.0 (the 17037* "License"); you may not use this file except in compliance 17038* with the License. You may obtain a copy of the License at 17039* 17040* http://www.apache.org/licenses/LICENSE-2.0 17041* 17042* Unless required by applicable law or agreed to in writing, 17043* software distributed under the License is distributed on an 17044* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17045* KIND, either express or implied. See the License for the 17046* specific language governing permissions and limitations 17047* under the License. 17048*/ 17049 17050 var PATH_COLOR = ['textStyle', 'color']; 17051 17052 var textStyleMixin = { 17053 /** 17054 * Get color property or get color from option.textStyle.color 17055 * @param {boolean} [isEmphasis] 17056 * @return {string} 17057 */ 17058 getTextColor: function (isEmphasis) { 17059 var ecModel = this.ecModel; 17060 return this.getShallow('color') 17061 || ( 17062 (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null 17063 ); 17064 }, 17065 17066 /** 17067 * Create font string from fontStyle, fontWeight, fontSize, fontFamily 17068 * @return {string} 17069 */ 17070 getFont: function () { 17071 return getFont({ 17072 fontStyle: this.getShallow('fontStyle'), 17073 fontWeight: this.getShallow('fontWeight'), 17074 fontSize: this.getShallow('fontSize'), 17075 fontFamily: this.getShallow('fontFamily') 17076 }, this.ecModel); 17077 }, 17078 17079 getTextRect: function (text) { 17080 return getBoundingRect( 17081 text, 17082 this.getFont(),
17083 this.getShallow('align'), 17084 this.getShallow('verticalAlign') || this.getShallow('baseline'), 17085 this.getShallow('padding'), 17086 this.getShallow('rich'), 17087 this.getShallow('truncateText') 17088 ); 17089 } 17090 }; 17091 17092 /* 17093* Licensed to the Apache Software Foundation (ASF) under one 17094* or more contributor license agreements. See the NOTICE file 17095* distributed with this work for additional information 17096* regarding copyright ownership. The ASF licenses this file 17097* to you under the Apache License, Version 2.0 (the 17098* "License"); you may not use this file except in compliance 17099* with the License. You may obtain a copy of the License at 17100* 17101* http://www.apache.org/licenses/LICENSE-2.0 17102* 17103* Unless required by applicable law or agreed to in writing, 17104* software distributed under the License is distributed on an 17105* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17106* KIND, either express or implied. See the License for the 17107* specific language governing permissions and limitations 17108* under the License. 17109*/ 17110 17111 var getItemStyle = makeStyleMapper( 17112 [ 17113 ['fill', 'color'], 17114 ['stroke', 'borderColor'], 17115 ['lineWidth', 'borderWidth'], 17116 ['opacity'], 17117 ['shadowBlur'], 17118 ['shadowOffsetX'], 17119 ['shadowOffsetY'], 17120 ['shadowColor'], 17121 ['textPosition'], 17122 ['textAlign'] 17123 ] 17124 ); 17125 17126 var itemStyleMixin = { 17127 getItemStyle: function (excludes, includes) { 17128 var style = getItemStyle(this, excludes, includes); 17129 var lineDash = this.getBorderLineDash(); 17130 lineDash && (style.lineDash = lineDash); 17131 return style; 17132 }, 17133 17134 getBorderLineDash: function () { 17135 var lineType = this.get('borderType'); 17136 return (lineType === 'solid' || lineType == null) ? null 17137 : (lineType === 'dashed' ? [5, 5] : [1, 1]); 17138 } 17139 }; 17140 17141 /* 17142* Licensed to the Apache Software Foundation (ASF) under one 17143* or more contributor license agreements. See the NOTICE file 17144* distributed with this work for additional information 17145* regarding copyright ownership. The ASF licenses this file 17146* to you under the Apache License, Version 2.0 (the 17147* "License"); you may not use this file except in compliance 17148* with the License. You may obtain a copy of the License at 17149* 17150* http://www.apache.org/licenses/LICENSE-2.0 17151* 17152* Unless required by applicable law or agreed to in writing, 17153* software distributed under the License is distributed on an 17154* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17155* KIND, either express or implied. See the License for the 17156* specific language governing permissions and limitations 17157* under the License. 17158*/ 17159 17160 /** 17161 * @module echarts/model/Model 17162 */ 17163 17164 var mixin$1 = mixin; 17165 var inner = makeInner(); 17166 17167 /** 17168 * @alias module:echarts/model/Model 17169 * @constructor 17170 * @param {Object} option 17171 * @param {module:echarts/model/Model} [parentModel] 17172 * @param {module:echarts/model/Global} [ecModel] 17173 */ 17174 function Model(option, parentModel, ecModel) { 17175 /** 17176 * @type {module:echarts/model/Model} 17177 * @readOnly 17178 */ 17179 this.parentModel = parentModel; 17180 17181 /** 17182 * @type {module:echarts/model/Global} 17183 * @readOnly 17184 */ 17185 this.ecModel = ecModel; 17186 17187 /** 17188 * @type {Object} 17189 * @protected 17190 */ 17191 this.option = option; 17192 17193 // Simple optimization 17194 // if (this.init) { 17195 // if (arguments.length <= 4) { 17196 // this.init(option, parentModel, ecModel, extraOpt); 17197 // } 17198 // else { 17199 // this.init.apply(this, arguments); 17200 // } 17201 // } 17202 } 17203 17204 Model.prototype = { 17205 17206 constructor: Model, 17207 17208 /** 17209 * Model é¨å«åµæ¿®å¬ªå¯²éè¥æ 17210 * @param {Object} option 17211 */ 17212 init: null, 17213 17214 /** 17215 * æµ åº¢æé¨ï¿½ Option merge 17216 */ 17217 mergeOption: function (option) { 17218 merge(this.option, option, true); 17219 }, 17220 17221 /** 17222 * @param {string|Array.<string>} path 17223 * @param {boolean} [ignoreParent=false] 17224 * @return {*} 17225 */ 17226 get: function (path, ignoreParent) { 17227 if (path == null) { 17228 return this.option; 17229 } 17230 17231 return doGet( 17232 this.option, 17233 this.parsePath(path), 17234 !ignoreParent && getParent(this, path) 17235 ); 17236 }, 17237 17238 /** 17239 * @param {string} key 17240 * @param {boolean} [ignoreParent=false] 17241 * @return {*} 17242 */ 17243 getShallow: function (key, ignoreParent) { 17244 var option = this.option; 17245 17246 var val = option == null ? option : option[key]; 17247 var parentModel = !ignoreParent && getParent(this, key); 17248 if (val == null && parentModel) { 17249 val = parentModel.getShallow(key); 17250 } 17251 return val; 17252 }, 17253 17254 /** 17255 * @param {string|Array.<string>} [path] 17256 * @param {module:echarts/model/Model} [parentModel] 17257 * @return {module:echarts/model/Model} 17258 */ 17259 getModel: function (path, parentModel) { 17260 var obj = path == null 17261 ? this.option 17262 : doGet(this.option, path = this.parsePath(path)); 17263 17264 var thisParentModel; 17265 parentModel = parentModel || ( 17266 (thisParentModel = getParent(this, path)) 17267 && thisParentModel.getModel(path) 17268 ); 17269 17270 return new Model(obj, parentModel, this.ecModel); 17271 }, 17272 17273 /** 17274 * If model has option 17275 */ 17276 isEmpty: function () { 17277 return this.option == null; 17278 }, 17279 17280 restoreData: function () {}, 17281 17282 // Pending 17283 clone: function () { 17284 var Ctor = this.constructor; 17285 return new Ctor(clone(this.option)); 17286 }, 17287 17288 setReadOnly: function (properties) { 17289 // clazzUtil.setReadOnly(this, properties); 17290 }, 17291 17292 // If path is null/undefined, return null/undefined. 17293 parsePath: function(path) { 17294 if (typeof path === 'string') { 17295 path = path.split('.'); 17296 } 17297 return path; 17298 }, 17299 17300 /** 17301 * @param {Function} getParentMethod 17302 * param {Array.<string>|string} path 17303 * return {module:echarts/model/Model} 17304 */ 17305 customizeGetParent: function (getParentMethod) { 17306 inner(this).getParent = getParentMethod; 17307 }, 17308 17309 isAnimationEnabled: function () { 17310 if (!env$1.node) { 17311 if (this.option.animation != null) { 17312 return !!this.option.animation; 17313 } 17314 else if (this.parentModel) { 17315 return this.parentModel.isAnimationEnabled(); 17316 } 17317 } 17318 } 17319 17320 }; 17321 17322 function doGet(obj, pathArr, parentModel) { 17323 for (var i = 0; i < pathArr.length; i++) { 17324 // Ignore empty 17325 if (!pathArr[i]) { 17326 continue; 17327 } 17328 // obj could be number/string/... (like 0) 17329 obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null; 17330 if (obj == null) { 17331 break; 17332 } 17333 } 17334 if (obj == null && parentModel) { 17335 obj = parentModel.get(pathArr); 17336 } 17337 return obj;
17338 } 17339 17340// `path` can be null/undefined 17341 function getParent(model, path) { 17342 var getParentMethod = inner(model).getParent; 17343 return getParentMethod ? getParentMethod.call(model, path) : model.parentModel; 17344 } 17345 17346// Enable Model.extend. 17347 enableClassExtend(Model); 17348 enableClassCheck(Model); 17349 17350 mixin$1(Model, lineStyleMixin); 17351 mixin$1(Model, areaStyleMixin); 17352 mixin$1(Model, textStyleMixin); 17353 mixin$1(Model, itemStyleMixin); 17354 17355 /* 17356* Licensed to the Apache Software Foundation (ASF) under one 17357* or more contributor license agreements. See the NOTICE file 17358* distributed with this work for additional information 17359* regarding copyright ownership. The ASF licenses this file 17360* to you under the Apache License, Version 2.0 (the 17361* "License"); you may not use this file except in compliance 17362* with the License. You may obtain a copy of the License at 17363* 17364* http://www.apache.org/licenses/LICENSE-2.0 17365* 17366* Unless required by applicable law or agreed to in writing, 17367* software distributed under the License is distributed on an 17368* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17369* KIND, either express or implied. See the License for the 17370* specific language governing permissions and limitations 17371* under the License. 17372*/ 17373 17374 var base = 0; 17375 17376 /** 17377 * @public 17378 * @param {string} type 17379 * @return {string} 17380 */ 17381 function getUID(type) { 17382 // Considering the case of crossing js context, 17383 // use Math.random to make id as unique as possible. 17384 return [(type || ''), base++, Math.random().toFixed(5)].join('_'); 17385 } 17386 17387 /** 17388 * @inner 17389 */ 17390 function enableSubTypeDefaulter(entity) { 17391 17392 var subTypeDefaulters = {}; 17393 17394 entity.registerSubTypeDefaulter = function (componentType, defaulter) { 17395 componentType = parseClassType$1(componentType); 17396 subTypeDefaulters[componentType.main] = defaulter; 17397 }; 17398 17399 entity.determineSubType = function (componentType, option) { 17400 var type = option.type; 17401 if (!type) { 17402 var componentTypeMain = parseClassType$1(componentType).main; 17403 if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) { 17404 type = subTypeDefaulters[componentTypeMain](option); 17405 } 17406 } 17407 return type; 17408 }; 17409 17410 return entity; 17411 } 17412 17413 /** 17414 * Topological travel on Activity Network (Activity On Vertices).
17415 * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis']. 17416 * 17417 * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology. 17418 * 17419 * If there is circle dependencey, Error will be thrown. 17420 * 17421 */ 17422 function enableTopologicalTravel(entity, dependencyGetter) { 17423 17424 /** 17425 * @public 17426 * @param {Array.<string>} targetNameList Target Component type list. 17427 * Can be ['aa', 'bb', 'aa.xx'] 17428 * @param {Array.<string>} fullNameList By which we can build dependency graph. 17429 * @param {Function} callback Params: componentType, dependencies. 17430 * @param {Object} context Scope of callback. 17431 */ 17432 entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) { 17433 if (!targetNameList.length) { 17434 return; 17435 } 17436 17437 var result = makeDepndencyGraph(fullNameList); 17438 var graph = result.graph; 17439 var stack = result.noEntryList; 17440 17441 var targetNameSet = {}; 17442 each$1(targetNameList, function (name) { 17443 targetNameSet[name] = true; 17444 }); 17445 17446 while (stack.length) { 17447 var currComponentType = stack.pop(); 17448 var currVertex = graph[currComponentType]; 17449 var isInTargetNameSet = !!targetNameSet[currComponentType]; 17450 if (isInTargetNameSet) { 17451 callback.call(context, currComponentType, currVertex.originalDeps.slice()); 17452 delete targetNameSet[currComponentType]; 17453 } 17454 each$1( 17455 currVertex.successor, 17456 isInTargetNameSet ? removeEdgeAndAdd : removeEdge 17457 ); 17458 } 17459 17460 each$1(targetNameSet, function () { 17461 throw new Error('Circle dependency may exists'); 17462 }); 17463 17464 function removeEdge(succComponentType) { 17465 graph[succComponentType].entryCount--; 17466 if (graph[succComponentType].entryCount === 0) { 17467 stack.push(succComponentType); 17468 } 17469 } 17470 17471 // Consider this case: legend depends on series, and we call 17472 // chart.setOption({series: [...]}), where only series is in option. 17473 // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will 17474 // not be called, but only sereis.mergeOption is called. Thus legend 17475 // have no chance to update its local record about series (like which 17476 // name of series is available in legend). 17477 function removeEdgeAndAdd(succComponentType) { 17478 targetNameSet[succComponentType] = true; 17479 removeEdge(succComponentType); 17480 } 17481 }; 17482 17483 /** 17484 * DepndencyGraph: {Object} 17485 * key: conponentType, 17486 * value: { 17487 * successor: [conponentTypes...], 17488 * originalDeps: [conponentTypes...], 17489 * entryCount: {number} 17490 * } 17491 */ 17492 function makeDepndencyGraph(fullNameList) { 17493 var graph = {}; 17494 var noEntryList = []; 17495 17496 each$1(fullNameList, function (name) { 17497 17498 var thisItem = createDependencyGraphItem(graph, name); 17499 var originalDeps = thisItem.originalDeps = dependencyGetter(name); 17500 17501 var availableDeps = getAvailableDependencies(originalDeps, fullNameList); 17502 thisItem.entryCount = availableDeps.length; 17503 if (thisItem.entryCount === 0) { 17504 noEntryList.push(name); 17505 } 17506 17507 each$1(availableDeps, function (dependentName) { 17508 if (indexOf(thisItem.predecessor, dependentName) < 0) { 17509 thisItem.predecessor.push(dependentName); 17510 } 17511 var thatItem = createDependencyGraphItem(graph, dependentName); 17512 if (indexOf(thatItem.successor, dependentName) < 0) { 17513 thatItem.successor.push(name); 17514 } 17515 }); 17516 }); 17517 17518 return {graph: graph, noEntryList: noEntryList}; 17519 } 17520 17521 function createDependencyGraphItem(graph, name) { 17522 if (!graph[name]) { 17523 graph[name] = {predecessor: [], successor: []}; 17524 } 17525 return graph[name]; 17526 } 17527 17528 function getAvailableDependencies(originalDeps, fullNameList) { 17529 var availableDeps = []; 17530 each$1(originalDeps, function (dep) { 17531 indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep); 17532 }); 17533 return availableDeps; 17534 } 17535 } 17536 17537 /* 17538* Licensed to the Apache Software Foundation (ASF) under one 17539* or more contributor license agreements. See the NOTICE file 17540* distributed with this work for additional information 17541* regarding copyright ownership. The ASF licenses this file 17542* to you under the Apache License, Version 2.0 (the 17543* "License");
17543 you may not use this file except in compliance 17544* with the License. You may obtain a copy of the License at 17545* 17546* http://www.apache.org/licenses/LICENSE-2.0 17547* 17548* Unless required by applicable law or agreed to in writing, 17549* software distributed under the License is distributed on an 17550* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17551* KIND, either express or implied. See the License for the 17552* specific language governing permissions and limitations 17553* under the License. 17554*/ 17555 17556 var RADIAN_EPSILON = 1e-4; 17557 17558 function _trim(str) { 17559 return str.replace(/^\s+/, '').replace(/\s+$/, ''); 17560 } 17561 17562 /** 17563 * Linear mapping a value from domain to range 17564 * @memberOf module:echarts/util/number 17565 * @param {(number|Array.<number>)} val 17566 * @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1] 17567 * @param {Array.<number>} range Range extent range[0] can be bigger than range[1] 17568 * @param {boolean} clamp 17569 * @return {(number|Array.<number>} 17570 */ 17571 function linearMap(val, domain, range, clamp) { 17572 var subDomain = domain[1] - domain[0]; 17573 var subRange = range[1] - range[0]; 17574 17575 if (subDomain === 0) { 17576 return subRange === 0 17577 ? range[0] 17578 : (range[0] + range[1]) / 2; 17579 } 17580 17581 // Avoid accuracy problem in edge, such as 17582 // 146.39 - 62.83 === 83.55999999999999. 17583 // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError 17584 // It is a little verbose for efficiency considering this method 17585 // is a hotspot. 17586 if (clamp) { 17587 if (subDomain > 0) { 17588 if (val <= domain[0]) { 17589 return range[0]; 17590 } 17591 else if (val >= domain[1]) { 17592 return range[1]; 17593 } 17594 } 17595 else { 17596 if (val >= domain[0]) { 17597 return range[0]; 17598 } 17599 else if (val <= domain[1]) { 17600 return range[1]; 17601 } 17602 } 17603 } 17604 else { 17605 if (val === domain[0]) { 17606 return range[0]; 17607 } 17608 if (val === domain[1]) { 17609 return range[1]; 17610 } 17611 } 17612 17613 return (val - domain[0]) / subDomain * subRange + range[0]; 17614 } 17615 17616 /** 17617 * Convert a percent string to absolute number. 17618 * Returns NaN if percent is not a valid string or number 17619 * @memberOf module:echarts/util/number 17620 * @param {string|number} percent 17621 * @param {number} all 17622 * @return {number} 17623 */ 17624 function parsePercent$1(percent, all) { 17625 switch (percent) { 17626 case 'center': 17627 case 'middle': 17628 percent = '50%'; 17629 break; 17630 case 'left': 17631 case 'top': 17632 percent = '0%'; 17633 break; 17634 case 'right': 17635 case 'bottom': 17636 percent = '100%'; 17637 break; 17638 } 17639 if (typeof percent === 'string') { 17640 if (_trim(percent).match(/%$/)) { 17641 return parseFloat(percent) / 100 * all; 17642 } 17643 17644 return parseFloat(percent); 17645 } 17646 17647 return percent == null ? NaN : +percent; 17648 } 17649 17650 /** 17651 * (1) Fix rounding error of float numbers. 17652 * (2) Support return string to avoid scientific notation like '3.5e-7'. 17653 * 17654 * @param {number} x 17655 * @param {number} [precision] 17656 * @param {boolean} [returnStr] 17657 * @return {number|string} 17658 */ 17659 function round$1(x, precision, returnStr) { 17660 if (precision == null) { 17661 precision = 10; 17662 } 17663 // Avoid range error 17664 precision = Math.min(Math.max(0, precision), 20); 17665 x = (+x).toFixed(precision); 17666 return returnStr ? x : +x; 17667 } 17668 17669 function asc(arr) { 17670 arr.sort(function (a, b) { 17671 return a - b; 17672 }); 17673 return arr; 17674 } 17675 17676 /** 17677 * Get precision 17678 * @param {number} val 17679 */ 17680 function getPrecision(val) { 17681 val = +val; 17682 if (isNaN(val)) { 17683 return 0; 17684 }
vendor: 4,213 bytes, lines 17685-17814
17685 // It is much faster than methods converting number to string as follows 17686 // var tmp = val.toString(); 17687 // return tmp.length - 1 - tmp.indexOf('.'); 17688 // especially when precision is low 17689 var e = 1; 17690 var count = 0; 17691 while (Math.round(val * e) / e !== val) { 17692 e *= 10; 17693 count++; 17694 } 17695 return count; 17696 } 17697 17698 /** 17699 * @param {string|number} val 17700 * @return {number} 17701 */ 17702 function getPrecisionSafe(val) { 17703 var str = val.toString(); 17704 17705 // Consider scientific notation: '3.4e-12' '3.4e+12' 17706 var eIndex = str.indexOf('e'); 17707 if (eIndex > 0) { 17708 var precision = +str.slice(eIndex + 1); 17709 return precision < 0 ? -precision : 0; 17710 } 17711 else { 17712 var dotIndex = str.indexOf('.'); 17713 return dotIndex < 0 ? 0 : str.length - 1 - dotIndex; 17714 } 17715 } 17716 17717 /** 17718 * Minimal dicernible data precisioin according to a single pixel. 17719 * 17720 * @param {Array.<number>} dataExtent 17721 * @param {Array.<number>} pixelExtent 17722 * @return {number} precision 17723 */ 17724 function getPixelPrecision(dataExtent, pixelExtent) { 17725 var log = Math.log; 17726 var LN10 = Math.LN10; 17727 var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10); 17728 var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); 17729 // toFixed() digits argument must be between 0 and 20. 17730 var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20); 17731 return !isFinite(precision) ? 20 : precision; 17732 } 17733 17734 /** 17735 * Get a data of given precision, assuring the sum of percentages 17736 * in valueList is 1. 17737 * The largest remainer method is used. 17738 * https://en.wikipedia.org/wiki/Largest_remainder_method 17739 * 17740 * @param {Array.<number>} valueList a list of all data 17741 * @param {number} idx index of the data to be processed in valueList 17742 * @param {number} precision integer number showing digits of precision 17743 * @return {number} percent ranging from 0 to 100 17744 */ 17745 function getPercentWithPrecision(valueList, idx, precision) { 17746 if (!valueList[idx]) { 17747 return 0; 17748 } 17749 17750 var sum = reduce(valueList, function (acc, val) { 17751 return acc + (isNaN(val) ? 0 : val); 17752 }, 0); 17753 if (sum === 0) { 17754 return 0; 17755 } 17756 17757 var digits = Math.pow(10, precision); 17758 var votesPerQuota = map(valueList, function (val) { 17759 return (isNaN(val) ? 0 : val) / sum * digits * 100; 17760 }); 17761 var targetSeats = digits * 100; 17762 17763 var seats = map(votesPerQuota, function (votes) { 17764 // Assign automatic seats. 17765 return Math.floor(votes); 17766 }); 17767 var currentSum = reduce(seats, function (acc, val) { 17768 return acc + val; 17769 }, 0); 17770 17771 var remainder = map(votesPerQuota, function (votes, idx) { 17772 return votes - seats[idx]; 17773 }); 17774 17775 // Has remainding votes. 17776 while (currentSum < targetSeats) { 17777 // Find next largest remainder. 17778 var max = Number.NEGATIVE_INFINITY; 17779 var maxId = null; 17780 for (var i = 0, len = remainder.length; i < len; ++i) { 17781 if (remainder[i] > max) { 17782 max = remainder[i]; 17783 maxId = i; 17784 } 17785 } 17786 17787 // Add a vote to max remainder. 17788 ++seats[maxId]; 17789 remainder[maxId] = 0; 17790 ++currentSum; 17791 } 17792 17793 return seats[idx] / digits; 17794 } 17795 17796// Number.MAX_SAFE_INTEGER, ie do not support. 17797 var MAX_SAFE_INTEGER = 9007199254740991; 17798 17799 /** 17800 * To 0 - 2 * PI, considering negative radian. 17801 * @param {number} radian 17802 * @return {number} 17803 */ 17804 function remRadian(radian) { 17805 var pi2 = Math.PI * 2; 17806 return (radian % pi2 + pi2) % pi2; 17807 } 17808 17809 /** 17810 * @param {type} radian 17811 * @return {boolean} 17812 */ 17813 function isRadianAroundZero(val) { 17814 return val >
17814 -RADIAN_EPSILON && val < RADIAN_EPSILON; 17815 } 17816 17817 var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line 17818 17819 /** 17820 * @param {string|Date|number} value These values can be accepted: 17821 * + An instance of Date, represent a time in its own time zone. 17822 * + Or string in a subset of ISO 8601, only including: 17823 * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06', 17824 * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123', 17825 * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00', 17826 * all of which will be treated as local time if time zone is not specified 17827 * (see <https://momentjs.com/>). 17828 * + Or other string format, including (all of which will be treated as loacal time): 17829 * '2012', '2012-3-1', '2012/3/1', '2012/03/01', 17830 * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123' 17831 * + a timestamp, which represent a time in UTC. 17832 * @return {Date} date 17833 */ 17834 function parseDate(value) { 17835 if (value instanceof Date) { 17836 return value; 17837 } 17838 else if (typeof value === 'string') { 17839 // Different browsers parse date in different way, so we parse it manually. 17840 // Some other issues: 17841 // new Date('1970-01-01') is UTC, 17842 // new Date('1970/01/01') and new Date('1970-1-01') is local. 17843 // See issue #3623 17844 var match = TIME_REG.exec(value); 17845 17846 if (!match) { 17847 // return Invalid Date. 17848 return new Date(NaN); 17849 } 17850 17851 // Use local time when no timezone offset specifed. 17852 if (!match[8]) { 17853 // match[n] can only be string or undefined. 17854 // But take care of '12' + 1 => '121'. 17855 return new Date( 17856 +match[1], 17857 +(match[2] || 1) - 1, 17858 +match[3] || 1, 17859 +match[4] || 0, 17860 +(match[5] || 0), 17861 +match[6] || 0, 17862 +match[7] || 0 17863 ); 17864 } 17865 // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time, 17866 // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment). 17867 // For example, system timezone is set as "Time Zone: America/Toronto", 17868 // then these code will get different result: 17869 // `new Date(1478411999999).getTimezoneOffset(); // get 240` 17870 // `new Date(1478412000000).getTimezoneOffset(); // get 300` 17871 // So we should not use `new Date`, but use `Date.UTC`. 17872 else { 17873 var hour = +match[4] || 0; 17874 if (match[8].toUpperCase() !== 'Z') { 17875 hour -= match[8].slice(0, 3); 17876 } 17877 return new Date(Date.UTC( 17878 +match[1], 17879 +(match[2] || 1) - 1, 17880 +match[3] || 1, 17881 hour, 17882 +(match[5] || 0), 17883 +match[6] || 0, 17884 +match[7] || 0 17885 )); 17886 } 17887 } 17888 else if (value == null) { 17889 return new Date(NaN); 17890 } 17891 17892 return new Date(Math.round(value)); 17893 } 17894 17895 /** 17896 * Quantity of a number. e.g. 0.1, 1, 10, 100 17897 * 17898 * @param {number} val 17899 * @return {number} 17900 */ 17901 function quantity(val) { 17902 return Math.pow(10, quantityExponent(val)); 17903 } 17904 17905 function quantityExponent(val) { 17906 return Math.floor(Math.log(val) / Math.LN10); 17907 } 17908 17909 /** 17910 * find a 饿¸iceé¥ï¿½ number approximately equal to x. Round the number if round = true, 17911 * take ceiling if round = false. The primary observation is that the 饿¸icesté¥ï¿½ 17912 * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers. 17913 * 17914 * See "Nice Numbers for Graph Labels" of Graphic Gems. 17915 * 17916 * @param {number} val Non-negative value. 17917 * @param {boolean} round 17918 * @return {number} 17919 */ 17920 function nice(val, round) { 17921 var exponent = quantityExponent(val); 17922 var exp10 = Math.pow(10, exponent); 17923 var f = val / exp10; // 1 <= f < 10 17924 var nf; 17925 if (round) { 17926 if (f < 1.5) { nf = 1; } 17927 else if (f < 2.5) { nf = 2; } 17928 else if (f < 4) { nf = 3; } 17929 else if (f < 7) { nf = 5; } 17930 else { nf = 10; } 17931 } 17932 else { 17933 if (f < 1) { nf = 1; } 17934 else if (f < 2) { nf = 2; } 17935 else if (f < 3) { nf = 3; } 17936 else if (f < 5) { nf = 5; } 17937 else { nf = 10; } 17938 } 17939 val = nf * exp10; 17940 17941 // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754). 17942 // 20 is the uppper bound of toFixed. 17943 return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val; 17944 } 17945 17946 /** 17947 * Order intervals asc, and split them when overlap. 17948 * expect(numberUtil.reformIntervals([ 17949 * {interval: [18, 62], close: [1, 1]}, 17950 * {interval: [-Infinity, -70], close: [0, 0]}, 17951 * {interval: [-70, -26], close: [1, 1]}, 17952 * {interval: [-26, 18], close: [1, 1]}, 17953 * {interval: [62, 150], close: [1, 1]}, 17954 * {interval: [106, 150], close: [1, 1]}, 17955 * {interval: [150, Infinity], close: [0, 0]} 17956 * ])).toEqual([ 17957 * {interval: [-Infinity, -70], close: [0, 0]}, 17958 * {interval: [-70, -26], close: [1, 1]}, 17959 * {interval: [-26, 18], close: [0, 1]}, 17960 * {interval: [18, 62], close: [0, 1]}, 17961 * {interval: [62, 150], close: [0, 1]}, 17962 * {interval: [150, Infinity], close: [0, 0]} 17963 * ]); 17964 * @param {Array.<Object>} list, where `close` mean open or close 17965 * of the interval, and Infinity can be used. 17966 * @return {Array.<Object>} The origin list, which has been reformed. 17967 */ 17968 function reformIntervals(list) { 17969 list.sort(function (a, b) { 17970 return littleThan(a, b, 0) ? -1 : 1; 17971 }); 17972 17973 var curr = -Infinity; 17974 var currClose = 1; 17975 for (var i = 0; i < list.length;) { 17976 var interval = list[i].interval; 17977 var close = list[i].close; 17978 17979 for (var lg = 0; lg < 2; lg++) { 17980 if (interval[lg] <= curr) { 17981 interval[lg] = curr; 17982 close[lg] = !lg ? 1 - currClose : 1; 17983 } 17984 curr = interval[lg]; 17985 currClose = close[lg]; 17986 } 17987 17988 if (interval[0] === interval[1] && close[0] * close[1] !== 1) { 17989 list.splice(i, 1); 17990 } 17991 else { 17992 i++; 17993 } 17994 } 17995 17996 return list; 17997 17998 function littleThan(a, b, lg) { 17999 return a.interval[lg] < b.interval[lg] 18000 || ( 18001 a.interval[lg] === b.interval[lg] 18002 && ( 18003 (a.close[lg] - b.close[lg] === (!lg ? 1 : -1)) 18004 || (!lg && littleThan(a, b, 1)) 18005 ) 18006 ); 18007 } 18008 } 18009 18010 /** 18011 * parseFloat NaNs numeric-cast false positives (null|true|false|"") 18012 * ...but misinterprets leading-number strings, particularly hex literals ("0x...") 18013 * subtraction forces infinities to NaN 18014 * 18015 * @param {*} v 18016 * @return {boolean} 18017 */ 18018 function isNumeric(v) { 18019 return v - parseFloat(v) >= 0; 18020 } 18021 18022 18023 var number = (Object.freeze || Object)({ 18024 linearMap: linearMap, 18025 parsePercent: parsePercent$1, 18026 round: round$1, 18027 asc: asc, 18028 getPrecision: getPrecision, 18029 getPrecisionSafe: getPrecisionSafe, 18030 getPixelPrecision: getPixelPrecision, 18031 getPercentWithPrecision: getPercentWithPrecision, 18032 MAX_SAFE_INTEGER: MAX_SAFE_INTEGER, 18033 remRadian: remRadian, 18034 isRadianAroundZero: isRadianAroundZero, 18035 parseDate: parseDate, 18036 quantity: quantity, 18037 nice: nice, 18038 reformIntervals: reformIntervals, 18039 isNumeric: isNumeric 18040 });
18041 18042 /* 18043* Licensed to the Apache Software Foundation (ASF) under one 18044* or more contributor license agreements. See the NOTICE file 18045* distributed with this work for additional information 18046* regarding copyright ownership. The ASF licenses this file 18047* to you under the Apache License, Version 2.0 (the 18048* "License"); you may not use this file except in compliance 18049* with the License. You may obtain a copy of the License at 18050* 18051* http://www.apache.org/licenses/LICENSE-2.0 18052* 18053* Unless required by applicable law or agreed to in writing, 18054* software distributed under the License is distributed on an 18055* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18056* KIND, either express or implied. See the License for the 18057* specific language governing permissions and limitations 18058* under the License. 18059*/ 18060 18061 /** 18062 * å§£å¿ç¬æµ£å¶ç²¯çã å§,éç ç´¡é� 18063 * @param {string|number} x 18064 * @return {string} 18065 */ 18066 function addCommas(x) { 18067 if (isNaN(x)) { 18068 return '-'; 18069 } 18070 x = (x + '').split('.'); 18071 return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,') 18072 + (x.length > 1 ? ('.' + x[1]) : ''); 18073 } 18074 18075 /** 18076 * @param {string} str 18077 * @param {boolean} [upperCaseFirst=false] 18078 * @return {string} str 18079 */ 18080 function toCamelCase(str, upperCaseFirst) { 18081 str = (str || '').toLowerCase().replace(/-(.)/g, function(match, group1) { 18082 return group1.toUpperCase(); 18083 }); 18084 18085 if (upperCaseFirst && str) { 18086 str = str.charAt(0).toUpperCase() + str.slice(1); 18087 } 18088 18089 return str; 18090 } 18091 18092 var normalizeCssArray$1 = normalizeCssArray; 18093 18094 18095 var replaceReg = /([&<>"'])/g; 18096 var replaceMap = { 18097 '&': '&', 18098 '<': '<', 18099 '>': '>', 18100 '"': '"', 18101 '\'': ''' 18102 }; 18103 18104 function encodeHTML(source) { 18105 return source == null 18106 ? '' 18107 : (source + '').replace(replaceReg, function (str, c) { 18108 return replaceMap[c]; 18109 }); 18110 } 18111 18112 var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; 18113 18114 var wrapVar = function (varName, seriesIdx) { 18115 return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}'; 18116 }; 18117 18118 /** 18119 * Template formatter 18120 * @param {string} tpl 18121 * @param {Array.<Object>|Object} paramsList 18122 * @param {boolean} [encode=false] 18123 * @return {string} 18124 */ 18125 function formatTpl(tpl, paramsList, encode) { 18126 if (!isArray(paramsList)) { 18127 paramsList = [paramsList]; 18128 } 18129 var seriesLen = paramsList.length; 18130 if (!seriesLen) { 18131 return ''; 18132 } 18133 18134 var $vars = paramsList[0].$vars || []; 18135 for (var i = 0; i < $vars.length; i++) { 18136 var alias = TPL_VAR_ALIAS[i]; 18137 tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0)); 18138 } 18139 for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) { 18140 for (var k = 0; k < $vars.length; k++) { 18141 var val = paramsList[seriesIdx][$vars[k]]; 18142 tpl = tpl.replace( 18143 wrapVar(TPL_VAR_ALIAS[k], seriesIdx), 18144 encode ? encodeHTML(val) : val 18145 ); 18146 } 18147 } 18148 18149 return tpl; 18150 } 18151 18152 /** 18153 * simple Template formatter 18154 * 18155 * @param {string} tpl 18156 * @param {Object} param 18157 * @param {boolean} [encode=false] 18158 * @return {string} 18159 */ 18160 function formatTplSimple(tpl, param, encode) { 18161 each$1(param, function (value, key) { 18162 tpl = tpl.replace( 18163 '{' + key + '}', 18164 encode ? encodeHTML(value) : value 18165 ); 18166 }); 18167 return tpl; 18168 } 18169 18170 /** 18171 * @param {Object|string} [opt] If string, means color. 18172 * @param {string} [opt.color] 18173 * @param {string} [opt.extraCssText] 18174 * @param {string} [opt.type='item'] 'item' or 'subItem' 18175 * @return {string} 18176 */ 18177 function getTooltipMarker(opt, extraCssText) { 18178 opt = isString(opt) ? {color: opt, extraCssText: extraCssText} : (opt || {}); 18179 var color = opt.color; 18180 var type = opt.type; 18181 var extraCssText = opt.extraCssText; 18182 18183 if (!color) { 18184 return ''; 18185 } 18186 18187 return type === 'subItem' 18188 ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' 18189 + 'border-radius:4px;width:4px;height:4px;background-color:' 18190 + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>' 18191 : '<span style="display:inline-block;margin-right:5px;' 18192 + 'border-radius:10px;width:10px;height:10px;background-color:' 18193 + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'; 18194 } 18195 18196 function pad(str, len) { 18197 str += ''; 18198 return '0000'.substr(0, len - str.length) + str; 18199 } 18200 18201 18202 /** 18203 * ISO Date format 18204 * @param {string} tpl 18205 * @param {number} value 18206 * @param {boolean} [isUTC=false] Default in local time. 18207 * see `module:echarts/scale/Time` 18208 * and `module:echarts/util/number#parseDate`. 18209 * @inner 18210 */ 18211 function formatTime(tpl, value, isUTC) { 18212 if (tpl === 'week' 18213 || tpl === 'month' 18214 || tpl === 'quarter' 18215 || tpl === 'half-year' 18216 || tpl === 'year' 18217 ) { 18218 tpl = 'MM-dd\nyyyy'; 18219 } 18220 18221 var date = parseDate(value); 18222 var utc = isUTC ? 'UTC' : ''; 18223 var y = date['get' + utc + 'FullYear'](); 18224 var M = date['get' + utc + 'Month']() + 1; 18225 var d = date['get' + utc + 'Date'](); 18226 var h = date['get' + utc + 'Hours'](); 18227 var m = date['get' + utc + 'Minutes'](); 18228 var s = date['get' + utc + 'Seconds'](); 18229 var S = date['get' + utc + 'Milliseconds'](); 18230 18231 tpl = tpl.replace('MM', pad(M, 2)) 18232 .replace('M', M) 18233 .replace('yyyy', y) 18234 .replace('yy', y % 100) 18235 .replace('dd', pad(d, 2)) 18236 .replace('d', d) 18237 .replace('hh', pad(h, 2)) 18238 .replace('h', h) 18239 .replace('mm', pad(m, 2)) 18240 .replace('m', m) 18241 .replace('ss', pad(s, 2)) 18242 .replace('s', s) 18243 .replace('SSS', pad(S, 3)); 18244 18245 return tpl; 18246 } 18247 18248 /** 18249 * Capital first 18250 * @param {string} str 18251 * @return {string} 18252 */ 18253 function capitalFirst(str) { 18254 return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; 18255 } 18256 18257 var truncateText$1 = truncateText; 18258 18259 var getTextRect = getBoundingRect; 18260 18261 18262 var format = (Object.freeze || Object)({ 18263 addCommas: addCommas, 18264 toCamelCase: toCamelCase, 18265 normalizeCssArray: normalizeCssArray$1, 18266 encodeHTML: encodeHTML, 18267 formatTpl: formatTpl, 18268 formatTplSimple: formatTplSimple, 18269 getTooltipMarker: getTooltipMarker, 18270 formatTime: formatTime, 18271 capitalFirst: capitalFirst, 18272 truncateText: truncateText$1, 18273 getTextRect: getTextRect 18274 });
18275 18276 /* 18277* Licensed to the Apache Software Foundation (ASF) under one 18278* or more contributor license agreements. See the NOTICE file 18279* distributed with this work for additional information 18280* regarding copyright ownership. The ASF licenses this file 18281* to you under the Apache License, Version 2.0 (the 18282* "License"); you may not use this file except in compliance 18283* with the License. You may obtain a copy of the License at 18284* 18285* http://www.apache.org/licenses/LICENSE-2.0 18286* 18287* Unless required by applicable law or agreed to in writing, 18288* software distributed under the License is distributed on an 18289* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18290* KIND, either express or implied. See the License for the 18291* specific language governing permissions and limitations 18292* under the License. 18293*/ 18294 18295// Layout helpers for each component positioning 18296 18297 var each$3 = each$1; 18298 18299 /** 18300 * @public 18301 */ 18302 var LOCATION_PARAMS = [ 18303 'left', 'right', 'top', 'bottom', 'width', 'height' 18304 ]; 18305 18306 /** 18307 * @public 18308 */ 18309 var HV_NAMES = [ 18310 ['width', 'left', 'right'], 18311 ['height', 'top', 'bottom'] 18312 ]; 18313 18314 function boxLayout(orient, group, gap, maxWidth, maxHeight) { 18315 var x = 0; 18316 var y = 0; 18317 18318 if (maxWidth == null) { 18319 maxWidth = Infinity; 18320 } 18321 if (maxHeight == null) { 18322 maxHeight = Infinity; 18323 } 18324 var currentLineMaxSize = 0; 18325 18326 group.eachChild(function (child, idx) { 18327 var position = child.position; 18328 var rect = child.getBoundingRect(); 18329 var nextChild = group.childAt(idx + 1); 18330 var nextChildRect = nextChild && nextChild.getBoundingRect(); 18331 var nextX; 18332 var nextY; 18333 18334 if (orient === 'horizontal') { 18335 var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0); 18336 nextX = x + moveX; 18337 // Wrap when width exceeds maxWidth or meet a `newline` group 18338 // FIXME compare before adding gap? 18339 if (nextX > maxWidth || child.newline) { 18340 x = 0; 18341 nextX = moveX; 18342 y += currentLineMaxSize + gap; 18343 currentLineMaxSize = rect.height; 18344 } 18345 else { 18346 // FIXME: consider rect.y is not `0`? 18347 currentLineMaxSize = Math.max(currentLineMaxSize, rect.height); 18348 } 18349 } 18350 else { 18351 var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0); 18352 nextY = y + moveY; 18353 // Wrap when width exceeds maxHeight or meet a `newline` group 18354 if (nextY > maxHeight || child.newline) { 18355 x += currentLineMaxSize + gap; 18356 y = 0; 18357 nextY = moveY; 18358 currentLineMaxSize = rect.width; 18359 } 18360 else { 18361 currentLineMaxSize = Math.max(currentLineMaxSize, rect.width); 18362 } 18363 } 18364 18365 if (child.newline) { 18366 return; 18367 } 18368 18369 position[0] = x; 18370 position[1] = y; 18371 18372 orient === 'horizontal' 18373 ? (x = nextX + gap) 18374 : (y = nextY + gap); 18375 }); 18376 } 18377 18378 /** 18379 * VBox or HBox layouting 18380 * @param {string} orient 18381 * @param {module:zrender/container/Group} group 18382 * @param {number} gap 18383 * @param {number} [width=Infinity] 18384 * @param {number} [height=Infinity] 18385 */ 18386 var box = boxLayout; 18387 18388 /** 18389 * VBox layouting 18390 * @param {module:zrender/container/Group} group 18391 * @param {number} gap 18392 * @param {number} [width=Infinity] 18393 * @param {number} [height=Infinity] 18394 */ 18395 var vbox = curry(boxLayout, 'vertical'); 18396 18397 /** 18398 * HBox layouting 18399 * @param {module:zrender/container/Group} group 18400 * @param {number} gap 18401 * @param {number} [width=Infinity] 18402 * @param {number} [height=Infinity] 18403 */ 18404 var hbox = curry(boxLayout, 'horizontal'); 18405 18406 /** 18407 * If x or x2 is not specified or 'center' 'left' 'right', 18408 * the width would be as long as possible. 18409 * If y or y2 is not specified or 'middle' 'top' 'bottom', 18410 * the height would be as long as possible. 18411 * 18412 * @param {Object} positionInfo 18413 * @param {number|string} [positionInfo.x] 18414 * @param {number|string} [positionInfo.y] 18415 * @param {number|string} [positionInfo.x2] 18416 * @param {number|string} [positionInfo.y2] 18417 * @param {Object} containerRect {width, height} 18418 * @param {string|number} margin 18419 * @return {Object} {width, height} 18420 */ 18421 function getAvailableSize(positionInfo, containerRect, margin) { 18422 var containerWidth = containerRect.width; 18423 var containerHeight = containerRect.height; 18424 18425 var x = parsePercent$1(positionInfo.x, containerWidth);
vendor: 4,578 bytes, lines 18426-18541
18426 var y = parsePercent$1(positionInfo.y, containerHeight); 18427 var x2 = parsePercent$1(positionInfo.x2, containerWidth); 18428 var y2 = parsePercent$1(positionInfo.y2, containerHeight); 18429 18430 (isNaN(x) || isNaN(parseFloat(positionInfo.x))) && (x = 0); 18431 (isNaN(x2) || isNaN(parseFloat(positionInfo.x2))) && (x2 = containerWidth); 18432 (isNaN(y) || isNaN(parseFloat(positionInfo.y))) && (y = 0); 18433 (isNaN(y2) || isNaN(parseFloat(positionInfo.y2))) && (y2 = containerHeight); 18434 18435 margin = normalizeCssArray$1(margin || 0); 18436 18437 return { 18438 width: Math.max(x2 - x - margin[1] - margin[3], 0), 18439 height: Math.max(y2 - y - margin[0] - margin[2], 0) 18440 }; 18441 } 18442 18443 /** 18444 * Parse position info. 18445 * 18446 * @param {Object} positionInfo 18447 * @param {number|string} [positionInfo.left] 18448 * @param {number|string} [positionInfo.top] 18449 * @param {number|string} [positionInfo.right] 18450 * @param {number|string} [positionInfo.bottom] 18451 * @param {number|string} [positionInfo.width] 18452 * @param {number|string} [positionInfo.height] 18453 * @param {number|string} [positionInfo.aspect] Aspect is width / height 18454 * @param {Object} containerRect 18455 * @param {string|number} [margin] 18456 * 18457 * @return {module:zrender/core/BoundingRect} 18458 */ 18459 function getLayoutRect( 18460 positionInfo, containerRect, margin 18461 ) { 18462 margin = normalizeCssArray$1(margin || 0); 18463 18464 var containerWidth = containerRect.width; 18465 var containerHeight = containerRect.height; 18466 18467 var left = parsePercent$1(positionInfo.left, containerWidth); 18468 var top = parsePercent$1(positionInfo.top, containerHeight); 18469 var right = parsePercent$1(positionInfo.right, containerWidth); 18470 var bottom = parsePercent$1(positionInfo.bottom, containerHeight); 18471 var width = parsePercent$1(positionInfo.width, containerWidth); 18472 var height = parsePercent$1(positionInfo.height, containerHeight); 18473 18474 var verticalMargin = margin[2] + margin[0]; 18475 var horizontalMargin = margin[1] + margin[3]; 18476 var aspect = positionInfo.aspect; 18477 18478 // If width is not specified, calculate width from left and right 18479 if (isNaN(width)) { 18480 width = containerWidth - right - horizontalMargin - left; 18481 } 18482 if (isNaN(height)) { 18483 height = containerHeight - bottom - verticalMargin - top; 18484 } 18485 18486 if (aspect != null) { 18487 // If width and height are not given 18488 // 1. Graph should not exceeds the container 18489 // 2. Aspect must be keeped 18490 // 3. Graph should take the space as more as possible 18491 // FIXME 18492 // Margin is not considered, because there is no case that both 18493 // using margin and aspect so far. 18494 if (isNaN(width) && isNaN(height)) { 18495 if (aspect > containerWidth / containerHeight) { 18496 width = containerWidth * 0.8; 18497 } 18498 else { 18499 height = containerHeight * 0.8; 18500 } 18501 } 18502 18503 // Calculate width or height with given aspect 18504 if (isNaN(width)) { 18505 width = aspect * height; 18506 } 18507 if (isNaN(height)) { 18508 height = width / aspect; 18509 } 18510 } 18511 18512 // If left is not specified, calculate left from right and width 18513 if (isNaN(left)) { 18514 left = containerWidth - right - width - horizontalMargin; 18515 } 18516 if (isNaN(top)) { 18517 top = containerHeight - bottom - height - verticalMargin; 18518 } 18519 18520 // Align left and top 18521 switch (positionInfo.left || positionInfo.right) { 18522 case 'center': 18523 left = containerWidth / 2 - width / 2 - margin[3]; 18524 break; 18525 case 'right': 18526 left = containerWidth - width - horizontalMargin; 18527 break; 18528 } 18529 switch (positionInfo.top || positionInfo.bottom) { 18530 case 'middle': 18531 case 'center': 18532 top = containerHeight / 2 - height / 2 - margin[0]; 18533 break; 18534 case 'bottom': 18535 top = containerHeight - height - verticalMargin; 18536 break; 18537 } 18538 // If something is wrong and left, top, width, height are calculated as NaN 18539 left = left || 0; 18540 top = top || 0; 18541 if (isNaN(width)) {
18542 // Width may be NaN if only one value is given except width 18543 width = containerWidth - horizontalMargin - left - (right || 0); 18544 } 18545 if (isNaN(height)) { 18546 // Height may be NaN if only one value is given except height 18547 height = containerHeight - verticalMargin - top - (bottom || 0); 18548 } 18549 18550 var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); 18551 rect.margin = margin; 18552 return rect; 18553 } 18554 18555 18556 /** 18557 * Position a zr element in viewport 18558 * Group position is specified by either 18559 * {left, top}, {right, bottom} 18560 * If all properties exists, right and bottom will be igonred. 18561 * 18562 * Logic: 18563 * 1. Scale (against origin point in parent coord) 18564 * 2. Rotate (against origin point in parent coord) 18565 * 3. Traslate (with el.position by this method) 18566 * So this method only fixes the last step 'Traslate', which does not affect 18567 * scaling and rotating. 18568 * 18569 * If be called repeatly with the same input el, the same result will be gotten. 18570 * 18571 * @param {module:zrender/Element} el Should have `getBoundingRect` method. 18572 * @param {Object} positionInfo 18573 * @param {number|string} [positionInfo.left] 18574 * @param {number|string} [positionInfo.top] 18575 * @param {number|string} [positionInfo.right] 18576 * @param {number|string} [positionInfo.bottom] 18577 * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw' 18578 * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw' 18579 * @param {Object} containerRect 18580 * @param {string|number} margin 18581 * @param {Object} [opt] 18582 * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical. 18583 * @param {Array.<number>} [opt.boundingMode='all'] 18584 * Specify how to calculate boundingRect when locating. 18585 * 'all': Position the boundingRect that is transformed and uioned 18586 * both itself and its descendants. 18587 * This mode simplies confine the elements in the bounding 18588 * of their container (e.g., using 'right: 0'). 18589 * 'raw': Position the boundingRect that is not transformed and only itself. 18590 * This mode is useful when you want a element can overflow its 18591 * container. (Consider a rotated circle needs to be located in a corner.) 18592 * In this mode positionInfo.width/height can only be number. 18593 */ 18594 function positionElement(el, positionInfo, containerRect, margin, opt) { 18595 var h = !opt || !opt.hv || opt.hv[0]; 18596 var v = !opt || !opt.hv || opt.hv[1]; 18597 var boundingMode = opt && opt.boundingMode || 'all'; 18598 18599 if (!h && !v) { 18600 return; 18601 } 18602 18603 var rect; 18604 if (boundingMode === 'raw') { 18605 rect = el.type === 'group' 18606 ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) 18607 : el.getBoundingRect(); 18608 } 18609 else { 18610 rect = el.getBoundingRect(); 18611 if (el.needLocalTransform()) { 18612 var transform = el.getLocalTransform(); 18613 // Notice: raw rect may be inner object of el, 18614 // which should not be modified. 18615 rect = rect.clone(); 18616 rect.applyTransform(transform); 18617 } 18618 } 18619 18620 // The real width and height can not be specified but calculated by the given el. 18621 positionInfo = getLayoutRect( 18622 defaults( 18623 {width: rect.width, height: rect.height}, 18624 positionInfo 18625 ), 18626 containerRect, 18627 margin 18628 ); 18629 18630 // Because 'tranlate' is the last step in transform 18631 // (see zrender/core/Transformable#getLocalTransform), 18632 // we can just only modify el.position to get final result. 18633 var elPos = el.position; 18634 var dx = h ? positionInfo.x - rect.x : 0; 18635 var dy = v ? positionInfo.y - rect.y : 0; 18636 18637 el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy
vendor: 5,464 bytes, lines 18637-18783
18637]); 18638 } 18639 18640 /** 18641 * @param {Object} option Contains some of the properties in HV_NAMES. 18642 * @param {number} hvIdx 0: horizontal; 1: vertical. 18643 */ 18644 function sizeCalculable(option, hvIdx) { 18645 return option[HV_NAMES[hvIdx][0]] != null 18646 || (option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null); 18647 } 18648 18649 /** 18650 * Consider Case: 18651 * When defulat option has {left: 0, width: 100}, and we set {right: 0} 18652 * through setOption or media query, using normal zrUtil.merge will cause 18653 * {right: 0} does not take effect. 18654 * 18655 * @example 18656 * ComponentModel.extend({ 18657 * init: function () { 18658 * ... 18659 * var inputPositionParams = layout.getLayoutParams(option); 18660 * this.mergeOption(inputPositionParams); 18661 * }, 18662 * mergeOption: function (newOption) { 18663 * newOption && zrUtil.merge(thisOption, newOption, true); 18664 * layout.mergeLayoutParam(thisOption, newOption); 18665 * } 18666 * }); 18667 * 18668 * @param {Object} targetOption 18669 * @param {Object} newOption 18670 * @param {Object|string} [opt] 18671 * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components 18672 * that width (or height) should not be calculated by left and right (or top and bottom). 18673 */ 18674 function mergeLayoutParam(targetOption, newOption, opt) { 18675 !isObject$1(opt) && (opt = {}); 18676 18677 var ignoreSize = opt.ignoreSize; 18678 !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]); 18679 18680 var hResult = merge$$1(HV_NAMES[0], 0); 18681 var vResult = merge$$1(HV_NAMES[1], 1); 18682 18683 copy(HV_NAMES[0], targetOption, hResult); 18684 copy(HV_NAMES[1], targetOption, vResult); 18685 18686 function merge$$1(names, hvIdx) { 18687 var newParams = {}; 18688 var newValueCount = 0; 18689 var merged = {}; 18690 var mergedValueCount = 0; 18691 var enoughParamNumber = 2; 18692 18693 each$3(names, function (name) { 18694 merged[name] = targetOption[name]; 18695 }); 18696 each$3(names, function (name) { 18697 // Consider case: newOption.width is null, which is 18698 // set by user for removing width setting. 18699 hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]); 18700 hasValue(newParams, name) && newValueCount++; 18701 hasValue(merged, name) && mergedValueCount++; 18702 }); 18703 18704 if (ignoreSize[hvIdx]) { 18705 // Only one of left/right is premitted to exist. 18706 if (hasValue(newOption, names[1])) { 18707 merged[names[2]] = null; 18708 } 18709 else if (hasValue(newOption, names[2])) { 18710 merged[names[1]] = null; 18711 } 18712 return merged; 18713 } 18714 18715 // Case: newOption: {width: ..., right: ...}, 18716 // or targetOption: {right: ...} and newOption: {width: ...}, 18717 // There is no conflict when merged only has params count 18718 // little than enoughParamNumber. 18719 if (mergedValueCount === enoughParamNumber || !newValueCount) { 18720 return merged; 18721 } 18722 // Case: newOption: {width: ..., right: ...}, 18723 // Than we can make sure user only want those two, and ignore 18724 // all origin params in targetOption. 18725 else if (newValueCount >= enoughParamNumber) { 18726 return newParams; 18727 } 18728 else { 18729 // Chose another param from targetOption by priority. 18730 for (var i = 0; i < names.length; i++) { 18731 var name = names[i]; 18732 if (!hasProp(newParams, name) && hasProp(targetOption, name)) { 18733 newParams[name] = targetOption[name]; 18734 break; 18735 } 18736 } 18737 return newParams; 18738 } 18739 } 18740 18741 function hasProp(obj, name) { 18742 return obj.hasOwnProperty(name); 18743 } 18744 18745 function hasValue(obj, name) { 18746 return obj[name] != null && obj[name] !== 'auto'; 18747 } 18748 18749 function copy(names, target, source) { 18750 each$3(names, function (name) { 18751 target[name] = source[name]; 18752 }); 18753 } 18754 } 18755 18756 /** 18757 * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. 18758 * @param {Object} source 18759 * @return {Object} Result contains those props. 18760 */ 18761 function getLayoutParams(source) { 18762 return copyLayoutParams({}, source); 18763 } 18764 18765 /** 18766 * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. 18767 * @param {Object} source 18768 * @return {Object} Result contains those props. 18769 */ 18770 function copyLayoutParams(target, source) { 18771 source && target && each$3(LOCATION_PARAMS, function (name) { 18772 source.hasOwnProperty(name) && (target[name] = source[name]); 18773 }); 18774 return target; 18775 } 18776 18777 /* 18778* Licensed to the Apache Software Foundation (ASF) under one 18779* or more contributor license agreements. See the NOTICE file 18780* distributed with this work for additional information 18781* regarding copyright ownership. The ASF licenses this file 18782* to you under the Apache License, Version 2.0 (the 18783* "License");
18783 you may not use this file except in compliance 18784* with the License. You may obtain a copy of the License at 18785* 18786* http://www.apache.org/licenses/LICENSE-2.0 18787* 18788* Unless required by applicable law or agreed to in writing, 18789* software distributed under the License is distributed on an 18790* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18791* KIND, either express or implied. See the License for the 18792* specific language governing permissions and limitations 18793* under the License. 18794*/ 18795 18796 18797 var boxLayoutMixin = { 18798 getBoxLayoutParams: function () { 18799 return { 18800 left: this.get('left'), 18801 top: this.get('top'), 18802 right: this.get('right'), 18803 bottom: this.get('bottom'), 18804 width: this.get('width'), 18805 height: this.get('height') 18806 }; 18807 } 18808 }; 18809 18810 /* 18811* Licensed to the Apache Software Foundation (ASF) under one 18812* or more contributor license agreements. See the NOTICE file 18813* distributed with this work for additional information 18814* regarding copyright ownership. The ASF licenses this file 18815* to you under the Apache License, Version 2.0 (the 18816* "License"); you may not use this file except in compliance 18817* with the License. You may obtain a copy of the License at 18818* 18819* http://www.apache.org/licenses/LICENSE-2.0 18820* 18821* Unless required by applicable law or agreed to in writing, 18822* software distributed under the License is distributed on an 18823* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18824* KIND, either express or implied. See the License for the 18825* specific language governing permissions and limitations 18826* under the License. 18827*/ 18828 18829 /** 18830 * Component model 18831 * 18832 * @module echarts/model/Component 18833 */ 18834 18835 var inner$1 = makeInner(); 18836 18837 /** 18838 * @alias module:echarts/model/Component 18839 * @constructor 18840 * @param {Object} option 18841 * @param {module:echarts/model/Model} parentModel 18842 * @param {module:echarts/model/Model} ecModel 18843 */ 18844 var ComponentModel = Model.extend({ 18845 18846 type: 'component', 18847 18848 /** 18849 * @readOnly 18850 * @type {string} 18851 */ 18852 id: '', 18853 18854 /** 18855 * Because simplified concept is probably better, series.name (or component.name) 18856 * has been having too many resposibilities: 18857 * (1) Generating id (which requires name in option should not be modified). 18858 * (2) As an index to mapping series when merging option or calling API (a name 18859 * can refer to more then one components, which is convinient is some case). 18860 * (3) Display. 18861 * @readOnly 18862 */ 18863 name: '', 18864 18865 /** 18866 * @readOnly 18867 * @type {string} 18868 */ 18869 mainType: '', 18870 18871 /** 18872 * @readOnly 18873 * @type {string} 18874 */ 18875 subType: '', 18876 18877 /** 18878 * @readOnly 18879 * @type {number} 18880 */ 18881 componentIndex: 0, 18882 18883 /** 18884 * @type {Object} 18885 * @protected 18886 */ 18887 defaultOption: null, 18888 18889 /** 18890 * @type {module:echarts/model/Global} 18891 * @readOnly 18892 */ 18893 ecModel: null, 18894 18895 /** 18896 * key: componentType 18897 * value: Component model list, can not be null. 18898 * @type {Object.<string, Array.<module:echarts/model/Model>>} 18899 * @readOnly 18900 */ 18901 dependentModels: [], 18902 18903 /** 18904 * @type {string} 18905 * @readOnly 18906 */ 18907 uid: null, 18908 18909 /** 18910 * Support merge layout params. 18911 * Only support 'box' now (left/right/top/bottom/width/height). 18912 * @type {string|Object} Object can be {ignoreSize: true} 18913 * @readOnly 18914 */ 18915 layoutMode: null, 18916 18917 $constructor: function (option, parentModel, ecModel, extraOpt) { 18918 Model.call(this, option, parentModel, ecModel, extraOpt); 18919 18920 this.uid = getUID('ec_cpt_model'); 18921 }, 18922 18923 init: function (option, parentModel, ecModel, extraOpt) { 18924 this.mergeDefaultAndTheme(option, ecModel); 18925 }, 18926 18927 mergeDefaultAndTheme: function (option, ecModel) { 18928 var layoutMode = this.layoutMode; 18929 var inputPositionParams = layoutMode 18930 ? getLayoutParams(option) : {}; 18931 18932 var themeModel = ecModel.getTheme(); 18933 merge(option, themeModel.get(this.mainType)); 18934 merge(option, this.getDefaultOption()); 18935 18936 if (layoutMode) { 18937 mergeLayoutParam(option, inputPositionParams, layoutMode); 18938 } 18939 }, 18940 18941 mergeOption: function (option, extraOpt) { 18942 merge(this.option, option, true); 18943 18944 var layoutMode = this.layoutMode; 18945 if (layoutMode) { 18946 mergeLayoutParam(this.option, option, layoutMode); 18947 } 18948 }, 18949 18950 // Hooker after init or mergeOption 18951 optionUpdated: function (newCptOption, isInit) {}, 18952 18953 getDefaultOption: function () { 18954 var fields = inner$1(this); 18955 if (!fields.defaultOption) { 18956 var optList = []; 18957 var Class = this.constructor; 18958 while (Class) { 18959 var opt = Class.prototype.defaultOption; 18960 opt && optList.push(opt); 18961 Class = Class.superClass; 18962 } 18963 18964 var defaultOption = {}; 18965 for (var i = optList.length - 1; i >= 0; i--) { 18966 defaultOption = merge(defaultOption, optList[i], true); 18967 } 18968 fields.defaultOption = defaultOption; 18969 } 18970 return fields.defaultOption; 18971 }, 18972 18973 getReferringComponents: function (mainType) { 18974 return this.ecModel.queryComponents({ 18975 mainType: mainType, 18976 index: this.get(mainType + 'Index', true), 18977 id: this.get(mainType + 'Id', true) 18978 }); 18979 } 18980 18981 });
18982 18983// Reset ComponentModel.extend, add preConstruct. 18984// clazzUtil.enableClassExtend( 18985// ComponentModel, 18986// function (option, parentModel, ecModel, extraOpt) { 18987// // Set dependentModels, componentIndex, name, id, mainType, subType. 18988// zrUtil.extend(this, extraOpt); 18989 18990// this.uid = componentUtil.getUID('componentModel'); 18991 18992// // this.setReadOnly([ 18993// // 'type', 'id', 'uid', 'name', 'mainType', 'subType', 18994// // 'dependentModels', 'componentIndex' 18995// // ]); 18996// } 18997// ); 18998 18999// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. 19000 enableClassManagement( 19001 ComponentModel, {registerWhenExtend: true} 19002 ); 19003 enableSubTypeDefaulter(ComponentModel); 19004 19005// Add capability of ComponentModel.topologicalTravel. 19006 enableTopologicalTravel(ComponentModel, getDependencies); 19007 19008 function getDependencies(componentType) { 19009 var deps = []; 19010 each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
19011 deps = deps.concat(Clazz.prototype.dependencies || []); 19012 }); 19013 19014 // Ensure main type. 19015 deps = map(deps, function (type) { 19016 return parseClassType$1(type).main; 19017 }); 19018 19019 // Hack dataset for convenience. 19020 if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) { 19021 deps.unshift('dataset'); 19022 } 19023 19024 return deps; 19025 } 19026 19027 mixin(ComponentModel, boxLayoutMixin); 19028 19029 /* 19030* Licensed to the Apache Software Foundation (ASF) under one 19031* or more contributor license agreements. See the NOTICE file 19032* distributed with this work for additional information 19033* regarding copyright ownership. The ASF licenses this file 19034* to you under the Apache License, Version 2.0 (the 19035* "License"); you may not use this file except in compliance 19036* with the License. You may obtain a copy of the License at 19037* 19038* http://www.apache.org/licenses/LICENSE-2.0 19039* 19040* Unless required by applicable law or agreed to in writing, 19041* software distributed under the License is distributed on an 19042* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19043* KIND, either express or implied. See the License for the 19044* specific language governing permissions and limitations 19045* under the License. 19046*/ 19047 19048 19049 var platform = ''; 19050// Navigator not exists in node 19051 if (typeof navigator !== 'undefined') { 19052 platform = navigator.platform || ''; 19053 } 19054 19055 var globalDefault = { 19056 // backgroundColor: 'rgba(0,0,0,0)', 19057 19058 // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization 19059 // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'], 19060 // Light colors: 19061 // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'], 19062 // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'], 19063 // Dark colors: 19064 color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'], 19065 19066 gradientColor: ['#f6efa6', '#d88273', '#bf444c'], 19067 19068 // If xAxis and yAxis declared, grid is created by default. 19069 // grid: {}, 19070 19071 textStyle: { 19072 // color: '#000', 19073 // decoration: 'none', 19074 // PENDING 19075 fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif', 19076 // fontFamily: 'Arial, Verdana, sans-serif', 19077 fontSize: 12, 19078 fontStyle: 'normal', 19079 fontWeight: 'normal' 19080 }, 19081 19082 // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/ 19083 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation 19084 // Default is source-over 19085 blendMode: null, 19086 19087 animation: 'auto', 19088 animationDuration: 1000, 19089 animationDurationUpdate: 300, 19090 animationEasing: 'exponentialOut', 19091 animationEasingUpdate: 'cubicOut', 19092 19093 animationThreshold: 2000, 19094 // Configuration for progressive/incremental rendering 19095 progressiveThreshold: 3000, 19096 progressive: 400, 19097 19098 // Threshold of if use single hover layer to optimize. 19099 // It is recommended that `hoverLayerThreshold` is equivalent to or less than 19100 // `progressiveThreshold`, otherwise hover will cause restart of progressive, 19101 // which is unexpected. 19102 // see example <echarts/test/heatmap-large.html>. 19103 hoverLayerThreshold: 3000, 19104 19105 // See: module:echarts/scale/Time 19106 useUTC: false 19107 }; 19108 19109 /* 19110* Licensed to the Apache Software Foundation (ASF) under one 19111* or more contributor license agreements. See the NOTICE file 19112* distributed with this work for additional information 19113* regarding copyright ownership. The ASF licenses this file 19114* to you under the Apache License, Version 2.0 (the 19115* "License"); you may not use this file except in compliance 19116* with the License. You may obtain a copy of the License at 19117* 19118* http://www.apache.org/licenses/LICENSE-2.0 19119* 19120* Unless required by applicable law or agreed to in writing, 19121* software distributed under the License is distributed on an 19122* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19123* KIND, either express or implied. See the License for the 19124* specific language governing permissions and limitations 19125* under the License. 19126*/ 19127 19128 var inner$2 = makeInner(); 19129
19130 function getNearestColorPalette(colors, requestColorNum) { 19131 var paletteNum = colors.length; 19132 // TODO colors must be in order 19133 for (var i = 0; i < paletteNum; i++) { 19134 if (colors[i].length > requestColorNum) { 19135 return colors[i]; 19136 } 19137 } 19138 return colors[paletteNum - 1]; 19139 } 19140 19141 var colorPaletteMixin = { 19142 clearColorPalette: function () { 19143 inner$2(this).colorIdx = 0; 19144 inner$2(this).colorNameMap = {}; 19145 }, 19146 19147 /** 19148 * @param {string} name MUST NOT be null/undefined. Otherwise call this function 19149 * twise with the same parameters will get different result. 19150 * @param {Object} [scope=this] 19151 * @param {Object} [requestColorNum] 19152 * @return {string} color string. 19153 */ 19154 getColorFromPalette: function (name, scope, requestColorNum) { 19155 scope = scope || this; 19156 var scopeFields = inner$2(scope); 19157 var colorIdx = scopeFields.colorIdx || 0; 19158 var colorNameMap = scopeFields.colorNameMap = scopeFields.colorNameMap || {}; 19159 // Use `hasOwnProperty` to avoid conflict with Object.prototype. 19160 if (colorNameMap.hasOwnProperty(name)) { 19161 return colorNameMap[name]; 19162 } 19163 var defaultColorPalette = normalizeToArray(this.get('color', true)); 19164 var layeredColorPalette = this.get('colorLayer', true); 19165 var colorPalette = ((requestColorNum == null || !layeredColorPalette) 19166 ? defaultColorPalette : getNearestColorPalette(layeredColorPalette, requestColorNum)); 19167 19168 // In case can't find in layered color palette. 19169 colorPalette = colorPalette || defaultColorPalette; 19170 19171 if (!colorPalette || !colorPalette.length) { 19172 return; 19173 } 19174 19175 var color = colorPalette[colorIdx]; 19176 if (name) { 19177 colorNameMap[name] = color; 19178 } 19179 scopeFields.colorIdx = (colorIdx + 1) % colorPalette.length; 19180 19181 return color; 19182 } 19183 }; 19184 19185 /* 19186* Licensed to the Apache Software Foundation (ASF) under one 19187* or more contributor license agreements. See the NOTICE file 19188* distributed with this work for additional information 19189* regarding copyright ownership. The ASF licenses this file 19190* to you under the Apache License, Version 2.0 (the 19191* "License"); you may not use this file except in compliance 19192* with the License. You may obtain a copy of the License at 19193* 19194* http://www.apache.org/licenses/LICENSE-2.0 19195* 19196* Unless required by applicable law or agreed to in writing, 19197* software distributed under the License is distributed on an 19198* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19199* KIND, either express or implied. See the License for the 19200* specific language governing permissions and limitations 19201* under the License. 19202*/ 19203 19204 /** 19205 * Helper for model references. 19206 * There are many manners to refer axis/coordSys. 19207 */ 19208 19209// TODO 19210// merge relevant logic to this file? 19211// check: "modelHelper" of tooltip and "BrushTargetManager". 19212 19213 /** 19214 * @return {Object} For example: 19215 * { 19216 * coordSysName: 'cartesian2d', 19217 * coordSysDims: ['x', 'y', ...], 19218 * axisMap: HashMap({ 19219 * x: xAxisModel, 19220 * y: yAxisModel 19221 * }), 19222 * categoryAxisMap: HashMap({ 19223 * x: xAxisModel, 19224 * y: undefined 19225 * }), 19226 * // It also indicate that whether there is category axis. 19227 * firstCategoryDimIndex: 1, 19228 * // To replace user specified encode. 19229 * } 19230 */ 19231 function getCoordSysDefineBySeries(seriesModel) { 19232 var coordSysName = seriesModel.get('coordinateSystem'); 19233 var result = { 19234 coordSysName: coordSysName, 19235 coordSysDims: [], 19236 axisMap: createHashMap(), 19237 categoryAxisMap: createHashMap() 19238 }; 19239 var fetch = fetchers[coordSysName]; 19240 if (fetch) {
19241 fetch(seriesModel, result, result.axisMap, result.categoryAxisMap); 19242 return result; 19243 } 19244 } 19245 19246 var fetchers = { 19247 19248 cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) { 19249 var xAxisModel = seriesModel.getReferringComponents('xAxis')[0]; 19250 var yAxisModel = seriesModel.getReferringComponents('yAxis')[0]; 19251 19252 if (__DEV__) { 19253 if (!xAxisModel) { 19254 throw new Error('xAxis "' + retrieve( 19255 seriesModel.get('xAxisIndex'), 19256 seriesModel.get('xAxisId'), 19257 0 19258 ) + '" not found'); 19259 } 19260 if (!yAxisModel) { 19261 throw new Error('yAxis "' + retrieve( 19262 seriesModel.get('xAxisIndex'), 19263 seriesModel.get('yAxisId'), 19264 0 19265 ) + '" not found'); 19266 } 19267 } 19268 19269 result.coordSysDims = ['x', 'y']; 19270 axisMap.set('x', xAxisModel); 19271 axisMap.set('y', yAxisModel); 19272 19273 if (isCategory(xAxisModel)) { 19274 categoryAxisMap.set('x', xAxisModel); 19275 result.firstCategoryDimIndex = 0; 19276 } 19277 if (isCategory(yAxisModel)) { 19278 categoryAxisMap.set('y', yAxisModel); 19279 result.firstCategoryDimIndex = 1; 19280 } 19281 }, 19282 19283 singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) { 19284 var singleAxisModel = seriesModel.getReferringComponents('singleAxis')[0]; 19285 19286 if (__DEV__) { 19287 if (!singleAxisModel) { 19288 throw new Error('singleAxis should be specified.'); 19289 } 19290 } 19291 19292 result.coordSysDims = ['single']; 19293 axisMap.set('single', singleAxisModel); 19294 19295 if (isCategory(singleAxisModel)) { 19296 categoryAxisMap.set('single', singleAxisModel); 19297 result.firstCategoryDimIndex = 0; 19298 } 19299 }, 19300 19301 polar: function (seriesModel, result, axisMap, categoryAxisMap) { 19302 var polarModel = seriesModel.getReferringComponents('polar')[0]; 19303 var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); 19304 var angleAxisModel = polarModel.findAxisModel('angleAxis'); 19305 19306 if (__DEV__) { 19307 if (!angleAxisModel) { 19308 throw new Error('angleAxis option not found'); 19309 } 19310 if (!radiusAxisModel) { 19311 throw new Error('radiusAxis option not found'); 19312 } 19313 } 19314 19315 result.coordSysDims = ['radius', 'angle']; 19316 axisMap.set('radius', radiusAxisModel); 19317 axisMap.set('angle', angleAxisModel); 19318 19319 if (isCategory(radiusAxisModel)) { 19320 categoryAxisMap.set('radius', radiusAxisModel); 19321 result.firstCategoryDimIndex = 0; 19322 } 19323 if (isCategory(angleAxisModel)) { 19324 categoryAxisMap.set('angle', angleAxisModel); 19325 result.firstCategoryDimIndex = 1; 19326 } 19327 }, 19328 19329 geo: function (seriesModel, result, axisMap, categoryAxisMap) { 19330 result.coordSysDims = ['lng', 'lat']; 19331 }, 19332 19333 parallel: function (seriesModel, result, axisMap, categoryAxisMap) { 19334 var ecModel = seriesModel.ecModel; 19335 var parallelModel = ecModel.getComponent( 19336 'parallel', seriesModel.get('parallelIndex') 19337 ); 19338 var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice(); 19339 19340 each$1(parallelModel.parallelAxisIndex, function (axisIndex, index) { 19341 var axisModel = ecModel.getComponent('parallelAxis', axisIndex); 19342 var axisDim = coordSysDims[index]; 19343 axisMap.set(axisDim, axisModel); 19344 19345 if (isCategory(axisModel) && result.firstCategoryDimIndex == null) { 19346 categoryAxisMap.set(axisDim, axisModel); 19347 result.firstCategoryDimIndex = index; 19348 } 19349 }); 19350 } 19351 }; 19352 19353 function isCategory(axisModel) { 19354 return axisModel.get('type') === 'category'; 19355 } 19356 19357 /* 19358* Licensed to the Apache Software Foundation (ASF) under one 19359* or more contributor license agreements. See the NOTICE file 19360* distributed with this work for additional information 19361* regarding copyright ownership. The ASF licenses this file 19362* to you under the Apache License, Version 2.0 (the 19363* "License");
19363 you may not use this file except in compliance 19364* with the License. You may obtain a copy of the License at 19365* 19366* http://www.apache.org/licenses/LICENSE-2.0 19367* 19368* Unless required by applicable law or agreed to in writing, 19369* software distributed under the License is distributed on an 19370* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19371* KIND, either express or implied. See the License for the 19372* specific language governing permissions and limitations 19373* under the License. 19374*/ 19375 19376// Avoid typo. 19377 var SOURCE_FORMAT_ORIGINAL = 'original'; 19378 var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows'; 19379 var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows'; 19380 var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns'; 19381 var SOURCE_FORMAT_UNKNOWN = 'unknown'; 19382// ??? CHANGE A NAME 19383 var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray'; 19384 19385 var SERIES_LAYOUT_BY_COLUMN = 'column'; 19386 var SERIES_LAYOUT_BY_ROW = 'row'; 19387 19388 /* 19389* Licensed to the Apache Software Foundation (ASF) under one 19390* or more contributor license agreements. See the NOTICE file 19391* distributed with this work for additional information 19392* regarding copyright ownership. The ASF licenses this file 19393* to you under the Apache License, Version 2.0 (the 19394* "License"); you may not use this file except in compliance 19395* with the License. You may obtain a copy of the License at 19396* 19397* http://www.apache.org/licenses/LICENSE-2.0 19398* 19399* Unless required by applicable law or agreed to in writing, 19400* software distributed under the License is distributed on an 19401* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19402* KIND, either express or implied. See the License for the 19403* specific language governing permissions and limitations 19404* under the License. 19405*/ 19406 19407 /** 19408 * [sourceFormat] 19409 * 19410 * + "original": 19411 * This format is only used in series.data, where 19412 * itemStyle can be specified in data item. 19413 * 19414 * + "arrayRows": 19415 * [ 19416 * ['product', 'score', 'amount'], 19417 * ['Matcha Latte', 89.3, 95.8], 19418 * ['Milk Tea', 92.1, 89.4], 19419 * ['Cheese Cocoa', 94.4, 91.2], 19420 * ['Walnut Brownie', 85.4, 76.9] 19421 * ] 19422 * 19423 * + "objectRows": 19424 * [ 19425 * {product: 'Matcha Latte', score: 89.3, amount: 95.8}, 19426 * {product: 'Milk Tea', score: 92.1, amount: 89.4}, 19427 * {product: 'Cheese Cocoa', score: 94.4, amount: 91.2}, 19428 * {product: 'Walnut Brownie', score: 85.4, amount: 76.9} 19429 * ] 19430 * 19431 * + "keyedColumns": 19432 * { 19433 * 'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'], 19434 * 'count': [823, 235, 1042, 988], 19435 * 'score': [95.8, 81.4, 91.2, 76.9] 19436 * } 19437 * 19438 * + "typedArray" 19439 * 19440 * + "unknown" 19441 */ 19442 19443 /** 19444 * @constructor 19445 * @param {Object} fields 19446 * @param {string} fields.sourceFormat 19447 * @param {Array|Object} fields.fromDataset 19448 * @param {Array|Object} [fields.data] 19449 * @param {string} [seriesLayoutBy='column'] 19450 * @param {Array.<Object|string>} [dimensionsDefine] 19451 * @param {Objet|HashMap} [encodeDefine] 19452 * @param {number} [startIndex=0] 19453 * @param {number} [dimensionsDetectCount] 19454 */ 19455 function Source(fields) { 19456 19457 /** 19458 * @type {boolean} 19459 */ 19460 this.fromDataset = fields.fromDataset; 19461 19462 /** 19463 * Not null/undefined. 19464 * @type {Array|Object} 19465 */ 19466 this.data = fields.data || ( 19467 fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : [] 19468 ); 19469 19470 /** 19471 * See also "detectSourceFormat". 19472 * Not null/undefined. 19473 * @type {string} 19474 */ 19475 this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; 19476 19477 /** 19478 * 'row' or 'column' 19479 * Not null/undefined. 19480 * @type {string} seriesLayoutBy 19481 */ 19482 this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN; 19483 19484 /** 19485 * dimensions definition in option. 19486 * can be null/undefined. 19487 * @type {Array.<Object|string>} 19488 */ 19489 this.dimensionsDefine = fields.dimensionsDefine; 19490 19491 /** 19492 * encode definition in option. 19493 * can be null/undefined. 19494 * @type {Objet|HashMap} 19495 */ 19496 this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine); 19497 19498 /** 19499 * Not null/undefined, uint. 19500 * @type {number} 19501 */ 19502 this.startIndex = fields.startIndex || 0; 19503 19504 /** 19505 * Can be null/undefined (when unknown), uint. 19506 * @type {number} 19507 */ 19508 this.dimensionsDetectCount = fields.dimensionsDetectCount; 19509 } 19510 19511 /** 19512 * Wrap original series data for some compatibility cases. 19513 */ 19514 Source.seriesDataToSource = function (data) { 19515 return new Source({ 19516 data: data, 19517 sourceFormat: isTypedArray(data) 19518 ? SOURCE_FORMAT_TYPED_ARRAY 19519 : SOURCE_FORMAT_ORIGINAL, 19520 fromDataset: false 19521 }); 19522 }; 19523 19524 enableClassCheck(Source); 19525 19526 /* 19527* Licensed to the Apache Software Foundation (ASF) under one 19528* or more contributor license agreements. See the NOTICE file 19529* distributed with this work for additional information 19530* regarding copyright ownership. The ASF licenses this file 19531* to you under the Apache License, Version 2.0 (the 19532* "License");
19532 you may not use this file except in compliance 19533* with the License. You may obtain a copy of the License at 19534* 19535* http://www.apache.org/licenses/LICENSE-2.0 19536* 19537* Unless required by applicable law or agreed to in writing, 19538* software distributed under the License is distributed on an 19539* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19540* KIND, either express or implied. See the License for the 19541* specific language governing permissions and limitations 19542* under the License. 19543*/ 19544 19545 var inner$3 = makeInner(); 19546 19547 /** 19548 * @see {module:echarts/data/Source} 19549 * @param {module:echarts/component/dataset/DatasetModel} datasetModel 19550 * @return {string} sourceFormat 19551 */ 19552 function detectSourceFormat(datasetModel) { 19553 var data = datasetModel.option.source; 19554 var sourceFormat = SOURCE_FORMAT_UNKNOWN; 19555 19556 if (isTypedArray(data)) { 19557 sourceFormat = SOURCE_FORMAT_TYPED_ARRAY; 19558 } 19559 else if (isArray(data)) { 19560 // FIXME Whether tolerate null in top level array? 19561 for (var i = 0, len = data.length; i < len; i++) { 19562 var item = data[i]; 19563 19564 if (item == null) { 19565 continue; 19566 } 19567 else if (isArray(item)) { 19568 sourceFormat = SOURCE_FORMAT_ARRAY_ROWS; 19569 break; 19570 } 19571 else if (isObject$1(item)) { 19572 sourceFormat = SOURCE_FORMAT_OBJECT_ROWS; 19573 break; 19574 } 19575 } 19576 } 19577 else if (isObject$1(data)) { 19578 for (var key in data) { 19579 if (data.hasOwnProperty(key) && isArrayLike(data[key])) { 19580 sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS; 19581 break; 19582 } 19583 } 19584 } 19585 else if (data != null) { 19586 throw new Error('Invalid data'); 19587 } 19588 19589 inner$3(datasetModel).sourceFormat = sourceFormat; 19590 } 19591 19592 /** 19593 * [Scenarios]: 19594 * (1) Provide source data directly: 19595 * series: { 19596 * encode: {...}, 19597 * dimensions: [...] 19598 * seriesLayoutBy: 'row', 19599 * data: [[...]] 19600 * } 19601 * (2) Refer to datasetModel. 19602 * series: [{ 19603 * encode: {...} 19604 * // Ignore datasetIndex means `datasetIndex: 0` 19605 * // and the dimensions defination in dataset is used 19606 * }, { 19607 * encode: {...}, 19608 * seriesLayoutBy: 'column', 19609 * datasetIndex: 1 19610 * }] 19611 * 19612 * Get data from series itself or datset. 19613 * @return {module:echarts/data/Source} source 19614 */ 19615 function getSource(seriesModel) { 19616 return inner$3(seriesModel).source; 19617 } 19618 19619 /** 19620 * MUST be called before mergeOption of all series. 19621 * @param {module:echarts/model/Global} ecModel 19622 */ 19623 function resetSourceDefaulter(ecModel) { 19624 // `datasetMap` is used to make default encode. 19625 inner$3(ecModel).datasetMap = createHashMap(); 19626 } 19627 19628 /** 19629 * [Caution]: 19630 * MUST be called after series option merged and 19631 * before "series.getInitailData()" called. 19632 * 19633 * [The rule of making default encode]: 19634 * Category axis (if exists) alway map to the first dimension. 19635 * Each other axis occupies a subsequent dimension. 19636 * 19637 * [Why make default encode]: 19638 * Simplify the typing of encode in option, avoiding the case like that: 19639 * series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}], 19640 * where the "y" have to be manually typed as "1, 2, 3, ...". 19641 * 19642 * @param {module:echarts/model/Series} seriesModel 19643 */ 19644 function prepareSource(seriesModel) { 19645 var seriesOption = seriesModel.option; 19646 19647 var data = seriesOption.data; 19648 var sourceFormat = isTypedArray(data) 19649 ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL; 19650 var fromDataset = false; 19651 19652 var seriesLayoutBy = seriesOption.seriesLayoutBy; 19653 var sourceHeader = seriesOption.sourceHeader; 19654 var dimensionsDefine = seriesOption.dimensions; 19655 19656 var datasetModel = getDatasetModel(seriesModel); 19657 if (datasetModel) { 19658 var datasetOption = datasetModel.option; 19659 19660 data = datasetOption.source; 19661 sourceFormat = inner$3(datasetModel).sourceFormat; 19662 fromDataset = true; 19663 19664 // These settings from series has higher priority. 19665 seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy; 19666 sourceHeader == null && (sourceHeader = datasetOption.sourceHeader); 19667 dimensionsDefine = dimensionsDefine || datasetOption.dimensions; 19668 } 19669 19670 var completeResult = completeBySourceData( 19671 data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine 19672 ); 19673 19674 // Note: dataset option does not have `encode`. 19675 var encodeDefine = seriesOption.encode; 19676 if (!encodeDefine && datasetModel) { 19677 encodeDefine = makeDefaultEncode( 19678 seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult 19679 ); 19680 } 19681 19682 inner$3(seriesModel).source = new Source({ 19683 data: data, 19684 fromDataset: fromDataset,
19685 seriesLayoutBy: seriesLayoutBy, 19686 sourceFormat: sourceFormat, 19687 dimensionsDefine: completeResult.dimensionsDefine, 19688 startIndex: completeResult.startIndex, 19689 dimensionsDetectCount: completeResult.dimensionsDetectCount, 19690 encodeDefine: encodeDefine 19691 }); 19692 } 19693 19694// return {startIndex, dimensionsDefine, dimensionsCount} 19695 function completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) { 19696 if (!data) { 19697 return {dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine)}; 19698 } 19699 19700 var dimensionsDetectCount; 19701 var startIndex; 19702 var findPotentialName; 19703 19704 if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { 19705 // Rule: Most of the first line are string: it is header. 19706 // Caution: consider a line with 5 string and 1 number, 19707 // it still can not be sure it is a head, because the 19708 // 5 string may be 5 values of category columns. 19709 if (sourceHeader === 'auto' || sourceHeader == null) { 19710 arrayRowsTravelFirst(function (val) { 19711 // '-' is regarded as null/undefined. 19712 if (val != null && val !== '-') { 19713 if (isString(val)) { 19714 startIndex == null && (startIndex = 1); 19715 } 19716 else { 19717 startIndex = 0; 19718 } 19719 } 19720 // 10 is an experience number, avoid long loop. 19721 }, seriesLayoutBy, data, 10); 19722 } 19723 else { 19724 startIndex = sourceHeader ? 1 : 0; 19725 } 19726 19727 if (!dimensionsDefine && startIndex === 1) { 19728 dimensionsDefine = []; 19729 arrayRowsTravelFirst(function (val, index) { 19730 dimensionsDefine[index] = val != null ? val : ''; 19731 }, seriesLayoutBy, data); 19732 } 19733 19734 dimensionsDetectCount = dimensionsDefine 19735 ? dimensionsDefine.length 19736 : seriesLayoutBy === SERIES_LAYOUT_BY_ROW 19737 ? data.length 19738 : data[0] 19739 ? data[0].length 19740 : null; 19741 } 19742 else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { 19743 if (!dimensionsDefine) { 19744 dimensionsDefine = objectRowsCollectDimensions(data); 19745 findPotentialName = true; 19746 } 19747 } 19748 else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { 19749 if (!dimensionsDefine) { 19750 dimensionsDefine = []; 19751 findPotentialName = true; 19752 each$1(data, function (colArr, key) { 19753 dimensionsDefine.push(key); 19754 }); 19755 } 19756 } 19757 else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { 19758 var value0 = getDataItemValue(data[0]); 19759 dimensionsDetectCount = isArray(value0) && value0.length || 1; 19760 } 19761 else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { 19762 if (__DEV__) { 19763 assert$1(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.'); 19764 } 19765 } 19766 19767 var potentialNameDimIndex; 19768 if (findPotentialName) { 19769 each$1(dimensionsDefine, function (dim, idx) { 19770 if ((isObject$1(dim) ? dim.name : dim) === 'name') { 19771 potentialNameDimIndex = idx; 19772 } 19773 }); 19774 } 19775 19776 return { 19777 startIndex: startIndex, 19778 dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine), 19779 dimensionsDetectCount: dimensionsDetectCount, 19780 potentialNameDimIndex: potentialNameDimIndex 19781 // TODO: potentialIdDimIdx 19782 }; 19783 } 19784 19785// Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'], 19786// which is reasonable. But dimension name is duplicated. 19787// Returns undefined or an array contains only object without null/undefiend or string. 19788 function normalizeDimensionsDefine(dimensionsDefine) { 19789 if (!dimensionsDefine) { 19790 // The meaning of null/undefined is different from empty array. 19791 return; 19792 } 19793 var nameMap = createHashMap(); 19794 return map(dimensionsDefine, function (item, index) { 19795 item = extend({}, isObject$1(item) ? item : {name: item}); 19796 19797 // User can set null in dimensions. 19798 // We dont auto specify name, othewise a given name may 19799 // cause it be refered unexpectedly. 19800 if (item.name == null) { 19801 return item; 19802 } 19803 19804 // Also consider number form like 2012. 19805 item.name += ''; 19806 // User may also specify displayName. 19807 // displayName will always exists except user not 19808 // specified or dim name is not specified or detected. 19809 // (A auto generated dim name will not be used as 19810 // displayName). 19811 if (item.displayName == null) { 19812 item.displayName = item.name; 19813 } 19814 19815 var exist = nameMap.get(item.name); 19816 if (!exist) { 19817 nameMap.set(item.name, {count: 1}); 19818 } 19819 else { 19820 item.name += '-' + exist.count++; 19821 } 19822 19823 return item; 19824 }); 19825 } 19826 19827 function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) { 19828 maxLoop == null && (maxLoop = Infinity); 19829 if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { 19830 for (var i = 0; i < data.length && i < maxLoop; i++) { 19831 cb(data[i] ? data[i][0] : null, i); 19832 } 19833 } 19834 else { 19835 var value0 = data[0] || []; 19836 for (var i = 0; i < value0.length && i < maxLoop; i++) { 19837 cb(value0[i], i); 19838 } 19839 } 19840 } 19841 19842 function objectRowsCollectDimensions(data) { 19843 var firstIndex = 0; 19844 var obj; 19845 while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line 19846 if (obj) { 19847 var dimensions = []; 19848 each$1(obj, function (value, key) { 19849 dimensions.push(key); 19850 }); 19851 return dimensions; 19852 } 19853 } 19854 19855// ??? TODO merge to completedimensions, where also has 19856// default encode making logic. And the default rule 19857// should depends on series? consider 'map'. 19858 function makeDefaultEncode( 19859 seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult 19860 ) {
19861 var coordSysDefine = getCoordSysDefineBySeries(seriesModel); 19862 var encode = {}; 19863 // var encodeTooltip = []; 19864 // var encodeLabel = []; 19865 var encodeItemName = []; 19866 var encodeSeriesName = []; 19867 var seriesType = seriesModel.subType; 19868 19869 // ??? TODO refactor: provide by series itself. 19870 // Consider the case: 'map' series is based on geo coordSys, 19871 // 'graph', 'heatmap' can be based on cartesian. But can not 19872 // give default rule simply here. 19873 var nSeriesMap = createHashMap(['pie', 'map', 'funnel']); 19874 var cSeriesMap = createHashMap([ 19875 'line', 'bar', 'pictorialBar', 'scatter', 'effectScatter', 'candlestick', 'boxplot' 19876 ]); 19877 19878 // Usually in this case series will use the first data 19879 // dimension as the "value" dimension, or other default 19880 // processes respectively. 19881 if (coordSysDefine && cSeriesMap.get(seriesType) != null) { 19882 var ecModel = seriesModel.ecModel; 19883 var datasetMap = inner$3(ecModel).datasetMap; 19884 var key = datasetModel.uid + '_' + seriesLayoutBy; 19885 var datasetRecord = datasetMap.get(key) 19886 || datasetMap.set(key, {categoryWayDim: 1, valueWayDim: 0}); 19887 19888 // TODO 19889 // Auto detect first time axis and do arrangement. 19890 each$1(coordSysDefine.coordSysDims, function (coordDim) { 19891 // In value way. 19892 if (coordSysDefine.firstCategoryDimIndex == null) { 19893 var dataDim = datasetRecord.valueWayDim++; 19894 encode[coordDim] = dataDim; 19895 19896 // ??? TODO give a better default series name rule? 19897 // especially when encode x y specified. 19898 // consider: when mutiple series share one dimension 19899 // category axis, series name should better use 19900 // the other dimsion name. On the other hand, use 19901 // both dimensions name. 19902 19903 encodeSeriesName.push(dataDim); 19904 // encodeTooltip.push(dataDim); 19905 // encodeLabel.push(dataDim); 19906 } 19907 // In category way, category axis. 19908 else if (coordSysDefine.categoryAxisMap.get(coordDim)) { 19909 encode[coordDim] = 0; 19910 encodeItemName.push(0); 19911 } 19912 // In category way, non-category axis. 19913 else { 19914 var dataDim = datasetRecord.categoryWayDim++; 19915 encode[coordDim] = dataDim; 19916 // encodeTooltip.push(dataDim); 19917 // encodeLabel.push(dataDim); 19918 encodeSeriesName.push(dataDim); 19919 } 19920 }); 19921 } 19922 // Do not make a complex rule! Hard to code maintain and not necessary. 19923 // ??? TODO refactor: provide by series itself. 19924 // [{name: ..., value: ...}, ...] like: 19925 else if (nSeriesMap.get(seriesType) != null) { 19926 // Find the first not ordinal. (5 is an experience value) 19927 var firstNotOrdinal; 19928 for (var i = 0; i < 5 && firstNotOrdinal == null; i++) { 19929 if (!doGuessOrdinal( 19930 data, sourceFormat, seriesLayoutBy, 19931 completeResult.dimensionsDefine, completeResult.startIndex, i 19932 )) { 19933 firstNotOrdinal = i; 19934 } 19935 } 19936 if (firstNotOrdinal != null) { 19937 encode.value = firstNotOrdinal; 19938 var nameDimIndex = completeResult.potentialNameDimIndex 19939 || Math.max(firstNotOrdinal - 1, 0); 19940 // By default, label use itemName in charts. 19941 // So we dont set encodeLabel here. 19942 encodeSeriesName.push(nameDimIndex); 19943 encodeItemName.push(nameDimIndex); 19944 // encodeTooltip.push(firstNotOrdinal); 19945 } 19946 } 19947 19948 // encodeTooltip.length && (encode.tooltip = encodeTooltip); 19949 // encodeLabel.length && (encode.label = encodeLabel); 19950 encodeItemName.length && (encode.itemName = encodeItemName); 19951 encodeSeriesName.length && (encode.seriesName = encodeSeriesName); 19952 19953 return encode; 19954 } 19955 19956 /** 19957 * If return null/undefined, indicate that should not use datasetModel. 19958 */ 19959 function getDatasetModel(seriesModel) { 19960 var option = seriesModel.option; 19961 // Caution: consider the scenario: 19962 // A dataset is declared and a series is not expected to use the dataset, 19963 // and at the beginning `setOption({series: { noData })` (just prepare other 19964 // option but no data), then `setOption({series: {data: [...]}); In this case, 19965 // the user should set an empty array to avoid that dataset is used by default. 19966 var thisData = option.data; 19967 if (!thisData) { 19968 return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0); 19969 } 19970 } 19971 19972 /** 19973 * The rule should not be complex, otherwise user might not 19974 * be able to known where the data is wrong. 19975 * The code is ugly, but how to make it neat? 19976 * 19977 * @param {module:echars/data/Source} source 19978 * @param {number} dimIndex 19979 * @return {boolean} Whether ordinal. 19980 */ 19981 function guessOrdinal(source, dimIndex) { 19982 return doGuessOrdinal( 19983 source.data, 19984 source.sourceFormat, 19985 source.seriesLayoutBy, 19986 source.dimensionsDefine, 19987 source.startIndex, 19988 dimIndex 19989 ); 19990 } 19991 19992// dimIndex may be overflow source data. 19993 function doGuessOrdinal( 19994 data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex 19995 ) { 19996 var result; 19997 // Experience value. 19998 var maxLoop = 5; 19999 20000 if (isTypedArray(data)) { 20001 return false; 20002 } 20003 20004 // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine 20005 // always exists in source. 20006 var dimName; 20007 if (dimensionsDefine) {
20008 dimName = dimensionsDefine[dimIndex]; 20009 dimName = isObject$1(dimName) ? dimName.name : dimName; 20010 } 20011 20012 if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { 20013 if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { 20014 var sample = data[dimIndex]; 20015 for (var i = 0; i < (sample || []).length && i < maxLoop; i++) { 20016 if ((result = detectValue(sample[startIndex + i])) != null) { 20017 return result; 20018 } 20019 } 20020 } 20021 else { 20022 for (var i = 0; i < data.length && i < maxLoop; i++) { 20023 var row = data[startIndex + i]; 20024 if (row && (result = detectValue(row[dimIndex])) != null) { 20025 return result; 20026 } 20027 } 20028 } 20029 } 20030 else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { 20031 if (!dimName) { 20032 return; 20033 } 20034 for (var i = 0; i < data.length && i < maxLoop; i++) { 20035 var item = data[i]; 20036 if (item && (result = detectValue(item[dimName])) != null) { 20037 return result; 20038 } 20039 } 20040 } 20041 else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { 20042 if (!dimName) { 20043 return; 20044 } 20045 var sample = data[dimName]; 20046 if (!sample || isTypedArray(sample)) { 20047 return false; 20048 } 20049 for (var i = 0; i < sample.length && i < maxLoop; i++) { 20050 if ((result = detectValue(sample[i])) != null) { 20051 return result; 20052 } 20053 } 20054 } 20055 else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { 20056 for (var i = 0; i < data.length && i < maxLoop; i++) { 20057 var item = data[i]; 20058 var val = getDataItemValue(item); 20059 if (!isArray(val)) { 20060 return false; 20061 } 20062 if ((result = detectValue(val[dimIndex])) != null) { 20063 return result; 20064 } 20065 } 20066 } 20067 20068 function detectValue(val) { 20069 // Consider usage convenience, '1', '2' will be treated as "number". 20070 // `isFinit('')` get `true`. 20071 if (val != null && isFinite(val) && val !== '') { 20072 return false; 20073 } 20074 else if (isString(val) && val !== '-') { 20075 return true; 20076 } 20077 } 20078 20079 return false; 20080 } 20081 20082 /* 20083* Licensed to the Apache Software Foundation (ASF) under one 20084* or more contributor license agreements. See the NOTICE file 20085* distributed with this work for additional information 20086* regarding copyright ownership. The ASF licenses this file 20087* to you under the Apache License, Version 2.0 (the 20088* "License"); you may not use this file except in compliance 20089* with the License. You may obtain a copy of the License at 20090* 20091* http://www.apache.org/licenses/LICENSE-2.0 20092* 20093* Unless required by applicable law or agreed to in writing, 20094* software distributed under the License is distributed on an 20095* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20096* KIND, either express or implied. See the License for the 20097* specific language governing permissions and limitations 20098* under the License. 20099*/ 20100 20101 /** 20102 * ECharts global model 20103 * 20104 * @module {echarts/model/Global} 20105 */ 20106 20107 20108 /** 20109 * Caution: If the mechanism should be changed some day, these cases 20110 * should be considered: 20111 * 20112 * (1) In `merge option` mode, if using the same option to call `setOption` 20113 * many times, the result should be the same (try our best to ensure that). 20114 * (2) In `merge option` mode, if a component has no id/name specified, it 20115 * will be merged by index, and the result sequence of the components is 20116 * consistent to the original sequence. 20117 * (3) `reset` feature (in toolbox). Find detailed info in comments about 20118 * `mergeOption` in module:echarts/model/OptionManager. 20119 */ 20120 20121 var OPTION_INNER_KEY = '\0_ec_inner'; 20122 20123 /** 20124 * @alias module:echarts/model/Global 20125 * 20126 * @param {Object} option 20127 * @param {module:echarts/model/Model} parentModel 20128 * @param {Object} theme 20129 */ 20130 var GlobalModel = Model.extend({ 20131 20132 init: function (option, parentModel, theme, optionManager) { 20133 theme = theme || {}; 20134 20135 this.option = null; // Mark as not initialized. 20136 20137 /** 20138 * @type {module:echarts/model/Model} 20139 * @private 20140 */ 20141 this._theme = new Model(theme); 20142 20143 /** 20144 * @type {module:echarts/model/OptionManager} 20145 */ 20146 this._optionManager = optionManager; 20147 }, 20148 20149 setOption: function (option, optionPreprocessorFuncs) { 20150 assert$1( 20151 !(OPTION_INNER_KEY in option), 20152 'please use chart.getOption()' 20153 ); 20154 20155 this._optionManager.setOption(option, optionPreprocessorFuncs); 20156 20157 this.resetOption(null); 20158 }, 20159 20160 /** 20161 * @param {string} type null/undefined: reset all. 20162 * 'recreate': force recreate all. 20163 * 'timeline': only reset timeline option 20164 * 'media': only reset media query option 20165 * @return {boolean} Whether option changed. 20166 */ 20167 resetOption: function (type) { 20168 var optionChanged = false; 20169 var optionManager = this._optionManager; 20170 20171 if (!type || type === 'recreate') { 20172 var baseOption = optionManager.mountOption(type === 'recreate'); 20173 20174 if (!this.option || type === 'recreate') { 20175 initBase.call(this, baseOption); 20176 } 20177 else { 20178 this.restoreData(); 20179 this.mergeOption(baseOption); 20180 } 20181 optionChanged = true; 20182 } 20183 20184 if (type === 'timeline' || type === 'media') { 20185 this.restoreData(); 20186 } 20187 20188 if (!type || type === 'recreate' || type === 'timeline') { 20189 var timelineOption = optionManager.getTimelineOption(this); 20190 timelineOption && (this.mergeOption(timelineOption), optionChanged = true); 20191 } 20192 20193 if (!type || type === 'recreate' || type === 'media') { 20194 var mediaOptions = optionManager.getMediaOption(this, this._api); 20195 if (mediaOptions.length) { 20196 each$1(mediaOptions, function (mediaOption) { 20197 this.mergeOption(mediaOption, optionChanged = true); 20198 }, this); 20199 } 20200 } 20201 20202 return optionChanged; 20203 }, 20204 20205 /** 20206 * @protected 20207 */ 20208 mergeOption: function (newOption) { 20209 var option = this.option; 20210 var componentsMap = this._componentsMap; 20211 var newCptTypes = []; 20212 20213 resetSourceDefaulter(this); 20214 20215 // If no component class, merge directly. 20216 // For example: color, animaiton options, etc. 20217 each$1(newOption, function (componentOption, mainType) { 20218 if (componentOption == null) { 20219 return; 20220 } 20221 20222 if (!ComponentModel.hasClass(mainType)) { 20223 // globalSettingTask.dirty(); 20224 option[mainType] = option[mainType] == null 20225 ? clone(componentOption) 20226 : merge(option[mainType], componentOption, true); 20227 } 20228 else if (mainType) { 20229 newCptTypes.push(mainType); 20230 } 20231 }); 20232 20233 ComponentModel.topologicalTravel( 20234 newCptTypes, ComponentModel.getAllClassMainTypes(), visit
20234Component, this 20235 ); 20236 20237 function visitComponent(mainType, dependencies) { 20238 20239 var newCptOptionList = normalizeToArray(newOption[mainType]); 20240 20241 var mapResult = mappingToExists( 20242 componentsMap.get(mainType), newCptOptionList 20243 ); 20244 20245 makeIdAndName(mapResult); 20246 20247 // Set mainType and complete subType. 20248 each$1(mapResult, function (item, index) { 20249 var opt = item.option; 20250 if (isObject$1(opt)) { 20251 item.keyInfo.mainType = mainType; 20252 item.keyInfo.subType = determineSubType(mainType, opt, item.exist); 20253 } 20254 }); 20255 20256 var dependentModels = getComponentsByTypes( 20257 componentsMap, dependencies 20258 ); 20259 20260 option[mainType] = []; 20261 componentsMap.set(mainType, []); 20262 20263 each$1(mapResult, function (resultItem, index) { 20264 var componentModel = resultItem.exist; 20265 var newCptOption = resultItem.option; 20266 20267 assert$1( 20268 isObject$1(newCptOption) || componentModel, 20269 'Empty component definition' 20270 ); 20271 20272 // Consider where is no new option and should be merged using {}, 20273 // see removeEdgeAndAdd in topologicalTravel and 20274 // ComponentModel.getAllClassMainTypes. 20275 if (!newCptOption) { 20276 componentModel.mergeOption({}, this); 20277 componentModel.optionUpdated({}, false); 20278 } 20279 else { 20280 var ComponentModelClass = ComponentModel.getClass( 20281 mainType, resultItem.keyInfo.subType, true 20282 ); 20283 20284 if (componentModel && componentModel instanceof ComponentModelClass) { 20285 componentModel.name = resultItem.keyInfo.name; 20286 // componentModel.settingTask && componentModel.settingTask.dirty(); 20287 componentModel.mergeOption(newCptOption, this); 20288 componentModel.optionUpdated(newCptOption, false); 20289 } 20290 else { 20291 // PENDING Global as parent ? 20292 var extraOpt = extend( 20293 { 20294 dependentModels: dependentModels, 20295 componentIndex: index 20296 }, 20297 resultItem.keyInfo 20298 ); 20299 componentModel = new ComponentModelClass( 20300 newCptOption, this, this, extraOpt 20301 ); 20302 extend(componentModel, extraOpt); 20303 componentModel.init(newCptOption, this, this, extraOpt); 20304 20305 // Call optionUpdated after init. 20306 // newCptOption has been used as componentModel.option 20307 // and may be merged with theme and default, so pass null 20308 // to avoid confusion. 20309 componentModel.optionUpdated(null, true); 20310 } 20311 } 20312 20313 componentsMap.get(mainType)[index] = componentModel; 20314 option[mainType][index] = componentModel.option; 20315 }, this); 20316 20317 // Backup series for filtering. 20318 if (mainType === 'series') { 20319 createSeriesIndices(this, componentsMap.get('series')); 20320 } 20321 } 20322 20323 this._seriesIndicesMap = createHashMap( 20324 this._seriesIndices = this._seriesIndices || [] 20325 ); 20326 }, 20327 20328 /** 20329 * Get option for output (cloned option and inner info removed) 20330 * @public 20331 * @return {Object} 20332 */ 20333 getOption: function () { 20334 var option = clone(this.option); 20335 20336 each$1(option, function (opts, mainType) { 20337 if (ComponentModel.hasClass(mainType)) { 20338 var opts = normalizeToArray(opts); 20339 for (var i = opts.length - 1; i >= 0; i--) { 20340 // Remove options with inner id. 20341 if (isIdInner(opts[i])) { 20342 opts.splice(i, 1); 20343 } 20344 } 20345 option[mainType] = opts; 20346 } 20347 }); 20348 20349 delete option[OPTION_INNER_KEY]; 20350 20351 return option; 20352 }, 20353 20354 /** 20355 * @return {module:echarts/model/Model} 20356 */ 20357 getTheme: function () { 20358 return this._theme; 20359 }, 20360 20361 /** 20362 * @param {string} mainType 20363 * @param {number} [idx=0] 20364 * @return {module:echarts/model/Component} 20365 */ 20366 getComponent: function (mainType, idx) { 20367 var list = this._componentsMap.get(mainType); 20368 if (list) { 20369 return list[idx || 0]; 20370 } 20371 }, 20372 20373 /** 20374 * If none of index and id and name used, return all components with mainType. 20375 * @param {Object} condition 20376 * @param {string} condition.mainType 20377 * @param {string} [condition.subType] If ignore, only query by mainType 20378 * @param {number|Array.<number>} [condition.index] Either input index or id or name. 20379 * @param {string|Array.<string>} [condition.id] Either input index or id or name. 20380 * @param {string|Array.<string>} [condition.name] Either input index or id or name. 20381 * @return {Array.<module:echarts/model/Component>} 20382 */ 20383 queryComponents: function (condition) { 20384 var mainType = condition.mainType; 20385 if (!mainType) { 20386 return []; 20387 } 20388 20389 var index = condition.index; 20390 var id = condition.id; 20391 var name = condition.name; 20392 20393 var cpts = this._componentsMap.get(mainType); 20394 20395 if (!cpts || !cpts.length) { 20396 return []; 20397 } 20398 20399 var result; 20400 20401 if (index != null) { 20402 if (!isArray(index)) { 20403 index = [index]; 20404 } 20405 result = filter(map(index, function (idx) { 20406 return cpts[idx]; 20407 }), function (val) { 20408 return !!val; 20409 }); 20410 } 20411 else if (id != null) { 20412 var isIdArray = isArray(id); 20413 result = filter(cpts, function (cpt) { 20414 return (isIdArray && indexOf(id, cpt.id) >= 0) 20415 || (!isIdArray && cpt.id === id); 20416 }); 20417 } 20418 else if (name != null) { 20419 var isNameArray = isArray(name); 20420 result = filter(cpts, function (cpt) { 20421 return (isNameArray && indexOf(name, cpt.name) >= 0) 20422 || (!isNameArray && cpt.name === name); 20423 }); 20424 } 20425 else { 20426 // Return all components with mainType 20427 result = cpts.slice(); 20428 } 20429 20430 return filterBySubType(result, condition); 20431 }, 20432 20433 /** 20434 * The interface is different from queryComponents, 20435 * which is convenient for inner usage. 20436 * 20437 * @usage 20438 * var result = findComponents( 20439 * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}} 20440 * ); 20441 * var result = findComponents(
20442 * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}} 20443 * ); 20444 * var result = findComponents( 20445 * {mainType: 'series'}, 20446 * function (model, index) {...} 20447 * ); 20448 * // result like [component0, componnet1, ...] 20449 * 20450 * @param {Object} condition 20451 * @param {string} condition.mainType Mandatory. 20452 * @param {string} [condition.subType] Optional. 20453 * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName}, 20454 * where xxx is mainType. 20455 * If query attribute is null/undefined or has no index/id/name, 20456 * do not filtering by query conditions, which is convenient for 20457 * no-payload situations or when target of action is global. 20458 * @param {Function} [condition.filter] parameter: component, return boolean. 20459 * @return {Array.<module:echarts/model/Component>} 20460 */ 20461 findComponents: function (condition) { 20462 var query = condition.query; 20463 var mainType = condition.mainType; 20464 20465 var queryCond = getQueryCond(query); 20466 var result = queryCond 20467 ? this.queryComponents(queryCond) 20468 : this._componentsMap.get(mainType); 20469 20470 return doFilter(filterBySubType(result, condition)); 20471 20472 function getQueryCond(q) { 20473 var indexAttr = mainType + 'Index'; 20474 var idAttr = mainType + 'Id'; 20475 var nameAttr = mainType + 'Name'; 20476 return q && ( 20477 q[indexAttr] != null 20478 || q[idAttr] != null 20479 || q[nameAttr] != null 20480 ) 20481 ? { 20482 mainType: mainType, 20483 // subType will be filtered finally. 20484 index: q[indexAttr], 20485 id: q[idAttr], 20486 name: q[nameAttr] 20487 } 20488 : null; 20489 } 20490 20491 function doFilter(res) { 20492 return condition.filter 20493 ? filter(res, condition.filter) 20494 : res; 20495 } 20496 }, 20497 20498 /** 20499 * @usage 20500 * eachComponent('legend', function (legendModel, index) { 20501 * ... 20502 * }); 20503 * eachComponent(function (componentType, model, index) { 20504 * // componentType does not include subType 20505 * // (componentType is 'xxx' but not 'xxx.aa') 20506 * }); 20507 * eachComponent( 20508 * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}, 20509 * function (model, index) {...} 20510 * ); 20511 * eachComponent( 20512 * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}, 20513 * function (model, index) {...} 20514 * ); 20515 * 20516 * @param {string|Object=} mainType When mainType is object, the definition 20517 * is the same as the method 'findComponents'. 20518 * @param {Function} cb 20519 * @param {*} context 20520 */ 20521 eachComponent: function (mainType, cb, context) { 20522 var componentsMap = this._componentsMap; 20523 20524 if (typeof mainType === 'function') { 20525 context = cb; 20526 cb = mainType; 20527 componentsMap.each(function (components, componentType) { 20528 each$1(components, function (component, index) { 20529 cb.call(context, componentType, component, index); 20530 }); 20531 }); 20532 } 20533 else if (isString(mainType)) { 20534 each$1(componentsMap.get(mainType), cb, context); 20535 } 20536 else if (isObject$1(mainType)) { 20537 var queryResult = this.findComponents(mainType); 20538 each$1(queryResult, cb, context); 20539 } 20540 }, 20541 20542 /** 20543 * @param {string} name 20544 * @return {Array.<module:echarts/model/Series>} 20545 */ 20546 getSeriesByName: function (name) { 20547 var series = this._componentsMap.get('series'); 20548 return filter(series, function (oneSeries) { 20549 return oneSeries.name === name; 20550 }); 20551 }, 20552 20553 /** 20554 * @param {number} seriesIndex 20555 * @return {module:echarts/model/Series} 20556 */ 20557 getSeriesByIndex: function (seriesIndex) { 20558 return this._componentsMap.get('series')[seriesIndex]; 20559 }, 20560 20561 /** 20562 * Get series list before filtered by type. 20563 * FIXME: rename to getRawSeriesByType? 20564 * 20565 * @param {string} subType 20566 * @return {Array.<module:echarts/model/Series>} 20567 */ 20568 getSeriesByType: function (subType) { 20569 var series = this._componentsMap.get('series'); 20570 return filter(series, function (oneSeries) { 20571 return oneSeries.subType === subType; 20572 }); 20573 }, 20574 20575 /** 20576 * @return {Array.<module:echarts/model/Series>} 20577 */ 20578 getSeries: function () { 20579 return this._componentsMap.get('series').slice(); 20580 }, 20581 20582 /** 20583 * @return {number} 20584 */ 20585 getSeriesCount: function () { 20586 return this._componentsMap.get('series').length; 20587 }, 20588 20589 /** 20590 * After filtering, series may be different 20591 * frome raw series. 20592 * 20593 * @param {Function} cb 20594 * @param {*} context 20595 */ 20596 eachSeries: function (cb, context) { 20597 assertSeriesInitialized(this); 20598 each$1(this._seriesIndices, function (rawSeriesIndex) { 20599 var series = this._componentsMap.get('series')[rawSeriesIndex]; 20600 cb.call(context, series, rawSeriesIndex); 20601 }, this); 20602 }, 20603 20604 /** 20605 * Iterate raw series before filtered. 20606 * 20607 * @param {Function} cb 20608 * @param {*} context 20609 */ 20610 eachRawSeries: function (cb, context) { 20611 each$1(this._componentsMap.get('series'), cb, context); 20612 }, 20613 20614 /** 20615 * After filtering, series may be different. 20616 * frome raw series. 20617 * 20618 * @parma {string} subType 20619 * @param {Function} cb 20620 * @param {*} context 20621 */ 20622 eachSeriesByType: function (subType, cb, context) { 20623 assertSeriesInitialized(this); 20624 each$1(this._seriesIndices, function (rawSeriesIndex) { 20625 var series = this._componentsMap.get('series')[rawSeriesIndex]; 20626 if (series.subType === subType) { 20627 cb.call(context, series, rawSeriesIndex); 20628 } 20629 }, this); 20630 }, 20631 20632 /** 20633 * Iterate raw series before filtered of given type. 20634 * 20635 * @parma {string} subType 20636 * @param {Function} cb 20637 * @param {*} context 20638 */ 20639 eachRawSeriesByType: function (subType, cb, context) { 20640 return each$1(this.getSeriesByType(subType), cb, context); 20641 }, 20642 20643 /** 20644 * @param {module:echarts/model/Series} seriesModel 20645 */ 20646 isSeriesFiltered: function (seriesModel) { 20647 assertSeriesInitialized(this); 20648 return this._seriesIndicesMap.get(seriesModel.componentIndex) == null; 20649 }, 20650 20651 /** 20652 * @return {Array.<number>} 20653 */ 20654 getCurrentSeriesIndices: function () { 20655 return (this._seriesIndices || []).slice(); 20656 }, 20657 20658 /** 20659 * @param {Function} cb 20660 * @param {*} context 20661 */ 20662 filterSeries: function (cb, context) { 20663 assertSeriesInitialized(this); 20664 var filteredSeries = filter( 20665 this._componentsMap.get('series'), cb, context 20666 );
20667 createSeriesIndices(this, filteredSeries); 20668 }, 20669 20670 restoreData: function (payload) { 20671 var componentsMap = this._componentsMap; 20672 20673 createSeriesIndices(this, componentsMap.get('series')); 20674 20675 var componentTypes = []; 20676 componentsMap.each(function (components, componentType) { 20677 componentTypes.push(componentType); 20678 }); 20679 20680 ComponentModel.topologicalTravel( 20681 componentTypes, 20682 ComponentModel.getAllClassMainTypes(), 20683 function (componentType, dependencies) { 20684 each$1(componentsMap.get(componentType), function (component) { 20685 (componentType !== 'series' || !isNotTargetSeries(component, payload)) 20686 && component.restoreData(); 20687 }); 20688 } 20689 ); 20690 } 20691 20692 }); 20693 20694 function isNotTargetSeries(seriesModel, payload) { 20695 if (payload) { 20696 var index = payload.seiresIndex; 20697 var id = payload.seriesId; 20698 var name = payload.seriesName; 20699 return (index != null && seriesModel.componentIndex !== index) 20700 || (id != null && seriesModel.id !== id) 20701 || (name != null && seriesModel.name !== name); 20702 } 20703 } 20704 20705 /** 20706 * @inner 20707 */ 20708 function mergeTheme(option, theme) { 20709 // PENDING 20710 // NOT use `colorLayer` in theme if option has `color` 20711 var notMergeColorLayer = option.color && !option.colorLayer; 20712 20713 each$1(theme, function (themeItem, name) { 20714 if (name === 'colorLayer' && notMergeColorLayer) { 20715 return; 20716 } 20717 // æ¿¡åçé� component model 鿬妏éèç¶é¨ï¿½ merge é«æç·«æµãç²°ç� model æ¾¶åæ 20718 if (!ComponentModel.hasClass(name)) { 20719 if (typeof themeItem === 'object') { 20720 option[name] = !option[name] 20721 ? clone(themeItem) 20722 : merge(option[name], themeItem, false); 20723 } 20724 else { 20725 if (option[name] == null) { 20726 option[name] = themeItem; 20727 } 20728 } 20729 } 20730 }); 20731 } 20732 20733 function initBase(baseOption) { 20734 baseOption = baseOption; 20735 20736 // Using OPTION_INNER_KEY to mark that this option can not be used outside, 20737 // i.e. `chart.setOption(chart.getModel().option);` is forbiden. 20738 this.option = {}; 20739 this.option[OPTION_INNER_KEY] = 1; 20740 20741 /** 20742 * Init with series: [], in case of calling findSeries method 20743 * before series initialized. 20744 * @type {Object.<string, Array.<module:echarts/model/Model>>} 20745 * @private 20746 */ 20747 this._componentsMap = createHashMap({series: []}); 20748 20749 /** 20750 * Mapping between filtered series list and raw series list. 20751 * key: filtered series indices, value: raw series indices. 20752 * @type {Array.<nubmer>} 20753 * @private 20754 */ 20755 this._seriesIndices; 20756 20757 this._seriesIndicesMap; 20758 20759 mergeTheme(baseOption, this._theme.option); 20760 20761 // TODO Needs clone when merging to the unexisted property 20762 merge(baseOption, globalDefault, false); 20763 20764 this.mergeOption(baseOption); 20765 } 20766 20767 /** 20768 * @inner 20769 * @param {Array.<string>|string} types model types 20770 * @return {Object} key: {string} type, value: {Array.<Object>} models 20771 */ 20772 function getComponentsByTypes(componentsMap, types) { 20773 if (!isArray(types)) { 20774 types = types ? [types] : []; 20775 } 20776 20777 var ret = {}; 20778 each$1(types, function (type) { 20779 ret[type] = (componentsMap.get(type) || []).slice(); 20780 }); 20781 20782 return ret; 20783 } 20784 20785 /** 20786 * @inner 20787 */ 20788 function determineSubType(mainType, newCptOption, existComponent) { 20789 var subType = newCptOption.type 20790 ? newCptOption.type 20791 : existComponent 20792 ? existComponent.subType 20793 // Use determineSubType only when there is no existComponent. 20794 : ComponentModel.determineSubType(mainType, newCptOption); 20795 20796 // tooltip, markline, markpoint may always has no subType 20797 return subType; 20798 } 20799 20800 /** 20801 * @inner 20802 */ 20803 function createSeriesIndices(ecModel, seriesModels) { 20804 ecModel._seriesIndicesMap = createHashMap(
20805 ecModel._seriesIndices = map(seriesModels, function (series) { 20806 return series.componentIndex; 20807 }) || [] 20808 ); 20809 } 20810 20811 /** 20812 * @inner 20813 */ 20814 function filterBySubType(components, condition) { 20815 // Using hasOwnProperty for restrict. Consider 20816 // subType is undefined in user payload. 20817 return condition.hasOwnProperty('subType') 20818 ? filter(components, function (cpt) { 20819 return cpt.subType === condition.subType; 20820 }) 20821 : components; 20822 } 20823 20824 /** 20825 * @inner 20826 */ 20827 function assertSeriesInitialized(ecModel) { 20828 // Components that use _seriesIndices should depends on series component, 20829 // which make sure that their initialization is after series. 20830 if (__DEV__) { 20831 if (!ecModel._seriesIndices) { 20832 throw new Error('Option should contains series.'); 20833 } 20834 } 20835 } 20836 20837 mixin(GlobalModel, colorPaletteMixin); 20838 20839 /* 20840* Licensed to the Apache Software Foundation (ASF) under one 20841* or more contributor license agreements. See the NOTICE file 20842* distributed with this work for additional information 20843* regarding copyright ownership. The ASF licenses this file 20844* to you under the Apache License, Version 2.0 (the 20845* "License"); you may not use this file except in compliance 20846* with the License. You may obtain a copy of the License at 20847* 20848* http://www.apache.org/licenses/LICENSE-2.0 20849* 20850* Unless required by applicable law or agreed to in writing, 20851* software distributed under the License is distributed on an 20852* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20853* KIND, either express or implied. See the License for the 20854* specific language governing permissions and limitations 20855* under the License. 20856*/ 20857 20858 var echartsAPIList = [ 20859 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed', 20860 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption', 20861 'getViewOfComponentModel', 'getViewOfSeriesModel' 20862 ]; 20863// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js 20864 20865 function ExtensionAPI(chartInstance) { 20866 each$1(echartsAPIList, function (name) { 20867 this[name] = bind(chartInstance[name], chartInstance); 20868 }, this); 20869 } 20870 20871 /* 20872* Licensed to the Apache Software Foundation (ASF) under one 20873* or more contributor license agreements. See the NOTICE file 20874* distributed with this work for additional information 20875* regarding copyright ownership. The ASF licenses this file 20876* to you under the Apache License, Version 2.0 (the 20877* "License"); you may not use this file except in compliance 20878* with the License. You may obtain a copy of the License at 20879* 20880* http://www.apache.org/licenses/LICENSE-2.0 20881* 20882* Unless required by applicable law or agreed to in writing, 20883* software distributed under the License is distributed on an 20884* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20885* KIND, either express or implied. See the License for the 20886* specific language governing permissions and limitations 20887* under the License. 20888*/ 20889 20890 var coordinateSystemCreators = {}; 20891 20892 function CoordinateSystemManager() { 20893 20894 this._coordinateSystems = []; 20895 } 20896 20897 CoordinateSystemManager.prototype = { 20898 20899 constructor: CoordinateSystemManager, 20900 20901 create: function (ecModel, api) { 20902 var coordinateSystems = []; 20903 each$1(coordinateSystemCreators, function (creater, type) { 20904 var list = creater.create(ecModel, api); 20905 coordinateSystems = coordinateSystems.concat(list || []); 20906 }); 20907 20908 this._coordinateSystems = coordinateSystems; 20909 }, 20910 20911 update: function (ecModel, api) { 20912 each$1(this._coordinateSystems, function (coordSys) { 20913 coordSys.update && coordSys.update(ecModel, api); 20914 }); 20915 }, 20916 20917 getCoordinateSystems: function () { 20918 return this._coordinateSystems.slice(); 20919 } 20920 }; 20921 20922 CoordinateSystemManager.register = function (type, coordinateSystemCreator) { 20923 coordinateSystemCreators[type] = coordinateSystemCreator; 20924 }; 20925 20926 CoordinateSystemManager.get = function (type) { 20927 return coordinateSystemCreators[type]; 20928 }; 20929 20930 /* 20931* Licensed to the Apache Software Foundation (ASF) under one 20932* or more contributor license agreements. See the NOTICE file 20933* distributed with this work for additional information 20934* regarding copyright ownership. The ASF licenses this file 20935* to you under the Apache License, Version 2.0 (the 20936* "License");
20936 you may not use this file except in compliance 20937* with the License. You may obtain a copy of the License at 20938* 20939* http://www.apache.org/licenses/LICENSE-2.0 20940* 20941* Unless required by applicable law or agreed to in writing, 20942* software distributed under the License is distributed on an 20943* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20944* KIND, either express or implied. See the License for the 20945* specific language governing permissions and limitations 20946* under the License. 20947*/ 20948 20949 /** 20950 * ECharts option manager 20951 * 20952 * @module {echarts/model/OptionManager} 20953 */ 20954 20955 20956 var each$4 = each$1; 20957 var clone$3 = clone; 20958 var map$1 = map; 20959 var merge$1 = merge; 20960 20961 var QUERY_REG = /^(min|max)?(.+)$/; 20962 20963 /** 20964 * TERM EXPLANATIONS: 20965 * 20966 * [option]: 20967 * 20968 * An object that contains definitions of components. For example: 20969 * var option = { 20970 * title: {...}, 20971 * legend: {...}, 20972 * visualMap: {...}, 20973 * series: [ 20974 * {data: [...]}, 20975 * {data: [...]}, 20976 * ... 20977 * ] 20978 * }; 20979 * 20980 * [rawOption]: 20981 * 20982 * An object input to echarts.setOption. 'rawOption' may be an 20983 * 'option', or may be an object contains multi-options. For example: 20984 * var option = { 20985 * baseOption: { 20986 * title: {...}, 20987 * legend: {...}, 20988 * series: [ 20989 * {data: [...]}, 20990 * {data: [...]}, 20991 * ... 20992 * ] 20993 * }, 20994 * timeline: {...}, 20995 * options: [ 20996 * {title: {...}, series: {data: [...]}}, 20997 * {title: {...}, series: {data: [...]}}, 20998 * ... 20999 * ], 21000 * media: [ 21001 * { 21002 * query: {maxWidth: 320}, 21003 * option: {series: {x: 20}, visualMap: {show: false}} 21004 * }, 21005 * { 21006 * query: {minWidth: 320, maxWidth: 720}, 21007 * option: {series: {x: 500}, visualMap: {show: true}} 21008 * }, 21009 * { 21010 * option: {series: {x: 1200}, visualMap: {show: true}} 21011 * } 21012 * ] 21013 * }; 21014 * 21015 * @alias module:echarts/model/OptionManager 21016 * @param {module:echarts/ExtensionAPI} api 21017 */ 21018 function OptionManager(api) { 21019 21020 /** 21021 * @private 21022 * @type {module:echarts/ExtensionAPI} 21023 */ 21024 this._api = api; 21025 21026 /** 21027 * @private 21028 * @type {Array.<number>} 21029 */ 21030 this._timelineOptions = []; 21031 21032 /** 21033 * @private 21034 * @type {Array.<Object>} 21035 */ 21036 this._mediaList = []; 21037 21038 /** 21039 * @private 21040 * @type {Object} 21041 */ 21042 this._mediaDefault; 21043 21044 /** 21045 * -1, means default. 21046 * empty means no media. 21047 * @private 21048 * @type {Array.<number>} 21049 */ 21050 this._currentMediaIndices = []; 21051 21052 /** 21053 * @private 21054 * @type {Object} 21055 */ 21056 this._optionBackup; 21057 21058 /** 21059 * @private 21060 * @type {Object} 21061 */ 21062 this._newBaseOption; 21063 } 21064 21065// timeline.notMerge is not supported in ec3. Firstly there is rearly 21066// case that notMerge is needed. Secondly supporting 'notMerge' requires 21067// rawOption cloned and backuped when timeline changed, which does no 21068// good to performance. What's more, that both timeline and setOption 21069// method supply 'notMerge' brings complex and some problems. 21070// Consider this case: 21071// (step1) chart.setOption({timeline: {notMerge: false}, ...}, false); 21072// (step2) chart.setOption({timeline: {notMerge: true}, ...}, false); 21073 21074 OptionManager.prototype = { 21075 21076 constructor: OptionManager, 21077 21078 /** 21079 * @public 21080 * @param {Object} rawOption Raw option. 21081 * @param {module:echarts/model/Global} ecModel 21082 * @param {Array.<Function>} optionPreprocessorFuncs 21083 * @return {Object} Init option 21084 */ 21085 setOption: function (rawOption, optionPreprocessorFuncs) { 21086 if (rawOption) { 21087 // That set dat primitive is dangerous if user reuse the data when setOption again. 21088 each$1(normalizeToArray(rawOption.series), function (series) { 21089 series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data); 21090 }); 21091 } 21092 21093 // Caution: some series modify option data, if do not clone, 21094 // it should ensure that the repeat modify correctly 21095 // (create a new object when modify itself). 21096 rawOption = clone$3(rawOption, true); 21097 21098 // FIXME 21099 // æ¿¡åç timeline options é´æ ¬â¬ï¿½ media æ¶î ¡îç¼î»ç°¡éæªéçç´â¬Ñç´é°å®aseOptionæ¶î ç éå¤îç¼îç´éæ¬ç¹çå²îé娿â¬ï¿½ 21100 21101 var oldOptionBackup = this._optionBackup; 21102 var newParsedOption = parseRawOption.call( 21103 this, rawOption, optionPreprocessorFuncs, !oldOptionBackup 21104 ); 21105 this._newBaseOption = newParsedOption.baseOption; 21106 21107 // For setOption at second time (using merge mode); 21108 if (oldOptionBackup) { 21109 // Only baseOption can be merged. 21110 mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption); 21111 21112 // For simplicity, timeline options and media options do not support merge, 21113 // that is, if you `setOption` twice and both has timeline options, the latter 21114 // timeline opitons will not be merged to the formers, but just substitude them. 21115 if (newParsedOption.timelineOptions.length) { 21116 oldOptionBackup.timelineOptions = newParsedOption.timelineOptions; 21117 } 21118 if (newParsedOption.mediaList.length) { 21119 oldOptionBackup.mediaList = newParsedOption.mediaList; 21120 } 21121 if (newParsedOption.mediaDefault) { 21122 oldOptionBackup.mediaDefault = newParsedOption.mediaDefault; 21123 } 21124 } 21125 else { 21126 this._optionBackup = newParsedOption; 21127 } 21128 }, 21129 21130 /** 21131 * @param {boolean} isRecreate 21132 * @return {Object} 21133 */ 21134 mountOption: function (isRecreate) { 21135 var optionBackup = this._optionBackup; 21136 21137 // TODO 21138 // æ¿¡åçå¨âæ¹resetéç»å 鿬ç¬cloneé� 21139 21140 this._timelineOptions = map$1(optionBackup.timelineOptions, clone$3); 21141 this._mediaList = map$1(optionBackup.mediaList, clone$3); 21142 this._mediaDefault = clone$3(optionBackup.mediaDefault); 21143 this._currentMediaIndices = []; 21144 21145 return clone$3(isRecreate 21146 // this._optionBackup.baseOption, which is created at the first `setOption` 21147 // called, and is merged into every new option by inner method `mergeOption` 21148 // each time `setOption` called, can be only used in `isRecreate`, because 21149 // its reliability is under suspicion. In other cases option merge is 21150 // performed by `model.mergeOption`. 21151 ? optionBackup.baseOption : this._newBaseOption 21152 ); 21153 }, 21154 21155 /** 21156 * @param {module:echarts/model/Global} ecModel 21157 * @return {Object} 21158 */ 21159 getTimelineOption: function (ecModel) { 21160 var option; 21161 var timelineOptions = this._timelineOptions; 21162 21163 if (timelineOptions.length) { 21164 // getTimelineOption can only be called after ecModel inited, 21165 // so we can get currentIndex from timelineModel. 21166 var timelineModel = ecModel.getComponent('timeline'); 21167 if (timelineModel) { 21168 option = clone$3( 21169 timelineOptions[timelineModel.getCurrentIndex()], 21170 true 21171 ); 21172 } 21173 } 21174 21175 return option; 21176 }, 21177 21178 /** 21179 * @param {module:echarts/model/Global} ecModel 21180 * @return {Array.<Object>} 21181 */ 21182 getMediaOption: function (ecModel) { 21183 var ecWidth = this._api.getWidth(); 21184 var ecHeight = this._api.getHeight(); 21185 var mediaList = this._mediaList; 21186 var mediaDefault = this._mediaDefault; 21187 var indices = []; 21188 var result = []; 21189 21190 // No media defined. 21191 if (!mediaList.length && !mediaDefault) { 21192 return result; 21193 } 21194 21195 // Multi media may be applied, the latter defined media has higher priority. 21196 for (var i = 0, len = mediaList.length; i < len; i++) {
21197 if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) { 21198 indices.push(i); 21199 } 21200 } 21201 21202 // FIXME 21203 // éîæmediaDefaultæ´æî寮ååé¢ã¦åçå§çéå±½æéæ¬å½²é³ææ ¨ééç¬é³èæ´è¤°æâ¬ï¿½ 21204 if (!indices.length && mediaDefault) { 21205 indices = [-1]; 21206 } 21207 21208 if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) { 21209 result = map$1(indices, function (index) { 21210 return clone$3( 21211 index === -1 ? mediaDefault.option : mediaList[index].option 21212 ); 21213 }); 21214 } 21215 // Otherwise return nothing. 21216 21217 this._currentMediaIndices = indices; 21218 21219 return result; 21220 } 21221 }; 21222 21223 function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) { 21224 var timelineOptions = []; 21225 var mediaList = []; 21226 var mediaDefault; 21227 var baseOption; 21228 21229 // Compatible with ec2. 21230 var timelineOpt = rawOption.timeline; 21231 21232 if (rawOption.baseOption) { 21233 baseOption = rawOption.baseOption; 21234 } 21235 21236 // For timeline 21237 if (timelineOpt || rawOption.options) { 21238 baseOption = baseOption || {}; 21239 timelineOptions = (rawOption.options || []).slice(); 21240 } 21241 21242 // For media query 21243 if (rawOption.media) { 21244 baseOption = baseOption || {}; 21245 var media = rawOption.media; 21246 each$4(media, function (singleMedia) { 21247 if (singleMedia && singleMedia.option) { 21248 if (singleMedia.query) { 21249 mediaList.push(singleMedia); 21250 } 21251 else if (!mediaDefault) { 21252 // Use the first media default. 21253 mediaDefault = singleMedia; 21254 } 21255 } 21256 }); 21257 } 21258 21259 // For normal option 21260 if (!baseOption) { 21261 baseOption = rawOption; 21262 } 21263 21264 // Set timelineOpt to baseOption in ec3, 21265 // which is convenient for merge option. 21266 if (!baseOption.timeline) { 21267 baseOption.timeline = timelineOpt; 21268 } 21269 21270 // Preprocess. 21271 each$4([baseOption].concat(timelineOptions) 21272 .concat(map(mediaList, function (media) { 21273 return media.option; 21274 })), 21275 function (option) { 21276 each$4(optionPreprocessorFuncs, function (preProcess) { 21277 preProcess(option, isNew); 21278 }); 21279 } 21280 ); 21281 21282 return { 21283 baseOption: baseOption, 21284 timelineOptions: timelineOptions, 21285 mediaDefault: mediaDefault, 21286 mediaList: mediaList 21287 }; 21288 } 21289 21290 /** 21291 * @see <http://www.w3.org/TR/css3-mediaqueries/#media1> 21292 * Support: width, height, aspectRatio 21293 * Can use max or min as prefix. 21294 */ 21295 function applyMediaQuery(query, ecWidth, ecHeight) { 21296 var realMap = { 21297 width: ecWidth, 21298 height: ecHeight, 21299 aspectratio: ecWidth / ecHeight // lowser case for convenientce. 21300 }; 21301 21302 var applicatable = true; 21303 21304 each$1(query, function (value, attr) { 21305 var matched = attr.match(QUERY_REG); 21306 21307 if (!matched || !matched[1] || !matched[2]) { 21308 return; 21309 } 21310 21311 var operator = matched[1]; 21312 var realAttr = matched[2].toLowerCase(); 21313 21314 if (!compare(realMap[realAttr], value, operator)) { 21315 applicatable = false; 21316 } 21317 }); 21318 21319 return applicatable; 21320 } 21321 21322 function compare(real, expect, operator) { 21323 if (operator === 'min') { 21324 return real >= expect; 21325 } 21326 else if (operator === 'max') { 21327 return real <= expect; 21328 } 21329 else { // Equals 21330 return real === expect; 21331 } 21332 } 21333 21334 function indicesEquals(indices1, indices2) { 21335 // indices is always order by asc and has only finite number. 21336 return indices1.join(',') === indices2.join(','); 21337 } 21338 21339 /** 21340 * Consider case: 21341 * `chart.setOption(opt1);` 21342 * Then user do some interaction like dataZoom, dataView changing. 21343 * `chart.setOption(opt2);` 21344 * Then user press 'reset button' in toolbox. 21345 * 21346 * After doing that all of the interaction effects should be reset, the 21347 * chart should be the same as the result of invoke 21348 * `chart.setOption(opt1); chart.setOption(opt2);`. 21349 * 21350 * Although it is not able ensure that 21351 * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to 21352 * `chart.setOption(merge(opt1, opt2));` exactly, 21353 * this might be the only simple way to implement that feature. 21354 * 21355 * MEMO: We've considered some other approaches: 21356 * 1. Each model handle its self restoration but not uniform treatment. 21357 * (Too complex in logic and error-prone) 21358 * 2. Use a shadow ecModel. (Performace expensive) 21359 */ 21360 function mergeOption(oldOption, newOption) { 21361 newOption = newOption || {}; 21362 21363 each$4(newOption, function (newCptOpt, mainType) { 21364 if (newCptOpt == null) { 21365 return; 21366 } 21367 21368 var oldCptOpt = oldOption[mainType]; 21369 21370 if (!ComponentModel.hasClass(mainType)) { 21371 oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true); 21372 } 21373 else { 21374 newCptOpt = normalizeToArray(newCptOpt); 21375 oldCptOpt = normalizeToArray(oldCptOpt); 21376 21377 var mapResult = mappingToExists(oldCptOpt, newCptOpt); 21378 21379 oldOption[mainType] = map$1(mapResult, function (item) { 21380 return (item.option && item.exist) 21381 ? merge$1(item.exist, item.option, true) 21382 : (item.exist || item.option); 21383 }); 21384 } 21385 }); 21386 } 21387 21388 /* 21389* Licensed to the Apache Software Foundation (ASF) under one 21390* or more contributor license agreements. See the NOTICE file 21391* distributed with this work for additional information 21392* regarding copyright ownership. The ASF licenses this file 21393* to you under the Apache License, Version 2.0 (the 21394* "License");
vendor: 11,072 bytes, lines 21394-21692
21394 you may not use this file except in compliance 21395* with the License. You may obtain a copy of the License at 21396* 21397* http://www.apache.org/licenses/LICENSE-2.0 21398* 21399* Unless required by applicable law or agreed to in writing, 21400* software distributed under the License is distributed on an 21401* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21402* KIND, either express or implied. See the License for the 21403* specific language governing permissions and limitations 21404* under the License. 21405*/ 21406 21407 var each$5 = each$1; 21408 var isObject$3 = isObject$1; 21409 21410 var POSSIBLE_STYLES = [ 21411 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 21412 'chordStyle', 'label', 'labelLine' 21413 ]; 21414 21415 function compatEC2ItemStyle(opt) { 21416 var itemStyleOpt = opt && opt.itemStyle; 21417 if (!itemStyleOpt) { 21418 return; 21419 } 21420 for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) { 21421 var styleName = POSSIBLE_STYLES[i]; 21422 var normalItemStyleOpt = itemStyleOpt.normal; 21423 var emphasisItemStyleOpt = itemStyleOpt.emphasis; 21424 if (normalItemStyleOpt && normalItemStyleOpt[styleName]) { 21425 opt[styleName] = opt[styleName] || {}; 21426 if (!opt[styleName].normal) { 21427 opt[styleName].normal = normalItemStyleOpt[styleName]; 21428 } 21429 else { 21430 merge(opt[styleName].normal, normalItemStyleOpt[styleName]); 21431 } 21432 normalItemStyleOpt[styleName] = null; 21433 } 21434 if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) { 21435 opt[styleName] = opt[styleName] || {}; 21436 if (!opt[styleName].emphasis) { 21437 opt[styleName].emphasis = emphasisItemStyleOpt[styleName]; 21438 } 21439 else { 21440 merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]); 21441 } 21442 emphasisItemStyleOpt[styleName] = null; 21443 } 21444 } 21445 } 21446 21447 function convertNormalEmphasis(opt, optType, useExtend) { 21448 if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) { 21449 var normalOpt = opt[optType].normal; 21450 var emphasisOpt = opt[optType].emphasis; 21451 21452 if (normalOpt) { 21453 // Timeline controlStyle has other properties besides normal and emphasis 21454 if (useExtend) { 21455 opt[optType].normal = opt[optType].emphasis = null; 21456 defaults(opt[optType], normalOpt); 21457 } 21458 else { 21459 opt[optType] = normalOpt; 21460 } 21461 } 21462 if (emphasisOpt) { 21463 opt.emphasis = opt.emphasis || {}; 21464 opt.emphasis[optType] = emphasisOpt; 21465 } 21466 } 21467 } 21468 function removeEC3NormalStatus(opt) { 21469 convertNormalEmphasis(opt, 'itemStyle'); 21470 convertNormalEmphasis(opt, 'lineStyle'); 21471 convertNormalEmphasis(opt, 'areaStyle'); 21472 convertNormalEmphasis(opt, 'label'); 21473 convertNormalEmphasis(opt, 'labelLine'); 21474 // treemap 21475 convertNormalEmphasis(opt, 'upperLabel'); 21476 // graph 21477 convertNormalEmphasis(opt, 'edgeLabel'); 21478 } 21479 21480 function compatTextStyle(opt, propName) { 21481 // Check whether is not object (string\null\undefined ...) 21482 var labelOptSingle = isObject$3(opt) && opt[propName]; 21483 var textStyle = isObject$3(labelOptSingle) && labelOptSingle.textStyle; 21484 if (textStyle) { 21485 for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) { 21486 var propName = TEXT_STYLE_OPTIONS[i]; 21487 if (textStyle.hasOwnProperty(propName)) { 21488 labelOptSingle[propName] = textStyle[propName]; 21489 } 21490 } 21491 } 21492 } 21493 21494 function compatEC3CommonStyles(opt) { 21495 if (opt) { 21496 removeEC3NormalStatus(opt); 21497 compatTextStyle(opt, 'label'); 21498 opt.emphasis && compatTextStyle(opt.emphasis, 'label'); 21499 } 21500 } 21501 21502 function processSeries(seriesOpt) { 21503 if (!isObject$3(seriesOpt)) { 21504 return; 21505 } 21506 21507 compatEC2ItemStyle(seriesOpt); 21508 removeEC3NormalStatus(seriesOpt); 21509 21510 compatTextStyle(seriesOpt, 'label'); 21511 // treemap 21512 compatTextStyle(seriesOpt, 'upperLabel'); 21513 // graph 21514 compatTextStyle(seriesOpt, 'edgeLabel'); 21515 if (seriesOpt.emphasis) { 21516 compatTextStyle(seriesOpt.emphasis, 'label'); 21517 // treemap 21518 compatTextStyle(seriesOpt.emphasis, 'upperLabel'); 21519 // graph 21520 compatTextStyle(seriesOpt.emphasis, 'edgeLabel'); 21521 } 21522 21523 var markPoint = seriesOpt.markPoint; 21524 if (markPoint) { 21525 compatEC2ItemStyle(markPoint); 21526 compatEC3CommonStyles(markPoint); 21527 } 21528 21529 var markLine = seriesOpt.markLine; 21530 if (markLine) { 21531 compatEC2ItemStyle(markLine); 21532 compatEC3CommonStyles(markLine); 21533 } 21534 21535 var markArea = seriesOpt.markArea; 21536 if (markArea) { 21537 compatEC3CommonStyles(markArea); 21538 } 21539 21540 var data = seriesOpt.data; 21541 21542 // Break with ec3: if `setOption` again, there may be no `type` in option, 21543 // then the backward compat based on option type will not be performed. 21544 21545 if (seriesOpt.type === 'graph') { 21546 data = data || seriesOpt.nodes; 21547 var edgeData = seriesOpt.links || seriesOpt.edges; 21548 if (edgeData && !isTypedArray(edgeData)) { 21549 for (var i = 0; i < edgeData.length; i++) { 21550 compatEC3CommonStyles(edgeData[i]); 21551 } 21552 } 21553 each$1(seriesOpt.categories, function (opt) { 21554 removeEC3NormalStatus(opt); 21555 }); 21556 } 21557 21558 if (data && !isTypedArray(data)) { 21559 for (var i = 0; i < data.length; i++) { 21560 compatEC3CommonStyles(data[i]); 21561 } 21562 } 21563 21564 // mark point data 21565 var markPoint = seriesOpt.markPoint; 21566 if (markPoint && markPoint.data) { 21567 var mpData = markPoint.data; 21568 for (var i = 0; i < mpData.length; i++) { 21569 compatEC3CommonStyles(mpData[i]); 21570 } 21571 } 21572 // mark line data 21573 var markLine = seriesOpt.markLine; 21574 if (markLine && markLine.data) { 21575 var mlData = markLine.data; 21576 for (var i = 0; i < mlData.length; i++) { 21577 if (isArray(mlData[i])) { 21578 compatEC3CommonStyles(mlData[i][0]); 21579 compatEC3CommonStyles(mlData[i][1]); 21580 } 21581 else { 21582 compatEC3CommonStyles(mlData[i]); 21583 } 21584 } 21585 } 21586 21587 // Series 21588 if (seriesOpt.type === 'gauge') { 21589 compatTextStyle(seriesOpt, 'axisLabel'); 21590 compatTextStyle(seriesOpt, 'title'); 21591 compatTextStyle(seriesOpt, 'detail'); 21592 } 21593 else if (seriesOpt.type === 'treemap') { 21594 convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle'); 21595 each$1(seriesOpt.levels, function (opt) { 21596 removeEC3NormalStatus(opt); 21597 }); 21598 } 21599 else if (seriesOpt.type === 'tree') { 21600 removeEC3NormalStatus(seriesOpt.leaves); 21601 } 21602 // sunburst starts from ec4, so it does not need to compat levels. 21603 } 21604 21605 function toArr(o) { 21606 return isArray(o) ? o : o ? [o] : []; 21607 } 21608 21609 function toObj(o) { 21610 return (isArray(o) ? o[0] : o) || {}; 21611 } 21612 21613 var compatStyle = function (option, isTheme) { 21614 each$5(toArr(option.series), function (seriesOpt) { 21615 isObject$3(seriesOpt) && processSeries(seriesOpt); 21616 }); 21617 21618 var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar']; 21619 isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis'); 21620 21621 each$5( 21622 axes, 21623 function (axisName) { 21624 each$5(toArr(option[axisName]), function (axisOpt) { 21625 if (axisOpt) { 21626 compatTextStyle(axisOpt, 'axisLabel'); 21627 compatTextStyle(axisOpt.axisPointer, 'label'); 21628 } 21629 }); 21630 } 21631 ); 21632 21633 each$5(toArr(option.parallel), function (parallelOpt) { 21634 var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault; 21635 compatTextStyle(parallelAxisDefault, 'axisLabel'); 21636 compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label'); 21637 }); 21638 21639 each$5(toArr(option.calendar), function (calendarOpt) { 21640 convertNormalEmphasis(calendarOpt, 'itemStyle'); 21641 compatTextStyle(calendarOpt, 'dayLabel'); 21642 compatTextStyle(calendarOpt, 'monthLabel'); 21643 compatTextStyle(calendarOpt, 'yearLabel'); 21644 }); 21645 21646 // radar.name.textStyle 21647 each$5(toArr(option.radar), function (radarOpt) { 21648 compatTextStyle(radarOpt, 'name'); 21649 }); 21650 21651 each$5(toArr(option.geo), function (geoOpt) { 21652 if (isObject$3(geoOpt)) { 21653 compatEC3CommonStyles(geoOpt); 21654 each$5(toArr(geoOpt.regions), function (regionObj) { 21655 compatEC3CommonStyles(regionObj); 21656 }); 21657 } 21658 }); 21659 21660 each$5(toArr(option.timeline), function (timelineOpt) { 21661 compatEC3CommonStyles(timelineOpt); 21662 convertNormalEmphasis(timelineOpt, 'label'); 21663 convertNormalEmphasis(timelineOpt, 'itemStyle'); 21664 convertNormalEmphasis(timelineOpt, 'controlStyle', true); 21665 21666 var data = timelineOpt.data; 21667 isArray(data) && each$1(data, function (item) { 21668 if (isObject$1(item)) { 21669 convertNormalEmphasis(item, 'label'); 21670 convertNormalEmphasis(item, 'itemStyle'); 21671 } 21672 }); 21673 }); 21674 21675 each$5(toArr(option.toolbox), function (toolboxOpt) { 21676 convertNormalEmphasis(toolboxOpt, 'iconStyle'); 21677 each$5(toolboxOpt.feature, function (featureOpt) { 21678 convertNormalEmphasis(featureOpt, 'iconStyle'); 21679 }); 21680 }); 21681 21682 compatTextStyle(toObj(option.axisPointer), 'label'); 21683 compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); 21684 }; 21685 21686 /* 21687* Licensed to the Apache Software Foundation (ASF) under one 21688* or more contributor license agreements. See the NOTICE file 21689* distributed with this work for additional information 21690* regarding copyright ownership. The ASF licenses this file 21691* to you under the Apache License, Version 2.0 (the 21692* "License");
21692 you may not use this file except in compliance 21693* with the License. You may obtain a copy of the License at 21694* 21695* http://www.apache.org/licenses/LICENSE-2.0 21696* 21697* Unless required by applicable law or agreed to in writing, 21698* software distributed under the License is distributed on an 21699* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21700* KIND, either express or implied. See the License for the 21701* specific language governing permissions and limitations 21702* under the License. 21703*/ 21704 21705// Compatitable with 2.0 21706 21707 function get(opt, path) { 21708 path = path.split(','); 21709 var obj = opt; 21710 for (var i = 0; i < path.length; i++) { 21711 obj = obj && obj[path[i]]; 21712 if (obj == null) { 21713 break; 21714 } 21715 } 21716 return obj; 21717 } 21718 21719 function set$1(opt, path, val, overwrite) { 21720 path = path.split(','); 21721 var obj = opt; 21722 var key; 21723 for (var i = 0; i < path.length - 1; i++) { 21724 key = path[i]; 21725 if (obj[key] == null) { 21726 obj[key] = {}; 21727 } 21728 obj = obj[key]; 21729 } 21730 if (overwrite || obj[path[i]] == null) { 21731 obj[path[i]] = val; 21732 } 21733 } 21734 21735 function compatLayoutProperties(option) { 21736 each$1(LAYOUT_PROPERTIES, function (prop) { 21737 if (prop[0] in option && !(prop[1] in option)) { 21738 option[prop[1]] = option[prop[0]]; 21739 } 21740 }); 21741 } 21742 21743 var LAYOUT_PROPERTIES = [ 21744 ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom'] 21745 ]; 21746 21747 var COMPATITABLE_COMPONENTS = [ 21748 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline' 21749 ]; 21750 21751 var backwardCompat = function (option, isTheme) { 21752 compatStyle(option, isTheme); 21753 21754 // Make sure series array for model initialization. 21755 option.series = normalizeToArray(option.series); 21756 21757 each$1(option.series, function (seriesOpt) { 21758 if (!isObject$1(seriesOpt)) { 21759 return; 21760 } 21761 21762 var seriesType = seriesOpt.type; 21763 21764 if (seriesType === 'pie' || seriesType === 'gauge') { 21765 if (seriesOpt.clockWise != null) { 21766 seriesOpt.clockwise = seriesOpt.clockWise; 21767 } 21768 } 21769 if (seriesType === 'gauge') { 21770 var pointerColor = get(seriesOpt, 'pointer.color'); 21771 pointerColor != null 21772 && set$1(seriesOpt, 'itemStyle.normal.color', pointerColor); 21773 } 21774 21775 compatLayoutProperties(seriesOpt); 21776 }); 21777 21778 // dataRange has changed to visualMap 21779 if (option.dataRange) { 21780 option.visualMap = option.dataRange; 21781 } 21782 21783 each$1(COMPATITABLE_COMPONENTS, function (componentName) { 21784 var options = option[componentName]; 21785 if (options) { 21786 if (!isArray(options)) { 21787 options = [options]; 21788 } 21789 each$1(options, function (option) { 21790 compatLayoutProperties(option); 21791 }); 21792 } 21793 }); 21794 }; 21795 21796 /* 21797* Licensed to the Apache Software Foundation (ASF) under one 21798* or more contributor license agreements. See the NOTICE file 21799* distributed with this work for additional information 21800* regarding copyright ownership. The ASF licenses this file 21801* to you under the Apache License, Version 2.0 (the 21802* "License"); you may not use this file except in compliance 21803* with the License. You may obtain a copy of the License at 21804* 21805* http://www.apache.org/licenses/LICENSE-2.0 21806* 21807* Unless required by applicable law or agreed to in writing, 21808* software distributed under the License is distributed on an 21809* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21810* KIND, either express or implied. See the License for the 21811* specific language governing permissions and limitations 21812* under the License. 21813*/ 21814 21815// (1) [Caution]: the logic is correct based on the premises: 21816// data processing stage is blocked in stream. 21817// See <module:echarts/stream/Scheduler#performDataProcessorTasks> 21818// (2) Only register once when import repeatly. 21819// Should be executed before after series filtered and before stack calculation. 21820 var dataStack = function (ecModel) { 21821 var stackInfoMap = createHashMap(); 21822 ecModel.eachSeries(function (seriesModel) { 21823 var stack = seriesModel.get('stack'); 21824 // Compatibal: when `stack` is set as '', do not stack. 21825 if (stack) { 21826 var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []); 21827 var data = seriesModel.getData(); 21828 21829 var stackInfo = { 21830 // Used for calculate axis extent automatically. 21831 stackResultDimension: data.getCalculationInfo('stackResultDimension'), 21832 stackedOverDimension: data.getCalculationInfo('stackedOverDimension'), 21833 stackedDimension: data.getCalculationInfo('stackedDimension'), 21834 stackedByDimension: data.getCalculationInfo('stackedByDimension'), 21835 isStackedByIndex: data.getCalculationInfo('isStackedByIndex'), 21836 data: data, 21837 seriesModel: seriesModel 21838 }; 21839 21840 // If stacked on axis that do not support data stack. 21841 if (!stackInfo.stackedDimension 21842 || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension) 21843 ) { 21844 return; 21845 } 21846 21847 stackInfoList.length && data.setCalculationInfo( 21848 'stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel 21849 ); 21850 21851 stackInfoList.push(stackInfo); 21852 } 21853 }); 21854 21855 stackInfoMap.each(calculateStack); 21856 }; 21857 21858 function calculateStack(stackInfoList) { 21859 each$1(stackInfoList, function (targetStackInfo, idxInStack) { 21860 var resultVal = []; 21861 var resultNaN = [NaN, NaN]; 21862 var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension]; 21863 var targetData = targetStackInfo.data; 21864 var isStackedByIndex = targetStackInfo.isStackedByIndex; 21865 21866 // Should not write on raw data, because stack series model list changes 21867 // depending on legend selection. 21868 var newData = targetData.map(dims, function (v0, v1, dataIndex) { 21869 var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); 21870 21871 // Consider `connectNulls` of line area, if value is NaN, stackedOver 21872 // should also be NaN, to draw a appropriate belt area. 21873 if (isNaN(sum)) { 21874 return resultNaN; 21875 } 21876 21877 var byValue; 21878 var stackedDataRawIndex; 21879 21880 if (isStackedByIndex) { 21881 stackedDataRawIndex = targetData.getRawIndex(dataIndex); 21882 } 21883 else { 21884 byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex); 21885 } 21886 21887 // If stackOver is NaN, chart view will render point on value start. 21888 var stackedOver = NaN; 21889 21890 for (var j = idxInStack - 1; j >= 0; j--) { 21891 var stackInfo = stackInfoList[j]; 21892 21893 // Has been optimized by inverted indices on `stackedByDimension`. 21894 if (!isStackedByIndex) { 21895 stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue); 21896 } 21897 21898 if (stackedDataRawIndex >= 0) { 21899 var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); 21900 21901 // Considering positive stack, negative stack and empty data 21902 if ((sum >= 0 && val > 0) // Positive stack 21903 || (sum <= 0 && val < 0) // Negative stack 21904 ) { 21905 sum += val; 21906 stackedOver = val; 21907 break; 21908 } 21909 } 21910 } 21911 21912 resultVal[0] = sum; 21913 resultVal[1] = stackedOver; 21914 21915 return resultVal; 21916 }); 21917 21918 targetData.hostModel.setData(newData); 21919 // Update for consequent calculation 21920 targetStackInfo.data = newData; 21921 }); 21922 } 21923 21924 /* 21925* Licensed to the Apache Software Foundation (ASF) under one 21926* or more contributor license agreements. See the NOTICE file 21927* distributed with this work for additional information 21928* regarding copyright ownership. The ASF licenses this file 21929* to you under the Apache License, Version 2.0 (the 21930* "License");
21930 you may not use this file except in compliance 21931* with the License. You may obtain a copy of the License at 21932* 21933* http://www.apache.org/licenses/LICENSE-2.0 21934* 21935* Unless required by applicable law or agreed to in writing, 21936* software distributed under the License is distributed on an 21937* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21938* KIND, either express or implied. See the License for the 21939* specific language governing permissions and limitations 21940* under the License. 21941*/ 21942 21943// TODO 21944// ??? refactor? check the outer usage of data provider. 21945// merge with defaultDimValueGetter? 21946 21947 /** 21948 * If normal array used, mutable chunk size is supported. 21949 * If typed array used, chunk size must be fixed. 21950 */ 21951 function DefaultDataProvider(source, dimSize) { 21952 if (!Source.isInstance(source)) { 21953 source = Source.seriesDataToSource(source); 21954 } 21955 this._source = source; 21956 21957 var data = this._data = source.data; 21958 var sourceFormat = source.sourceFormat; 21959 21960 // Typed array. TODO IE10+? 21961 if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { 21962 if (__DEV__) { 21963 if (dimSize == null) { 21964 throw new Error('Typed array data must specify dimension size'); 21965 } 21966 } 21967 this._offset = 0; 21968 this._dimSize = dimSize; 21969 this._data = data; 21970 } 21971 21972 var methods = providerMethods[ 21973 sourceFormat === SOURCE_FORMAT_ARRAY_ROWS 21974 ? sourceFormat + '_' + source.seriesLayoutBy 21975 : sourceFormat 21976 ]; 21977 21978 if (__DEV__) { 21979 assert$1(methods, 'Invalide sourceFormat: ' + sourceFormat); 21980 } 21981 21982 extend(this, methods); 21983 } 21984 21985 var providerProto = DefaultDataProvider.prototype; 21986// If data is pure without style configuration 21987 providerProto.pure = false; 21988// If data is persistent and will not be released after use. 21989 providerProto.persistent = true; 21990 21991// ???! FIXME legacy data provider do not has method getSource 21992 providerProto.getSource = function () { 21993 return this._source; 21994 }; 21995 21996 var providerMethods = { 21997 21998 'arrayRows_column': { 21999 pure: true, 22000 count: function () { 22001 return Math.max(0, this._data.length - this._source.startIndex); 22002 }, 22003 getItem: function (idx) { 22004 return this._data[idx + this._source.startIndex]; 22005 }, 22006 appendData: appendDataSimply 22007 }, 22008 22009 'arrayRows_row': { 22010 pure: true, 22011 count: function () { 22012 var row = this._data[0]; 22013 return row ? Math.max(0, row.length - this._source.startIndex) : 0; 22014 }, 22015 getItem: function (idx) { 22016 idx += this._source.startIndex; 22017 var item = []; 22018 var data = this._data; 22019 for (var i = 0; i < data.length; i++) { 22020 var row = data[i]; 22021 item.push(row ? row[idx] : null); 22022 } 22023 return item; 22024 }, 22025 appendData: function () { 22026 throw new Error('Do not support appendData when set seriesLayoutBy: "row".'); 22027 } 22028 }, 22029 22030 'objectRows': { 22031 pure: true, 22032 count: countSimply, 22033 getItem: getItemSimply, 22034 appendData: appendDataSimply 22035 }, 22036 22037 'keyedColumns': { 22038 pure: true, 22039 count: function () { 22040 var dimName = this._source.dimensionsDefine[0].name; 22041 var col = this._data[dimName]; 22042 return col ? col.length : 0; 22043 }, 22044 getItem: function (idx) { 22045 var item = []; 22046 var dims = this._source.dimensionsDefine; 22047 for (var i = 0; i < dims.length; i++) { 22048 var col = this._data[dims[i].name]; 22049 item.push(col ? col[idx] : null); 22050 } 22051 return item; 22052 }, 22053 appendData: function (newData) { 22054 var data = this._data; 22055 each$1(newData, function (newCol, key) { 22056 var oldCol = data[key] || (data[key] = []); 22057 for (var i = 0; i < (newCol || []).length; i++) {
22058 oldCol.push(newCol[i]); 22059 } 22060 }); 22061 } 22062 }, 22063 22064 'original': { 22065 count: countSimply, 22066 getItem: getItemSimply, 22067 appendData: appendDataSimply 22068 }, 22069 22070 'typedArray': { 22071 persistent: false, 22072 pure: true, 22073 count: function () { 22074 return this._data ? (this._data.length / this._dimSize) : 0; 22075 }, 22076 getItem: function (idx, out) { 22077 idx = idx - this._offset; 22078 out = out || []; 22079 var offset = this._dimSize * idx; 22080 for (var i = 0; i < this._dimSize; i++) { 22081 out[i] = this._data[offset + i]; 22082 } 22083 return out; 22084 }, 22085 appendData: function (newData) { 22086 if (__DEV__) { 22087 assert$1( 22088 isTypedArray(newData), 22089 'Added data must be TypedArray if data in initialization is TypedArray' 22090 ); 22091 } 22092 22093 this._data = newData; 22094 }, 22095 22096 // Clean self if data is already used. 22097 clean: function () { 22098 // PENDING 22099 this._offset += this.count(); 22100 this._data = null; 22101 } 22102 } 22103 }; 22104 22105 function countSimply() { 22106 return this._data.length; 22107 } 22108 function getItemSimply(idx) { 22109 return this._data[idx]; 22110 } 22111 function appendDataSimply(newData) { 22112 for (var i = 0; i < newData.length; i++) { 22113 this._data.push(newData[i]); 22114 } 22115 } 22116 22117 22118 22119 var rawValueGetters = { 22120 22121 arrayRows: getRawValueSimply, 22122 22123 objectRows: function (dataItem, dataIndex, dimIndex, dimName) { 22124 return dimIndex != null ? dataItem[dimName] : dataItem; 22125 }, 22126 22127 keyedColumns: getRawValueSimply, 22128 22129 original: function (dataItem, dataIndex, dimIndex, dimName) { 22130 // FIXME 22131 // In some case (markpoint in geo (geo-map.html)), dataItem 22132 // is {coord: [...]} 22133 var value = getDataItemValue(dataItem); 22134 return (dimIndex == null || !(value instanceof Array)) 22135 ? value 22136 : value[dimIndex]; 22137 }, 22138 22139 typedArray: getRawValueSimply 22140 }; 22141 22142 function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) { 22143 return dimIndex != null ? dataItem[dimIndex] : dataItem; 22144 } 22145 22146 22147 var defaultDimValueGetters = { 22148 22149 arrayRows: getDimValueSimply, 22150 22151 objectRows: function (dataItem, dimName, dataIndex, dimIndex) { 22152 return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]); 22153 }, 22154 22155 keyedColumns: getDimValueSimply, 22156 22157 original: function (dataItem, dimName, dataIndex, dimIndex) { 22158 // Performance sensitive, do not use modelUtil.getDataItemValue. 22159 // If dataItem is an plain object with no value field, the var `value` 22160 // will be assigned with the object, but it will be tread correctly 22161 // in the `convertDataValue`. 22162 var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); 22163 22164 // If any dataItem is like { value: 10 } 22165 if (!this._rawData.pure && isDataItemOption(dataItem)) { 22166 this.hasItemOption = true; 22167 } 22168 return converDataValue( 22169 (value instanceof Array) 22170 ? value[dimIndex] 22171 // If value is a single number or something else not array. 22172 : value, 22173 this._dimensionInfos[dimName] 22174 ); 22175 }, 22176 22177 typedArray: function (dataItem, dimName, dataIndex, dimIndex) { 22178 return dataItem[dimIndex]; 22179 } 22180 22181 }; 22182 22183 function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) { 22184 return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]); 22185 } 22186 22187 /** 22188 * This helper method convert value in data. 22189 * @param {string|number|Date} value 22190 * @param {Object|string}
22190 [dimInfo] If string (like 'x'), dimType defaults 'number'. 22191 * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed. 22192 */ 22193 function converDataValue(value, dimInfo) { 22194 // Performance sensitive. 22195 var dimType = dimInfo && dimInfo.type; 22196 if (dimType === 'ordinal') { 22197 // If given value is a category string 22198 var ordinalMeta = dimInfo && dimInfo.ordinalMeta; 22199 return ordinalMeta 22200 ? ordinalMeta.parseAndCollect(value) 22201 : value; 22202 } 22203 22204 if (dimType === 'time' 22205 // spead up when using timestamp 22206 && typeof value !== 'number' 22207 && value != null 22208 && value !== '-' 22209 ) { 22210 value = +parseDate(value); 22211 } 22212 22213 // dimType defaults 'number'. 22214 // If dimType is not ordinal and value is null or undefined or NaN or '-', 22215 // parse to NaN. 22216 return (value == null || value === '') 22217 ? NaN 22218 // If string (like '-'), using '+' parse to NaN 22219 // If object, also parse to NaN 22220 : +value; 22221 } 22222 22223// ??? FIXME can these logic be more neat: getRawValue, getRawDataItem, 22224// Consider persistent. 22225// Caution: why use raw value to display on label or tooltip? 22226// A reason is to avoid format. For example time value we do not know 22227// how to format is expected. More over, if stack is used, calculated 22228// value may be 0.91000000001, which have brings trouble to display. 22229// TODO: consider how to treat null/undefined/NaN when display? 22230 /** 22231 * @param {module:echarts/data/List} data 22232 * @param {number} dataIndex 22233 * @param {string|number} [dim] dimName or dimIndex 22234 * @return {Array.<number>|string|number} can be null/undefined. 22235 */ 22236 function retrieveRawValue(data, dataIndex, dim) { 22237 if (!data) { 22238 return; 22239 } 22240 22241 // Consider data may be not persistent. 22242 var dataItem = data.getRawDataItem(dataIndex); 22243 22244 if (dataItem == null) { 22245 return; 22246 } 22247 22248 var sourceFormat = data.getProvider().getSource().sourceFormat; 22249 var dimName; 22250 var dimIndex; 22251 22252 var dimInfo = data.getDimensionInfo(dim); 22253 if (dimInfo) { 22254 dimName = dimInfo.name; 22255 dimIndex = dimInfo.index; 22256 } 22257 22258 return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName); 22259 } 22260 22261 /** 22262 * Compatible with some cases (in pie, map) like: 22263 * data: [{name: 'xx', value: 5, selected: true}, ...] 22264 * where only sourceFormat is 'original' and 'objectRows' supported. 22265 * 22266 * ??? TODO 22267 * Supported detail options in data item when using 'arrayRows'. 22268 * 22269 * @param {module:echarts/data/List} data 22270 * @param {number} dataIndex 22271 * @param {string} attr like 'selected' 22272 */ 22273 function retrieveRawAttr(data, dataIndex, attr) { 22274 if (!data) { 22275 return; 22276 } 22277 22278 var sourceFormat = data.getProvider().getSource().sourceFormat; 22279 22280 if (sourceFormat !== SOURCE_FORMAT_ORIGINAL 22281 && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS 22282 ) { 22283 return; 22284 } 22285 22286 var dataItem = data.getRawDataItem(dataIndex); 22287 if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject$1(dataItem)) { 22288 dataItem = null; 22289 } 22290 if (dataItem) { 22291 return dataItem[attr]; 22292 } 22293 } 22294 22295 /* 22296* Licensed to the Apache Software Foundation (ASF) under one 22297* or more contributor license agreements. See the NOTICE file 22298* distributed with this work for additional information 22299* regarding copyright ownership. The ASF licenses this file 22300* to you under the Apache License, Version 2.0 (the 22301* "License"); you may not use this file except in compliance 22302* with the License. You may obtain a copy of the License at 22303* 22304* http://www.apache.org/licenses/LICENSE-2.0 22305* 22306* Unless required by applicable law or agreed to in writing, 22307* software distributed under the License is distributed on an 22308* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22309* KIND, either express or implied. See the License for the 22310* specific language governing permissions and limitations 22311* under the License. 22312*/ 22313 22314 var DIMENSION_LABEL_REG = /\{@(.+?)\}/g; 22315 22316// PENDING A little ugly 22317 var dataFormatMixin = { 22318 /** 22319 * Get params for formatter 22320 * @param {number} dataIndex 22321 * @param {string} [dataType] 22322 * @return {Object} 22323 */ 22324 getDataParams: function (dataIndex, dataType) { 22325 var data = this.getData(dataType); 22326 var rawValue = this.getRawValue(dataIndex, dataType); 22327 var rawDataIndex = data.getRawIndex(dataIndex); 22328 var name = data.getName(dataIndex); 22329 var itemOpt = data.getRawDataItem(dataIndex); 22330 var color = data.getItemVisual(dataIndex, 'color'); 22331 22332 return { 22333 componentType: this.mainType, 22334 componentSubType: this.subType, 22335 seriesType: this.mainType === 'series' ? this.subType : null, 22336 seriesIndex: this.seriesIndex, 22337 seriesId: this.id, 22338 seriesName: this.name, 22339 name: name, 22340 dataIndex: rawDataIndex, 22341 data: itemOpt, 22342 dataType: dataType, 22343 value: rawValue, 22344 color: color, 22345 marker: getTooltipMarker(color), 22346 22347 // Param name list for mapping `a`, `b`, `c`, `d`, `e` 22348 $vars: ['seriesName', 'name', 'value'] 22349 }; 22350 }, 22351 22352 /** 22353 * Format label 22354 * @param {number} dataIndex 22355 * @param {string}
22355 [status='normal'] 'normal' or 'emphasis' 22356 * @param {string} [dataType] 22357 * @param {number} [dimIndex] 22358 * @param {string} [labelProp='label'] 22359 * @return {string} If not formatter, return null/undefined 22360 */ 22361 getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) { 22362 status = status || 'normal'; 22363 var data = this.getData(dataType); 22364 var itemModel = data.getItemModel(dataIndex); 22365 22366 var params = this.getDataParams(dataIndex, dataType); 22367 if (dimIndex != null && (params.value instanceof Array)) { 22368 params.value = params.value[dimIndex]; 22369 } 22370 22371 var formatter = itemModel.get( 22372 status === 'normal' 22373 ? [labelProp || 'label', 'formatter'] 22374 : [status, labelProp || 'label', 'formatter'] 22375 ); 22376 22377 if (typeof formatter === 'function') { 22378 params.status = status; 22379 return formatter(params); 22380 } 22381 else if (typeof formatter === 'string') { 22382 var str = formatTpl(formatter, params); 22383 22384 // Support 'aaa{@[3]}bbb{@product}ccc'. 22385 // Do not support '}' in dim name util have to. 22386 return str.replace(DIMENSION_LABEL_REG, function (origin, dim) { 22387 var len = dim.length; 22388 if (dim.charAt(0) === '[' && dim.charAt(len - 1) === ']') { 22389 dim = +dim.slice(1, len - 1); // Also: '[]' => 0 22390 } 22391 return retrieveRawValue(data, dataIndex, dim); 22392 }); 22393 } 22394 }, 22395 22396 /** 22397 * Get raw value in option 22398 * @param {number} idx 22399 * @param {string} [dataType] 22400 * @return {Array|number|string} 22401 */ 22402 getRawValue: function (idx, dataType) { 22403 return retrieveRawValue(this.getData(dataType), idx); 22404 }, 22405 22406 /** 22407 * Should be implemented. 22408 * @param {number} dataIndex 22409 * @param {boolean} [multipleSeries=false] 22410 * @param {number} [dataType] 22411 * @return {string} tooltip string 22412 */ 22413 formatTooltip: function () { 22414 // Empty function 22415 } 22416 }; 22417 22418 /* 22419* Licensed to the Apache Software Foundation (ASF) under one 22420* or more contributor license agreements. See the NOTICE file 22421* distributed with this work for additional information 22422* regarding copyright ownership. The ASF licenses this file 22423* to you under the Apache License, Version 2.0 (the 22424* "License"); you may not use this file except in compliance 22425* with the License. You may obtain a copy of the License at 22426* 22427* http://www.apache.org/licenses/LICENSE-2.0 22428* 22429* Unless required by applicable law or agreed to in writing, 22430* software distributed under the License is distributed on an 22431* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22432* KIND, either express or implied. See the License for the 22433* specific language governing permissions and limitations 22434* under the License. 22435*/ 22436 22437 /** 22438 * @param {Object} define 22439 * @return See the return of `createTask`. 22440 */ 22441 function createTask(define) { 22442 return new Task(define); 22443 } 22444 22445 /** 22446 * @constructor 22447 * @param {Object} define 22448 * @param {Function} define.reset Custom reset 22449 * @param {Function} [define.plan] Returns 'reset' indicate reset immediately. 22450 * @param {Function} [define.count] count is used to determin data task. 22451 * @param {Function} [define.onDirty] count is used to determin data task. 22452 */ 22453 function Task(define) { 22454 define = define || {}; 22455 22456 this._reset = define.reset; 22457 this._plan = define.plan; 22458 this._count = define.count; 22459 this._onDirty = define.onDirty; 22460 22461 this._dirty = true; 22462 22463 // Context must be specified implicitly, to 22464 // avoid miss update context when model changed. 22465 this.context; 22466 } 22467 22468 var taskProto = Task.prototype; 22469 22470 /** 22471 * @param {Object} performArgs 22472 * @param {number} [performArgs.step] Specified step. 22473 * @param {number} [performArgs.skip] Skip customer perform call. 22474 * @param {number} [performArgs.modBy] Sampling window size. 22475 * @param {number} [performArgs.modDataCount] Sampling count. 22476 */ 22477 taskProto.perform = function (performArgs) { 22478 var upTask = this._upstream; 22479 var skip = performArgs && performArgs.skip; 22480 22481 // TODO some refactor. 22482 // Pull data. Must pull data each time, because context.data 22483 // may be updated by Series.setData. 22484 if (this._dirty && upTask) { 22485 var context = this.context; 22486 context.data = context.outputData = upTask.context.outputData; 22487 } 22488 22489 if (this.__pipeline) { 22490 this.__pipeline.currentTask = this; 22491 } 22492 22493 var planResult; 22494 if (this._plan && !skip) { 22495 planResult = this._plan(this.context); 22496 } 22497
22498 // Support sharding by mod, which changes the render sequence and makes the rendered graphic 22499 // elements uniformed distributed when progress, especially when moving or zooming. 22500 var lastModBy = normalizeModBy(this._modBy); 22501 var lastModDataCount = this._modDataCount || 0; 22502 var modBy = normalizeModBy(performArgs && performArgs.modBy); 22503 var modDataCount = performArgs && performArgs.modDataCount || 0; 22504 if (lastModBy !== modBy || lastModDataCount !== modDataCount) { 22505 planResult = 'reset'; 22506 } 22507 22508 function normalizeModBy(val) { 22509 !(val >= 1) && (val = 1); // jshint ignore:line 22510 return val; 22511 } 22512 22513 var forceFirstProgress; 22514 if (this._dirty || planResult === 'reset') { 22515 this._dirty = false; 22516 forceFirstProgress = reset(this, skip); 22517 } 22518 22519 this._modBy = modBy; 22520 this._modDataCount = modDataCount; 22521 22522 var step = performArgs && performArgs.step; 22523 22524 if (upTask) { 22525 22526 if (__DEV__) { 22527 assert$1(upTask._outputDueEnd != null); 22528 } 22529 this._dueEnd = upTask._outputDueEnd; 22530 } 22531 // DataTask or overallTask 22532 else { 22533 if (__DEV__) { 22534 assert$1(!this._progress || this._count); 22535 } 22536 this._dueEnd = this._count ? this._count(this.context) : Infinity; 22537 } 22538 22539 // Note: Stubs, that its host overall task let it has progress, has progress. 22540 // If no progress, pass index from upstream to downstream each time plan called. 22541 if (this._progress) { 22542 var start = this._dueIndex; 22543 var end = Math.min( 22544 step != null ? this._dueIndex + step : Infinity, 22545 this._dueEnd 22546 ); 22547 22548 if (!skip && (forceFirstProgress || start < end)) { 22549 var progress = this._progress; 22550 if (isArray(progress)) { 22551 for (var i = 0; i < progress.length; i++) { 22552 doProgress(this, progress[i], start, end, modBy, modDataCount); 22553 } 22554 } 22555 else { 22556 doProgress(this, progress, start, end, modBy, modDataCount); 22557 } 22558 } 22559 22560 this._dueIndex = end; 22561 // If no `outputDueEnd`, assume that output data and 22562 // input data is the same, so use `dueIndex` as `outputDueEnd`. 22563 var outputDueEnd = this._settedOutputEnd != null 22564 ? this._settedOutputEnd : end; 22565 22566 if (__DEV__) { 22567 // ??? Can not rollback. 22568 assert$1(outputDueEnd >= this._outputDueEnd); 22569 } 22570 22571 this._outputDueEnd = outputDueEnd; 22572 } 22573 else { 22574 // (1) Some overall task has no progress. 22575 // (2) Stubs, that its host overall task do not let it has progress, has no progress. 22576 // This should always be performed so it can be passed to downstream. 22577 this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null 22578 ? this._settedOutputEnd : this._dueEnd; 22579 } 22580 22581 return this.unfinished(); 22582 }; 22583 22584 var iterator = (function () { 22585 22586 var end; 22587 var current; 22588 var modBy; 22589 var modDataCount; 22590 var winCount; 22591 22592 var it = { 22593 reset: function (s, e, sStep, sCount) { 22594 current = s; 22595 end = e; 22596 22597 modBy = sStep; 22598 modDataCount = sCount; 22599 winCount = Math.ceil(modDataCount / modBy); 22600 22601 it.next = (modBy > 1 && modDataCount > 0) ? modNext : sequentialNext; 22602 } 22603 }; 22604 22605 return it; 22606 22607 function sequentialNext() { 22608 return current < end ? current++ : null; 22609 } 22610 22611 function modNext() { 22612 var dataIndex = (current % winCount) * modBy + Math.ceil(current / winCount); 22613 var result = current >= end 22614 ? null 22615 : dataIndex < modDataCount 22616 ? dataIndex 22617 // If modDataCount is smaller than data.count() (consider `appendData` case), 22618 // Use normal linear rendering mode. 22619 : current; 22620 current++; 22621 return result; 22622 } 22623 })(); 22624 22625 taskProto.dirty = function () { 22626 this._dirty = true; 22627 this._onDirty && this._onDirty(this.context); 22628 }; 22629 22630 function doProgress(taskIns, progress, start, end, modBy, modDataCount) { 22631 iterator.reset(start, end, modBy, modDataCount); 22632 taskIns._callingProgress = progress; 22633 taskIns._callingProgress({ 22634 start: start, end: end, count: end - start, next: iterator.next 22635 }, taskIns.context); 22636 } 22637 22638 function reset(taskIns, skip) { 22639 taskIns._dueIndex = taskIns._outputDueEnd = taskIns._dueEnd = 0; 22640 taskIns._settedOutputEnd = null; 22641 22642 var progress; 22643 var forceFirstProgress; 22644 22645 if (!skip && taskIns._reset) { 22646 progress = taskIns._reset(taskIns.context); 22647 if (progress && progress.progress) { 22648 forceFirstProgress = progress.forceFirstProgress; 22649 progress = progress.progress; 22650 } 22651 // To simplify no progress checking, array must has item. 22652 if (isArray(progress) && !progress.length) { 22653 progress = null; 22654 } 22655 } 22656 22657 taskIns._progress = progress; 22658 taskIns._modBy = taskIns._modDataCount = null; 22659 22660 var downstream = taskIns._downstream; 22661 downstream && downstream.dirty(); 22662 22663 return forceFirstProgress; 22664 } 22665 22666 /** 22667 * @return {boolean} 22668 */ 22669 taskProto.unfinished = function () { 22670 return this._progress && this._dueIndex < this._dueEnd; 22671 }; 22672 22673 /** 22674 * @param {Object} downTask The downstream task. 22675 * @return {Object} The downstream task. 22676 */ 22677 taskProto.pipe = function (downTask) { 22678 if (__DEV__) { 22679 assert$1(downTask && !downTask._disposed && downTask !== this); 22680 } 22681 22682 // If already downstream, do not dirty downTask. 22683 if (this._downstream !== downTask || this._dirty) { 22684 this._downstream = downTask; 22685 downTask._upstream = this; 22686 downTask.dirty(); 22687 } 22688 }; 22689 22690 taskProto.dispose = function () { 22691 if (this._disposed) { 22692 return; 22693 } 22694 22695 this._upstream && (this._upstream._downstream = null); 22696 this._downstream && (this._downstream._upstream = null); 22697 22698 this._dirty = false; 22699 this._disposed = true; 22700 }; 22701 22702 taskProto.getUpstream = function () { 22703 return this._upstream; 22704 }; 22705 22706 taskProto.getDownstream = function () { 22707 return this._downstream; 22708 }; 22709 22710 taskProto.setOutputEnd = function (end) { 22711 // This only happend in dataTask, dataZoom, map, currently. 22712 // where dataZoom do not set end each time, but only set 22713 // when reset. So we should record the setted end, in case 22714 // that the stub of dataZoom perform again and earse the 22715 // setted end by upstream. 22716 this._outputDueEnd = this._settedOutputEnd = end; 22717 }; 22718 22719 22720/////////////////////////////////////////////////////////// 22721// For stream debug (Should be commented out after used!) 22722// Usage: printTask(this, 'begin'); 22723// Usage: printTask(this, null, {someExtraProp}); 22724// function printTask(task, prefix, extra) { 22725// window.ecTaskUID == null && (window.ecTaskUID = 0); 22726// task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`); 22727// task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`); 22728// var props = []; 22729// if (task.__pipeline) { 22730// var val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`; 22731// props.push({text: 'idx', value: val}); 22732// } else { 22733// var stubCount = 0; 22734// task.agentStubMap.each(() => stubCount++); 22735// props.push({text: 'idx', value: `overall (stubs: ${stubCount})`}); 22736// } 22737// props.push({text: 'uid', value: task.uidDebug});
22738// if (task.__pipeline) { 22739// props.push({text: 'pid', value: task.__pipeline.id}); 22740// task.agent && props.push( 22741// {text: 'stubFor', value: task.agent.uidDebug} 22742// ); 22743// } 22744// props.push( 22745// {text: 'dirty', value: task._dirty}, 22746// {text: 'dueIndex', value: task._dueIndex}, 22747// {text: 'dueEnd', value: task._dueEnd}, 22748// {text: 'outputDueEnd', value: task._outputDueEnd} 22749// ); 22750// if (extra) { 22751// Object.keys(extra).forEach(key => { 22752// props.push({text: key, value: extra[key]}); 22753// }); 22754// } 22755// var args = ['color: blue']; 22756// var msg = `%c[${prefix || 'T'}] %c` + props.map(item => ( 22757// args.push('color: black', 'color: red'), 22758// `${item.text}: %c${item.value}` 22759// )).join('%c, '); 22760// console.log.apply(console, [msg].concat(args)); 22761// // console.log(this); 22762// } 22763 22764 /* 22765* Licensed to the Apache Software Foundation (ASF) under one 22766* or more contributor license agreements. See the NOTICE file 22767* distributed with this work for additional information 22768* regarding copyright ownership. The ASF licenses this file 22769* to you under the Apache License, Version 2.0 (the 22770* "License"); you may not use this file except in compliance 22771* with the License. You may obtain a copy of the License at 22772* 22773* http://www.apache.org/licenses/LICENSE-2.0 22774* 22775* Unless required by applicable law or agreed to in writing, 22776* software distributed under the License is distributed on an 22777* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22778* KIND, either express or implied. See the License for the 22779* specific language governing permissions and limitations 22780* under the License. 22781*/ 22782 22783 var inner$4 = makeInner(); 22784 22785 var SeriesModel = ComponentModel.extend({ 22786 22787 type: 'series.__base__', 22788 22789 /** 22790 * @readOnly 22791 */ 22792 seriesIndex: 0, 22793 22794 // coodinateSystem will be injected in the echarts/CoordinateSystem 22795 coordinateSystem: null, 22796 22797 /** 22798 * @type {Object} 22799 * @protected 22800 */ 22801 defaultOption: null, 22802 22803 /** 22804 * Data provided for legend 22805 * @type {Function} 22806 */ 22807 // PENDING 22808 legendDataProvider: null, 22809 22810 /** 22811 * Access path of color for visual 22812 */ 22813 visualColorAccessPath: 'itemStyle.color', 22814 22815 /** 22816 * Support merge layout params. 22817 * Only support 'box' now (left/right/top/bottom/width/height). 22818 * @type {string|Object} Object can be {ignoreSize: true} 22819 * @readOnly 22820 */ 22821 layoutMode: null, 22822 22823 init: function (option, parentModel, ecModel, extraOpt) { 22824 22825 /** 22826 * @type {number} 22827 * @readOnly 22828 */ 22829 this.seriesIndex = this.componentIndex; 22830 22831 this.dataTask = createTask({ 22832 count: dataTaskCount, 22833 reset: dataTaskReset 22834 }); 22835 this.dataTask.context = {model: this}; 22836 22837 this.mergeDefaultAndTheme(option, ecModel); 22838 22839 prepareSource(this); 22840 22841 22842 var data = this.getInitialData(option, ecModel); 22843 wrapData(data, this); 22844 this.dataTask.context.data = data; 22845 22846 if (__DEV__) { 22847 assert$1(data, 'getInitialData returned invalid data.'); 22848 } 22849 22850 /** 22851 * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph} 22852 * @private 22853 */ 22854 inner$4(this).dataBeforeProcessed = data; 22855 22856 // If we reverse the order (make data firstly, and then make 22857 // dataBeforeProcessed by cloneShallow), cloneShallow will 22858 // cause data.graph.data !== data when using 22859 // module:echarts/data/Graph or module:echarts/data/Tree. 22860 // See module:echarts/data/helper/linkList 22861 22862 // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model 22863 // init or merge stage, because the data can be restored. So we do not `restoreData` 22864 // and `setData` here, which forbids calling `seriesModel.getData()` in this stage. 22865 // Call `seriesModel.getRawData()` instead. 22866 // this.restoreData(); 22867 22868 autoSeriesName(this); 22869 }, 22870 22871 /** 22872 * Util for merge default and theme to option 22873 * @param {Object} option 22874 * @param {module:echarts/model/Global} ecModel 22875 */ 22876 mergeDefaultAndTheme: function (option, ecModel) { 22877 var layoutMode = this.layoutMode; 22878 var inputPositionParams = layoutMode 22879 ? getLayoutParams(option) : {}; 22880 22881 // Backward compat: using subType on theme. 22882 // But if name duplicate between series subType 22883 // (for example: parallel) add component mainType, 22884 // add suffix 'Series'. 22885 var themeSubType = this.subType; 22886 if (ComponentModel.hasClass(themeSubType)) { 22887 themeSubType += 'Series'; 22888 } 22889 merge( 22890 option, 22891 ecModel.getTheme().get(this.subType) 22892 ); 22893 merge(option, this.getDefaultOption()); 22894 22895 // Default label emphasis `show` 22896 defaultEmphasis(option, 'label', ['show']); 22897 22898 this.fillDataTextStyle(option.data); 22899 22900 if (layoutMode) { 22901 mergeLayoutParam(option, inputPositionParams, layoutMode); 22902 } 22903 }, 22904 22905 mergeOption: function (newSeriesOption, ecModel) { 22906 // this.settingTask.dirty(); 22907 22908 newSeriesOption = merge(this.option, newSeriesOption, true); 22909 this.fillDataTextStyle(newSeriesOption.data); 22910 22911 var layoutMode = this.layoutMode; 22912 if (layoutMode) { 22913 mergeLayoutParam(this.option, newSeriesOption, layoutMode); 22914 } 22915 22916 prepareSource(this); 22917 22918 var data = this.getInitialData(newSeriesOption, ecModel); 22919 wrapData(data, this); 22920 this.dataTask.dirty(); 22921 this.dataTask.context.data = data; 22922 22923 inner$4(this).dataBeforeProcessed = data; 22924 22925 autoSeriesName(this); 22926 }, 22927 22928 fillDataTextStyle: function (data) { 22929 // Default data label emphasis `show` 22930 // FIXME Tree structure data ? 22931 // FIXME Performance ? 22932 if (data && !isTypedArray(data)) { 22933 var props = ['show']; 22934 for (var i = 0; i < data.length; i++) { 22935 if (data[i] && data[i].label) { 22936 defaultEmphasis(data[i], 'label', props); 22937 } 22938 } 22939 } 22940 }, 22941 22942 /** 22943 * Init a data structure from data related option in series 22944 * Must be overwritten 22945 */ 22946 getInitialData: function () {}, 22947 22948 /** 22949 * Append data to list 22950 * @param {Object} params 22951 * @param {Array|TypedArray} params.data 22952 */ 22953 appendData: function (params) { 22954 // FIXME ??? 22955 // (1) If data from dataset, forbidden append. 22956 // (2) support append data of dataset. 22957 var data = this.getRawData(); 22958 data.appendData(params.data); 22959 }, 22960 22961 /** 22962 * Consider some method like `filter`, `map` need make new data, 22963 * We should make sure that `seriesModel.getData()` get correct 22964 * data in the stream procedure. So we fetch data from upstream 22965 * each time `task.perform` called. 22966 * @param {string} [dataType]
22967 * @return {module:echarts/data/List} 22968 */ 22969 getData: function (dataType) { 22970 var task = getCurrentTask(this); 22971 if (task) { 22972 var data = task.context.data; 22973 return dataType == null ? data : data.getLinkedData(dataType); 22974 } 22975 else { 22976 // When series is not alive (that may happen when click toolbox 22977 // restore or setOption with not merge mode), series data may 22978 // be still need to judge animation or something when graphic 22979 // elements want to know whether fade out. 22980 return inner$4(this).data; 22981 } 22982 }, 22983 22984 /** 22985 * @param {module:echarts/data/List} data 22986 */ 22987 setData: function (data) { 22988 var task = getCurrentTask(this); 22989 if (task) { 22990 var context = task.context; 22991 // Consider case: filter, data sample. 22992 if (context.data !== data && task.modifyOutputEnd) { 22993 task.setOutputEnd(data.count()); 22994 } 22995 context.outputData = data; 22996 // Caution: setData should update context.data, 22997 // Because getData may be called multiply in a 22998 // single stage and expect to get the data just 22999 // set. (For example, AxisProxy, x y both call 23000 // getData and setDate sequentially). 23001 // So the context.data should be fetched from 23002 // upstream each time when a stage starts to be 23003 // performed. 23004 if (task !== this.dataTask) { 23005 context.data = data; 23006 } 23007 } 23008 inner$4(this).data = data; 23009 }, 23010 23011 /** 23012 * @see {module:echarts/data/helper/sourceHelper#getSource} 23013 * @return {module:echarts/data/Source} source 23014 */ 23015 getSource: function () { 23016 return getSource(this); 23017 }, 23018 23019 /** 23020 * Get data before processed 23021 * @return {module:echarts/data/List} 23022 */ 23023 getRawData: function () { 23024 return inner$4(this).dataBeforeProcessed; 23025 }, 23026 23027 /** 23028 * Get base axis if has coordinate system and has axis. 23029 * By default use coordSys.getBaseAxis(); 23030 * Can be overrided for some chart. 23031 * @return {type} description 23032 */ 23033 getBaseAxis: function () { 23034 var coordSys = this.coordinateSystem; 23035 return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); 23036 }, 23037 23038 // FIXME 23039 /** 23040 * Default tooltip formatter 23041 * 23042 * @param {number} dataIndex 23043 * @param {boolean} [multipleSeries=false] 23044 * @param {number} [dataType] 23045 */ 23046 formatTooltip: function (dataIndex, multipleSeries, dataType) { 23047 23048 function formatArrayValue(value) { 23049 // ??? TODO refactor these logic. 23050 // check: category-no-encode-has-axis-data in dataset.html 23051 var vertially = reduce(value, function (vertially, val, idx) { 23052 var dimItem = data.getDimensionInfo(idx); 23053 return vertially |= dimItem && dimItem.tooltip !== false && dimItem.displayName != null; 23054 }, 0); 23055 23056 var result = []; 23057 23058 tooltipDims.length 23059 ? each$1(tooltipDims, function (dim) { 23060 setEachItem(retrieveRawValue(data, dataIndex, dim), dim); 23061 }) 23062 // By default, all dims is used on tooltip. 23063 : each$1(value, setEachItem); 23064 23065 function setEachItem(val, dim) { 23066 var dimInfo = data.getDimensionInfo(dim); 23067 // If `dimInfo.tooltip` is not set, show tooltip. 23068 if (!dimInfo || dimInfo.otherDims.tooltip === false) { 23069 return; 23070 } 23071 var dimType = dimInfo.type; 23072 var dimHead = getTooltipMarker({color: color, type: 'subItem'}); 23073 var valStr = (vertially 23074 ? dimHead + encodeHTML(dimInfo.displayName || '-') + ': ' 23075 : '' 23076 ) 23077 // FIXME should not format time for raw data? 23078 + encodeHTML(dimType === 'ordinal' 23079 ? val + '' 23080 : dimType === 'time' 23081 ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val)) 23082 : addCommas(val) 23083 ); 23084 valStr && result.push(valStr); 23085 } 23086 23087 return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', '); 23088 } 23089 23090 function formatSingleValue(val) { 23091 return encodeHTML(addCommas(val)); 23092 } 23093 23094 var data = this.getData(); 23095 var tooltipDims = data.mapDimension('defaultedTooltip', true); 23096 var tooltipDimLen = tooltipDims.length; 23097 var value = this.getRawValue(dataIndex); 23098 var isValueArr = isArray(value); 23099 23100 var color = data.getItemVisual(dataIndex, 'color'); 23101 if (isObject$1(color) && color.colorStops) { 23102 color = (color.colorStops[0] || {}).color; 23103 } 23104 color = color || 'transparent'; 23105 23106 // Complicated rule for pretty tooltip. 23107 var formattedValue = (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen)) 23108 ? formatArrayValue(value) 23109 : tooltipDimLen 23110 ? formatSingleValue(retrieveRawValue(data, dataIndex, tooltipDims[0])) 23111 : formatSingleValue(isValueArr ? value[0] : value); 23112 23113 var colorEl = getTooltipMarker(color); 23114 23115 var name = data.getName(dataIndex); 23116 23117 var seriesName = this.name; 23118 if (!isNameSpecified(this)) { 23119 seriesName = ''; 23120 } 23121 seriesName = seriesName 23122 ? encodeHTML(seriesName) + (!multipleSeries ? '<br/>' : ': ') 23123 : ''; 23124 23125 return !multipleSeries 23126 ? seriesName + colorEl 23127 + (name 23128 ? encodeHTML(name) + ': ' + formattedValue 23129 : formattedValue 23130 ) 23131 : colorEl + seriesName + formattedValue; 23132 }, 23133 23134 /** 23135 * @return {boolean} 23136 */ 23137 isAnimationEnabled: function () { 23138 if (env$1.node) { 23139 return false; 23140 } 23141
23142 var animationEnabled = this.getShallow('animation'); 23143 if (animationEnabled) { 23144 if (this.getData().count() > this.getShallow('animationThreshold')) { 23145 animationEnabled = false; 23146 } 23147 } 23148 return animationEnabled; 23149 }, 23150 23151 restoreData: function () { 23152 this.dataTask.dirty(); 23153 }, 23154 23155 getColorFromPalette: function (name, scope, requestColorNum) { 23156 var ecModel = this.ecModel; 23157 // PENDING 23158 var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope, requestColorNum); 23159 if (!color) { 23160 color = ecModel.getColorFromPalette(name, scope, requestColorNum); 23161 } 23162 return color; 23163 }, 23164 23165 /** 23166 * Use `data.mapDimension(coordDim, true)` instead. 23167 * @deprecated 23168 */ 23169 coordDimToDataDim: function (coordDim) { 23170 return this.getRawData().mapDimension(coordDim, true); 23171 }, 23172 23173 /** 23174 * Get progressive rendering count each step 23175 * @return {number} 23176 */ 23177 getProgressive: function () { 23178 return this.get('progressive'); 23179 }, 23180 23181 /** 23182 * Get progressive rendering count each step 23183 * @return {number} 23184 */ 23185 getProgressiveThreshold: function () { 23186 return this.get('progressiveThreshold'); 23187 }, 23188 23189 /** 23190 * Get data indices for show tooltip content. See tooltip. 23191 * @abstract 23192 * @param {Array.<string>|string} dim 23193 * @param {Array.<number>} value 23194 * @param {module:echarts/coord/single/SingleAxis} baseAxis 23195 * @return {Object} {dataIndices, nestestValue}. 23196 */ 23197 getAxisTooltipData: null, 23198 23199 /** 23200 * See tooltip. 23201 * @abstract 23202 * @param {number} dataIndex 23203 * @return {Array.<number>} Point of tooltip. null/undefined can be returned. 23204 */ 23205 getTooltipPosition: null, 23206 23207 /** 23208 * @see {module:echarts/stream/Scheduler} 23209 */ 23210 pipeTask: null, 23211 23212 /** 23213 * Convinient for override in extended class. 23214 * @protected 23215 * @type {Function} 23216 */ 23217 preventIncremental: null, 23218 23219 /** 23220 * @public 23221 * @readOnly 23222 * @type {Object} 23223 */ 23224 pipelineContext: null 23225 23226 }); 23227 23228 23229 mixin(SeriesModel, dataFormatMixin); 23230 mixin(SeriesModel, colorPaletteMixin); 23231 23232 /** 23233 * MUST be called after `prepareSource` called 23234 * Here we need to make auto series, especially for auto legend. But we 23235 * do not modify series.name in option to avoid side effects. 23236 */ 23237 function autoSeriesName(seriesModel) { 23238 // User specified name has higher priority, otherwise it may cause 23239 // series can not be queried unexpectedly. 23240 var name = seriesModel.name; 23241 if (!isNameSpecified(seriesModel)) { 23242 seriesModel.name = getSeriesAutoName(seriesModel) || name; 23243 } 23244 } 23245 23246 function getSeriesAutoName(seriesModel) { 23247 var data = seriesModel.getRawData(); 23248 var dataDims = data.mapDimension('seriesName', true); 23249 var nameArr = []; 23250 each$1(dataDims, function (dataDim) { 23251 var dimInfo = data.getDimensionInfo(dataDim); 23252 dimInfo.displayName && nameArr.push(dimInfo.displayName); 23253 }); 23254 return nameArr.join(' '); 23255 } 23256 23257 function dataTaskCount(context) { 23258 return context.model.getRawData().count(); 23259 } 23260 23261 function dataTaskReset(context) { 23262 var seriesModel = context.model; 23263 seriesModel.setData(seriesModel.getRawData().cloneShallow()); 23264 return dataTaskProgress; 23265 } 23266 23267 function dataTaskProgress(param, context) { 23268 // Avoid repead cloneShallow when data just created in reset. 23269 if (param.end > context.outputData.count()) { 23270 context.model.getRawData().cloneShallow(context.outputData); 23271 } 23272 } 23273 23274// TODO refactor 23275 function wrapData(data, seriesModel) { 23276 each$1(data.CHANGABLE_METHODS, function (methodName) { 23277 data.wrapMethod(methodName, curry(onDataSelfChange, seriesModel)); 23278 }); 23279 } 23280 23281 function onDataSelfChange(seriesModel) {
23282 var task = getCurrentTask(seriesModel); 23283 if (task) { 23284 // Consider case: filter, selectRange 23285 task.setOutputEnd(this.count()); 23286 } 23287 } 23288 23289 function getCurrentTask(seriesModel) { 23290 var scheduler = (seriesModel.ecModel || {}).scheduler; 23291 var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid); 23292 23293 if (pipeline) { 23294 // When pipline finished, the currrentTask keep the last 23295 // task (renderTask). 23296 var task = pipeline.currentTask; 23297 if (task) { 23298 var agentStubMap = task.agentStubMap; 23299 if (agentStubMap) { 23300 task = agentStubMap.get(seriesModel.uid); 23301 } 23302 } 23303 return task; 23304 } 23305 } 23306 23307 /* 23308* Licensed to the Apache Software Foundation (ASF) under one 23309* or more contributor license agreements. See the NOTICE file 23310* distributed with this work for additional information 23311* regarding copyright ownership. The ASF licenses this file 23312* to you under the Apache License, Version 2.0 (the 23313* "License"); you may not use this file except in compliance 23314* with the License. You may obtain a copy of the License at 23315* 23316* http://www.apache.org/licenses/LICENSE-2.0 23317* 23318* Unless required by applicable law or agreed to in writing, 23319* software distributed under the License is distributed on an 23320* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23321* KIND, either express or implied. See the License for the 23322* specific language governing permissions and limitations 23323* under the License. 23324*/ 23325 23326 var Component = function () { 23327 /** 23328 * @type {module:zrender/container/Group} 23329 * @readOnly 23330 */ 23331 this.group = new Group(); 23332 23333 /** 23334 * @type {string} 23335 * @readOnly 23336 */ 23337 this.uid = getUID('viewComponent'); 23338 }; 23339 23340 Component.prototype = { 23341 23342 constructor: Component, 23343 23344 init: function (ecModel, api) {}, 23345 23346 render: function (componentModel, ecModel, api, payload) {}, 23347 23348 dispose: function () {} 23349 23350 }; 23351 23352 var componentProto = Component.prototype; 23353 componentProto.updateView 23354 = componentProto.updateLayout 23355 = componentProto.updateVisual 23356 = function (seriesModel, ecModel, api, payload) { 23357 // Do nothing; 23358 }; 23359// Enable Component.extend. 23360 enableClassExtend(Component); 23361 23362// Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. 23363 enableClassManagement(Component, {registerWhenExtend: true}); 23364 23365 /* 23366* Licensed to the Apache Software Foundation (ASF) under one 23367* or more contributor license agreements. See the NOTICE file 23368* distributed with this work for additional information 23369* regarding copyright ownership. The ASF licenses this file 23370* to you under the Apache License, Version 2.0 (the 23371* "License"); you may not use this file except in compliance 23372* with the License. You may obtain a copy of the License at 23373* 23374* http://www.apache.org/licenses/LICENSE-2.0 23375* 23376* Unless required by applicable law or agreed to in writing, 23377* software distributed under the License is distributed on an 23378* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23379* KIND, either express or implied. See the License for the 23380* specific language governing permissions and limitations 23381* under the License. 23382*/ 23383 23384 /** 23385 * @return {string} If large mode changed, return string 'reset'; 23386 */ 23387 var createRenderPlanner = function () { 23388 var inner = makeInner(); 23389 23390 return function (seriesModel) { 23391 var fields = inner(seriesModel); 23392 var pipelineContext = seriesModel.pipelineContext; 23393 23394 var originalLarge = fields.large; 23395 var originalProgressive = fields.progressiveRender; 23396 23397 var large = fields.large = pipelineContext.large; 23398 var progressive = fields.progressiveRender = pipelineContext.progressiveRender; 23399 23400 return !!((originalLarge ^ large) || (originalProgressive ^ progressive)) && 'reset'; 23401 }; 23402 }; 23403 23404 /* 23405* Licensed to the Apache Software Foundation (ASF) under one 23406* or more contributor license agreements. See the NOTICE file 23407* distributed with this work for additional information 23408* regarding copyright ownership. The ASF licenses this file 23409* to you under the Apache License, Version 2.0 (the 23410* "License");
23410 you may not use this file except in compliance 23411* with the License. You may obtain a copy of the License at 23412* 23413* http://www.apache.org/licenses/LICENSE-2.0 23414* 23415* Unless required by applicable law or agreed to in writing, 23416* software distributed under the License is distributed on an 23417* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23418* KIND, either express or implied. See the License for the 23419* specific language governing permissions and limitations 23420* under the License. 23421*/ 23422 23423 var inner$5 = makeInner(); 23424 var renderPlanner = createRenderPlanner(); 23425 23426 function Chart() { 23427 23428 /** 23429 * @type {module:zrender/container/Group} 23430 * @readOnly 23431 */ 23432 this.group = new Group(); 23433 23434 /** 23435 * @type {string} 23436 * @readOnly 23437 */ 23438 this.uid = getUID('viewChart'); 23439 23440 this.renderTask = createTask({ 23441 plan: renderTaskPlan, 23442 reset: renderTaskReset 23443 }); 23444 this.renderTask.context = {view: this}; 23445 } 23446 23447 Chart.prototype = { 23448 23449 type: 'chart', 23450 23451 /** 23452 * Init the chart. 23453 * @param {module:echarts/model/Global} ecModel 23454 * @param {module:echarts/ExtensionAPI} api 23455 */ 23456 init: function (ecModel, api) {}, 23457 23458 /** 23459 * Render the chart. 23460 * @param {module:echarts/model/Series} seriesModel 23461 * @param {module:echarts/model/Global} ecModel 23462 * @param {module:echarts/ExtensionAPI} api 23463 * @param {Object} payload 23464 */ 23465 render: function (seriesModel, ecModel, api, payload) {}, 23466 23467 /** 23468 * Highlight series or specified data item. 23469 * @param {module:echarts/model/Series} seriesModel 23470 * @param {module:echarts/model/Global} ecModel 23471 * @param {module:echarts/ExtensionAPI} api 23472 * @param {Object} payload 23473 */ 23474 highlight: function (seriesModel, ecModel, api, payload) { 23475 toggleHighlight(seriesModel.getData(), payload, 'emphasis'); 23476 }, 23477 23478 /** 23479 * Downplay series or specified data item. 23480 * @param {module:echarts/model/Series} seriesModel 23481 * @param {module:echarts/model/Global} ecModel 23482 * @param {module:echarts/ExtensionAPI} api 23483 * @param {Object} payload 23484 */ 23485 downplay: function (seriesModel, ecModel, api, payload) { 23486 toggleHighlight(seriesModel.getData(), payload, 'normal'); 23487 }, 23488 23489 /** 23490 * Remove self. 23491 * @param {module:echarts/model/Global} ecModel 23492 * @param {module:echarts/ExtensionAPI} api 23493 */ 23494 remove: function (ecModel, api) { 23495 this.group.removeAll(); 23496 }, 23497 23498 /** 23499 * Dispose self. 23500 * @param {module:echarts/model/Global} ecModel 23501 * @param {module:echarts/ExtensionAPI} api 23502 */ 23503 dispose: function () {}, 23504 23505 /** 23506 * Rendering preparation in progressive mode. 23507 * @param {module:echarts/model/Series} seriesModel 23508 * @param {module:echarts/model/Global} ecModel 23509 * @param {module:echarts/ExtensionAPI} api 23510 * @param {Object} payload 23511 */ 23512 incrementalPrepareRender: null, 23513 23514 /** 23515 * Render in progressive mode. 23516 * @param {module:echarts/model/Series} seriesModel 23517 * @param {module:echarts/model/Global} ecModel 23518 * @param {module:echarts/ExtensionAPI} api 23519 * @param {Object} payload 23520 */ 23521 incrementalRender: null, 23522 23523 /** 23524 * Update transform directly. 23525 * @param {module:echarts/model/Series} seriesModel 23526 * @param {module:echarts/model/Global} ecModel 23527 * @param {module:echarts/ExtensionAPI} api 23528 * @param {Object} payload 23529 * @return {Object} {update: true} 23530 */ 23531 updateTransform: null 23532 23533 /** 23534 * The view contains the given point. 23535 * @interface 23536 * @param {Array.<number>} point 23537 * @return {boolean} 23538 */ 23539 // containPoint: function () {} 23540 23541 }; 23542 23543 var chartProto = Chart.prototype; 23544 chartProto.updateView 23545 = chartProto.updateLayout 23546 = chartProto.updateVisual 23547 = function (seriesModel, ecModel, api, payload) { 23548 this.render(seriesModel, ecModel, api, payload); 23549 }; 23550 23551 /** 23552 * Set state of single element 23553 * @param {module:zrender/Element} el 23554 * @param {string} state 23555 */ 23556 function elSetState(el, state) { 23557 if (el) { 23558 el.trigger(state); 23559 if (el.type === 'group') { 23560 for (var i = 0; i < el.childCount(); i++) { 23561 elSetState(el.childAt(i), state); 23562 } 23563 } 23564 } 23565 } 23566 /**
23567 * @param {module:echarts/data/List} data 23568 * @param {Object} payload 23569 * @param {string} state 'normal'|'emphasis' 23570 */ 23571 function toggleHighlight(data, payload, state) { 23572 var dataIndex = queryDataIndex(data, payload); 23573 23574 if (dataIndex != null) { 23575 each$1(normalizeToArray(dataIndex), function (dataIdx) { 23576 elSetState(data.getItemGraphicEl(dataIdx), state); 23577 }); 23578 } 23579 else { 23580 data.eachItemGraphicEl(function (el) { 23581 elSetState(el, state); 23582 }); 23583 } 23584 } 23585 23586// Enable Chart.extend. 23587 enableClassExtend(Chart, ['dispose']); 23588 23589// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. 23590 enableClassManagement(Chart, {registerWhenExtend: true}); 23591 23592 Chart.markUpdateMethod = function (payload, methodName) { 23593 inner$5(payload).updateMethod = methodName; 23594 }; 23595 23596 function renderTaskPlan(context) { 23597 return renderPlanner(context.model); 23598 } 23599 23600 function renderTaskReset(context) { 23601 var seriesModel = context.model; 23602 var ecModel = context.ecModel; 23603 var api = context.api; 23604 var payload = context.payload; 23605 // ???! remove updateView updateVisual 23606 var progressiveRender = seriesModel.pipelineContext.progressiveRender; 23607 var view = context.view; 23608 23609 var updateMethod = payload && inner$5(payload).updateMethod; 23610 var methodName = progressiveRender 23611 ? 'incrementalPrepareRender' 23612 : (updateMethod && view[updateMethod]) 23613 ? updateMethod 23614 // `appendData` is also supported when data amount 23615 // is less than progressive threshold. 23616 : 'render'; 23617 23618 if (methodName !== 'render') { 23619 view[methodName](seriesModel, ecModel, api, payload); 23620 } 23621 23622 return progressMethodMap[methodName]; 23623 } 23624 23625 var progressMethodMap = { 23626 incrementalPrepareRender: { 23627 progress: function (params, context) { 23628 context.view.incrementalRender( 23629 params, context.model, context.ecModel, context.api, context.payload 23630 ); 23631 } 23632 }, 23633 render: { 23634 // Put view.render in `progress` to support appendData. But in this case 23635 // view.render should not be called in reset, otherwise it will be called 23636 // twise. Use `forceFirstProgress` to make sure that view.render is called 23637 // in any cases. 23638 forceFirstProgress: true, 23639 progress: function (params, context) { 23640 context.view.render( 23641 context.model, context.ecModel, context.api, context.payload 23642 ); 23643 } 23644 } 23645 }; 23646 23647 /* 23648* Licensed to the Apache Software Foundation (ASF) under one 23649* or more contributor license agreements. See the NOTICE file 23650* distributed with this work for additional information 23651* regarding copyright ownership. The ASF licenses this file 23652* to you under the Apache License, Version 2.0 (the 23653* "License"); you may not use this file except in compliance 23654* with the License. You may obtain a copy of the License at 23655* 23656* http://www.apache.org/licenses/LICENSE-2.0 23657* 23658* Unless required by applicable law or agreed to in writing, 23659* software distributed under the License is distributed on an 23660* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23661* KIND, either express or implied. See the License for the 23662* specific language governing permissions and limitations 23663* under the License. 23664*/ 23665 23666 23667 var ORIGIN_METHOD = '\0__throttleOriginMethod'; 23668 var RATE = '\0__throttleRate'; 23669 var THROTTLE_TYPE = '\0__throttleType'; 23670 23671 /** 23672 * @public 23673 * @param {(Function)} fn 23674 * @param {number} [delay=0] Unit: ms. 23675 * @param {boolean} [debounce=false] 23676 * true: If call interval less than `delay`, only the last call works. 23677 * false: If call interval less than `delay, call works on fixed rate. 23678 * @return {(Function)} throttled fn. 23679 */ 23680 function throttle(fn, delay, debounce) { 23681 23682 var currCall; 23683 var lastCall = 0; 23684 var lastExec = 0; 23685 var timer = null; 23686 var diff; 23687 var scope; 23688 var args; 23689 var debounceNextCall; 23690
23691 delay = delay || 0; 23692 23693 function exec() { 23694 lastExec = (new Date()).getTime(); 23695 timer = null; 23696 fn.apply(scope, args || []); 23697 } 23698 23699 var cb = function () { 23700 currCall = (new Date()).getTime(); 23701 scope = this; 23702 args = arguments; 23703 var thisDelay = debounceNextCall || delay; 23704 var thisDebounce = debounceNextCall || debounce; 23705 debounceNextCall = null; 23706 diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay; 23707 23708 clearTimeout(timer); 23709 23710 // Here we should make sure that: the `exec` SHOULD NOT be called later 23711 // than a new call of `cb`, that is, preserving the command order. Consider 23712 // calculating "scale rate" when roaming as an example. When a call of `cb` 23713 // happens, either the `exec` is called dierectly, or the call is delayed. 23714 // But the delayed call should never be later than next call of `cb`. Under 23715 // this assurance, we can simply update view state each time `dispatchAction` 23716 // triggered by user roaming, but not need to add extra code to avoid the 23717 // state being "rolled-back". 23718 if (thisDebounce) { 23719 timer = setTimeout(exec, thisDelay); 23720 } 23721 else { 23722 if (diff >= 0) { 23723 exec(); 23724 } 23725 else { 23726 timer = setTimeout(exec, -diff); 23727 } 23728 } 23729 23730 lastCall = currCall; 23731 }; 23732 23733 /** 23734 * Clear throttle. 23735 * @public 23736 */ 23737 cb.clear = function () { 23738 if (timer) { 23739 clearTimeout(timer); 23740 timer = null; 23741 } 23742 }; 23743 23744 /** 23745 * Enable debounce once. 23746 */ 23747 cb.debounceNextCall = function (debounceDelay) { 23748 debounceNextCall = debounceDelay; 23749 }; 23750 23751 return cb; 23752 } 23753 23754 /** 23755 * Create throttle method or update throttle rate. 23756 * 23757 * @example 23758 * ComponentView.prototype.render = function () { 23759 * ... 23760 * throttle.createOrUpdate( 23761 * this, 23762 * '_dispatchAction', 23763 * this.model.get('throttle'), 23764 * 'fixRate' 23765 * ); 23766 * }; 23767 * ComponentView.prototype.remove = function () { 23768 * throttle.clear(this, '_dispatchAction'); 23769 * }; 23770 * ComponentView.prototype.dispose = function () { 23771 * throttle.clear(this, '_dispatchAction'); 23772 * }; 23773 * 23774 * @public 23775 * @param {Object} obj 23776 * @param {string} fnAttr 23777 * @param {number} [rate] 23778 * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce' 23779 * @return {Function} throttled function. 23780 */ 23781 function createOrUpdate(obj, fnAttr, rate, throttleType) { 23782 var fn = obj[fnAttr]; 23783 23784 if (!fn) { 23785 return; 23786 } 23787 23788 var originFn = fn[ORIGIN_METHOD] || fn; 23789 var lastThrottleType = fn[THROTTLE_TYPE]; 23790 var lastRate = fn[RATE]; 23791 23792 if (lastRate !== rate || lastThrottleType !== throttleType) { 23793 if (rate == null || !throttleType) { 23794 return (obj[fnAttr] = originFn); 23795 } 23796 23797 fn = obj[fnAttr] = throttle( 23798 originFn, rate, throttleType === 'debounce' 23799 ); 23800 fn[ORIGIN_METHOD] = originFn; 23801 fn[THROTTLE_TYPE] = throttleType; 23802 fn[RATE] = rate; 23803 } 23804 23805 return fn; 23806 } 23807 23808 /** 23809 * Clear throttle. Example see throttle.createOrUpdate. 23810 * 23811 * @public 23812 * @param {Object} obj 23813 * @param {string} fnAttr 23814 */ 23815 function clear(obj, fnAttr) { 23816 var fn = obj[fnAttr]; 23817 if (fn && fn[ORIGIN_METHOD]) { 23818 obj[fnAttr] = fn[ORIGIN_METHOD]; 23819 } 23820 } 23821 23822 /* 23823* Licensed to the Apache Software Foundation (ASF) under one 23824* or more contributor license agreements. See the NOTICE file 23825* distributed with this work for additional information 23826* regarding copyright ownership. The ASF licenses this file 23827* to you under the Apache License, Version 2.0 (the 23828* "License");
23828 you may not use this file except in compliance 23829* with the License. You may obtain a copy of the License at 23830* 23831* http://www.apache.org/licenses/LICENSE-2.0 23832* 23833* Unless required by applicable law or agreed to in writing, 23834* software distributed under the License is distributed on an 23835* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23836* KIND, either express or implied. See the License for the 23837* specific language governing permissions and limitations 23838* under the License. 23839*/ 23840 23841 var seriesColor = { 23842 createOnAllSeries: true, 23843 performRawSeries: true, 23844 reset: function (seriesModel, ecModel) { 23845 var data = seriesModel.getData(); 23846 var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.color').split('.'); 23847 var color = seriesModel.get(colorAccessPath) // Set in itemStyle 23848 || seriesModel.getColorFromPalette( 23849 // TODO series count changed. 23850 seriesModel.name, null, ecModel.getSeriesCount() 23851 ); // Default color 23852 23853 // FIXME Set color function or use the platte color 23854 data.setVisual('color', color); 23855 23856 // Only visible series has each data be visual encoded 23857 if (!ecModel.isSeriesFiltered(seriesModel)) { 23858 if (typeof color === 'function' && !(color instanceof Gradient)) { 23859 data.each(function (idx) { 23860 data.setItemVisual( 23861 idx, 'color', color(seriesModel.getDataParams(idx)) 23862 ); 23863 }); 23864 } 23865 23866 // itemStyle in each data item 23867 var dataEach = function (data, idx) { 23868 var itemModel = data.getItemModel(idx); 23869 var color = itemModel.get(colorAccessPath, true); 23870 if (color != null) { 23871 data.setItemVisual(idx, 'color', color); 23872 } 23873 }; 23874 23875 return { dataEach: data.hasItemOption ? dataEach : null }; 23876 } 23877 } 23878 }; 23879 23880 /* 23881* Licensed to the Apache Software Foundation (ASF) under one 23882* or more contributor license agreements. See the NOTICE file 23883* distributed with this work for additional information 23884* regarding copyright ownership. The ASF licenses this file 23885* to you under the Apache License, Version 2.0 (the 23886* "License"); you may not use this file except in compliance 23887* with the License. You may obtain a copy of the License at 23888* 23889* http://www.apache.org/licenses/LICENSE-2.0 23890* 23891* Unless required by applicable law or agreed to in writing, 23892* software distributed under the License is distributed on an 23893* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23894* KIND, either express or implied. See the License for the 23895* specific language governing permissions and limitations 23896* under the License. 23897*/ 23898 23899 23900 var lang = { 23901 toolbox: { 23902 brush: { 23903 title: { 23904 rect: 'éâè°é«å¤å«¨', 23905 polygon: 'é¦å ¥â¬ï¿½', 23906 lineX: '妯îæé«å¤å«¨', 23907 lineY: '绾éæé«å¤å«¨', 23908 keep: 'æ·æ¿å¯é«å¤å«¨', 23909 clear: 'å¨å´æ«é«å¤å«¨' 23910 } 23911 }, 23912 dataView: { 23913 title: 'éçåµçåæµ', 23914 lang: ['éçåµçåæµ', 'éæ½æ£´', 'ééæ'] 23915 }, 23916 dataZoom: { 23917 title: { 23918 zoom: 'éåç ç¼âæ', 23919 back: 'éåç ç¼âææ©æ¨ºå¸«' 23920 } 23921 }, 23922 magicType: { 23923 title: { 23924 line: 'éå¨å´²æ¶çå§ç»¾å®æµ', 23925 bar: 'éå¨å´²æ¶çç ´éè·ºæµ', 23926 stack: 'éå¨å´²æ¶åç¢é�', 23927 tiled: 'éå¨å´²æ¶åé©é¾ï¿½' 23928 } 23929 }, 23930 restore: { 23931 title: 'æ©æ¨ºå¸«' 23932 }, 23933 saveAsImage: { 23934 title: 'æ·æ¿ç¨æ¶åæµé�', 23935 lang: ['éæ½æéï¹ç¨æ¶åæµé�'] 23936 } 23937 }, 23938 series: { 23939 typeNames: { 23940 pie: 'æ¥ç æµ', 23941 bar: 'é辩姸é¥ï¿½', 23942 line: 'é¶æ¨¼åé¥ï¿½', 23943 scatter: 'éï½å£é¥ï¿½', 23944 effectScatter: 'å¨ç¸åéï½å£é¥ï¿½', 23945 radar: 'éç¯æªé¥ï¿½', 23946 tree: 'éææµ', 23947 treemap: 'éâè°éææµ', 23948 boxplot: 'ç» åç·é¥ï¿½', 23949 candlestick: 'Kç»¾å®æµ', 23950 k: 'Kç»¾å®æµ', 23951 heatmap: 'éî å§é¥ï¿½', 23952 map: '馿¿æµ', 23953 parallel: 'éªå® îé§æç£é¥ï¿½', 23954 lines: 'ç»¾å®æµ', 23955 graph: 'éå´é´é¥ï¿½', 23956 sankey: '妿çé¥ï¿½', 23957 funnel: 'å©å¿æé¥ï¿½', 23958 gauge: 'æµ îã驿¨ºæµ',
23959 pictorialBar: 'çâ³è°éåæµ', 23960 themeRiver: 'æ¶å©î½å¨è«ç¥¦é¥ï¿½', 23961 sunburst: 'éî æ£©é¥ï¿½' 23962 } 23963 }, 23964 aria: { 23965 general: { 23966 withTitle: 'æ©æ¬æ§¸æ¶â¬æ¶îå§æµåºâ¬æ¸°title}饿¿æ®é¥æãé�', 23967 withoutTitle: 'æ©æ¬æ§¸æ¶â¬æ¶îæµçîç´' 23968 }, 23969 series: { 23970 single: { 23971 prefix: '', 23972 withName: '饿ã绫诲ç·éçseriesType}éå²ãç»ç°seriesName}é�', 23973 withoutName: '饿ã绫诲ç·éçseriesType}é�' 23974 }, 23975 multiple: { 23976 prefix: 'ç¹åªæ±{seriesCount}æ¶îæµçã§é´éæ¥ç²é´æ©â¬ï¿½', 23977 withName: 'ç»çseriesId}æ¶îé´éæ¥æ§¸æ¶â¬æ¶îãç»ç°seriesName}é¨åseriesType}é�', 23978 withoutName: 'ç»çseriesId}æ¶îé´éæ¥æ§¸æ¶â¬æ¶ç seriesType}é�', 23979 separator: { 23980 middle: 'é�', 23981 end: 'é�' 23982 } 23983 } 23984 }, 23985 data: { 23986 allData: 'éèµæé¹î½æ§¸é¥æºâ¬ï¿½', 23987 partialData: 'éæµè éå±½å¢ {displayCnt}æ¤¤è§æ§¸é¥æºâ¬ï¿½', 23988 withName: '{name}é¨å¬æé¹î½æ§¸{value}', 23989 withoutName: '{value}', 23990 separator: { 23991 middle: 'é�', 23992 end: '' 23993 } 23994 } 23995 } 23996 }; 23997 23998 /* 23999* Licensed to the Apache Software Foundation (ASF) under one 24000* or more contributor license agreements. See the NOTICE file 24001* distributed with this work for additional information 24002* regarding copyright ownership. The ASF licenses this file 24003* to you under the Apache License, Version 2.0 (the 24004* "License"); you may not use this file except in compliance 24005* with the License. You may obtain a copy of the License at 24006* 24007* http://www.apache.org/licenses/LICENSE-2.0 24008* 24009* Unless required by applicable law or agreed to in writing, 24010* software distributed under the License is distributed on an 24011* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 24012* KIND, either express or implied. See the License for the 24013* specific language governing permissions and limitations 24014* under the License. 24015*/ 24016 24017 var aria = function (dom, ecModel) { 24018 var ariaModel = ecModel.getModel('aria'); 24019 if (!ariaModel.get('show')) { 24020 return; 24021 } 24022 else if (ariaModel.get('description')) { 24023 dom.setAttribute('aria-label', ariaModel.get('description')); 24024 return; 24025 } 24026 24027 var seriesCnt = 0; 24028 ecModel.eachSeries(function (seriesModel, idx) { 24029 ++seriesCnt; 24030 }, this); 24031 24032 var maxDataCnt = ariaModel.get('data.maxCount') || 10; 24033 var maxSeriesCnt = ariaModel.get('series.maxCount') || 10; 24034 var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt); 24035 24036 var ariaLabel; 24037 if (seriesCnt < 1) { 24038 // No series, no aria label 24039 return; 24040 } 24041 else { 24042 var title = getTitle(); 24043 if (title) { 24044 ariaLabel = replace(getConfig('general.withTitle'), { 24045 title: title 24046 }); 24047 } 24048 else { 24049 ariaLabel = getConfig('general.withoutTitle'); 24050 } 24051 24052 var seriesLabels = []; 24053 var prefix = seriesCnt > 1 24054 ? 'series.multiple.prefix' 24055 : 'series.single.prefix'; 24056 ariaLabel += replace(getConfig(prefix), { seriesCount: seriesCnt }); 24057 24058 ecModel.eachSeries(function (seriesModel, idx) { 24059 if (idx < displaySeriesCnt) { 24060 var seriesLabel; 24061 24062 var seriesName = seriesModel.get('name'); 24063 var seriesTpl = 'series.' 24064 + (seriesCnt > 1 ? 'multiple' : 'single') + '.'; 24065 seriesLabel = getConfig(seriesName 24066 ? seriesTpl + 'withName' 24067 : seriesTpl + 'withoutName'); 24068 24069 seriesLabel = replace(seriesLabel, { 24070 seriesId: seriesModel.seriesIndex, 24071 seriesName: seriesModel.get('name'), 24072 seriesType: getSeriesTypeName(seriesModel.subType) 24073 }); 24074 24075 var data = seriesModel.getData(); 24076 window.data = data; 24077 if (data.count() > maxDataCnt) { 24078 // Show part of data 24079 seriesLabel += replace(getConfig('data.partialData'), { 24080 displayCnt: maxDataCnt 24081 }); 24082 } 24083 else { 24084 seriesLabel += getConfig('data.allData'); 24085 } 24086 24087 var dataLabels = []; 24088 for (var i = 0; i < data.count(); i++) { 24089 if (i < maxDataCnt) { 24090 var name = data.getName(i); 24091 var value = retrieveRawValue(data, i); 24092 dataLabels.push( 24093 replace( 24094 name 24095 ? getConfig('data.withName') 24096 : getConfig('data.withoutName'), 24097 { 24098 name: name, 24099 value: value 24100 } 24101 ) 24102 ); 24103 } 24104 } 24105 seriesLabel += dataLabels 24106 .join(getConfig('data.separator.middle')) 24107 + getConfig('data.separator.end'); 24108 24109 seriesLabels.push(seriesLabel); 24110 } 24111 }); 24112 24113 ariaLabel += seriesLabels 24114 .join(getConfig('series.multiple.separator.middle')) 24115 + getConfig('series.multiple.separator.end'); 24116 24117 dom.setAttribute('aria-label', ariaLabel); 24118 } 24119 24120 function replace(str, keyValues) { 24121 if (typeof str !== 'string') { 24122 return str; 24123 } 24124 24125 var result = str; 24126 each$1(keyValues, function (value, key) { 24127 result = result.replace( 24128 new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'), 24129 value 24130 ); 24131 }); 24132 return result; 24133 } 24134 24135 function getConfig(path) { 24136 var userConfig = ariaModel.get(path); 24137 if (userConfig == null) { 24138 var pathArr = path.split('.'); 24139 var result = lang.aria; 24140 for (var i = 0; i < pathArr.length; ++i) { 24141 result = result[pathArr[i]]; 24142 } 24143 return result; 24144 } 24145 else { 24146 return userConfig; 24147 } 24148 } 24149 24150 function getTitle() { 24151 var title = ecModel.getModel('title').option; 24152 if (title && title.length) { 24153 title = title[0]; 24154 } 24155 return title && title.text; 24156 } 24157 24158 function getSeriesTypeName(type) { 24159 return lang.series.typeNames[type] || 'é·îç¾æ¶å¤æµ'; 24160 } 24161 }; 24162 24163 /* 24164* Licensed to the Apache Software Foundation (ASF) under one 24165* or more contributor license agreements. See the NOTICE file 24166* distributed with this work for additional information 24167* regarding copyright ownership. The ASF licenses this file 24168* to you under the Apache License, Version 2.0 (the 24169* "License");
24169 you may not use this file except in compliance 24170* with the License. You may obtain a copy of the License at 24171* 24172* http://www.apache.org/licenses/LICENSE-2.0 24173* 24174* Unless required by applicable law or agreed to in writing, 24175* software distributed under the License is distributed on an 24176* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 24177* KIND, either express or implied. See the License for the 24178* specific language governing permissions and limitations 24179* under the License. 24180*/ 24181 24182 var PI$1 = Math.PI; 24183 24184 /** 24185 * @param {module:echarts/ExtensionAPI} api 24186 * @param {Object} [opts] 24187 * @param {string} [opts.text] 24188 * @param {string} [opts.color] 24189 * @param {string} [opts.textColor] 24190 * @return {module:zrender/Element} 24191 */ 24192 var loadingDefault = function (api, opts) { 24193 opts = opts || {}; 24194 defaults(opts, { 24195 text: 'loading', 24196 color: '#c23531', 24197 textColor: '#000', 24198 maskColor: 'rgba(255, 255, 255, 0.8)', 24199 zlevel: 0 24200 }); 24201 var mask = new Rect({ 24202 style: { 24203 fill: opts.maskColor 24204 }, 24205 zlevel: opts.zlevel, 24206 z: 10000 24207 }); 24208 var arc = new Arc({ 24209 shape: { 24210 startAngle: -PI$1 / 2, 24211 endAngle: -PI$1 / 2 + 0.1, 24212 r: 10 24213 }, 24214 style: { 24215 stroke: opts.color, 24216 lineCap: 'round', 24217 lineWidth: 5 24218 }, 24219 zlevel: opts.zlevel, 24220 z: 10001 24221 }); 24222 var labelRect = new Rect({ 24223 style: { 24224 fill: 'none', 24225 text: opts.text, 24226 textPosition: 'right', 24227 textDistance: 10, 24228 textFill: opts.textColor 24229 }, 24230 zlevel: opts.zlevel, 24231 z: 10001 24232 }); 24233 24234 arc.animateShape(true) 24235 .when(1000, { 24236 endAngle: PI$1 * 3 / 2 24237 }) 24238 .start('circularInOut'); 24239 arc.animateShape(true) 24240 .when(1000, { 24241 startAngle: PI$1 * 3 / 2 24242 }) 24243 .delay(300) 24244 .start('circularInOut'); 24245 24246 var group = new Group(); 24247 group.add(arc); 24248 group.add(labelRect); 24249 group.add(mask); 24250 // Inject resize 24251 group.resize = function () { 24252 var cx = api.getWidth() / 2; 24253 var cy = api.getHeight() / 2; 24254 arc.setShape({ 24255 cx: cx, 24256 cy: cy 24257 }); 24258 var r = arc.shape.r; 24259 labelRect.setShape({ 24260 x: cx - r, 24261 y: cy - r, 24262 width: r * 2, 24263 height: r * 2 24264 }); 24265 24266 mask.setShape({ 24267 x: 0, 24268 y: 0, 24269 width: api.getWidth(), 24270 height: api.getHeight() 24271 }); 24272 }; 24273 group.resize(); 24274 return group; 24275 }; 24276 24277 /* 24278* Licensed to the Apache Software Foundation (ASF) under one 24279* or more contributor license agreements. See the NOTICE file 24280* distributed with this work for additional information 24281* regarding copyright ownership. The ASF licenses this file 24282* to you under the Apache License, Version 2.0 (the 24283* "License"); you may not use this file except in compliance 24284* with the License. You may obtain a copy of the License at 24285* 24286* http://www.apache.org/licenses/LICENSE-2.0 24287* 24288* Unless required by applicable law or agreed to in writing, 24289* software distributed under the License is distributed on an 24290* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 24291* KIND, either express or implied. See the License for the 24292* specific language governing permissions and limitations 24293* under the License. 24294*/ 24295 24296 /** 24297 * @module echarts/stream/Scheduler 24298 */ 24299 24300 /** 24301 * @constructor 24302 */ 24303 function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) { 24304 this.ecInstance = ecInstance; 24305 this.api = api; 24306 this.unfinished; 24307 24308 // Fix current processors in case that in some rear cases that 24309 // processors might be registered after echarts instance created. 24310 // Register processors incrementally for a echarts instance is
vendor: 4,354 bytes, lines 24311-24406
24311 // not supported by this stream architecture. 24312 var dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice(); 24313 var visualHandlers = this._visualHandlers = visualHandlers.slice(); 24314 this._allHandlers = dataProcessorHandlers.concat(visualHandlers); 24315 24316 /** 24317 * @private 24318 * @type { 24319 * [handlerUID: string]: { 24320 * seriesTaskMap?: { 24321 * [seriesUID: string]: Task 24322 * }, 24323 * overallTask?: Task 24324 * } 24325 * } 24326 */ 24327 this._stageTaskMap = createHashMap(); 24328 } 24329 24330 var proto = Scheduler.prototype; 24331 24332 /** 24333 * @param {module:echarts/model/Global} ecModel 24334 * @param {Object} payload 24335 */ 24336 proto.restoreData = function (ecModel, payload) { 24337 // TODO: Only restroe needed series and components, but not all components. 24338 // Currently `restoreData` of all of the series and component will be called. 24339 // But some independent components like `title`, `legend`, `graphic`, `toolbox`, 24340 // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`, 24341 // and some components like coordinate system, axes, dataZoom, visualMap only 24342 // need their target series refresh. 24343 // (1) If we are implementing this feature some day, we should consider these cases: 24344 // if a data processor depends on a component (e.g., dataZoomProcessor depends 24345 // on the settings of `dataZoom`), it should be re-performed if the component 24346 // is modified by `setOption`. 24347 // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`, 24348 // it should be re-performed when the result array of `getTargetSeries` changed. 24349 // We use `dependencies` to cover these issues. 24350 // (3) How to update target series when coordinate system related components modified. 24351 24352 // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty, 24353 // and this case all of the tasks will be set as dirty. 24354 24355 ecModel.restoreData(payload); 24356 24357 // Theoretically an overall task not only depends on each of its target series, but also 24358 // depends on all of the series. 24359 // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks 24360 // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure 24361 // that the overall task is set as dirty and to be performed, otherwise it probably cause 24362 // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it 24363 // probably cause state chaos (consider `dataZoomProcessor`). 24364 this._stageTaskMap.each(function (taskRecord) { 24365 var overallTask = taskRecord.overallTask; 24366 overallTask && overallTask.dirty(); 24367 }); 24368 }; 24369 24370// If seriesModel provided, incremental threshold is check by series data. 24371 proto.getPerformArgs = function (task, isBlock) { 24372 // For overall task 24373 if (!task.__pipeline) { 24374 return; 24375 } 24376 24377 var pipeline = this._pipelineMap.get(task.__pipeline.id); 24378 var pCtx = pipeline.context; 24379 var incremental = !isBlock 24380 && pipeline.progressiveEnabled 24381 && (!pCtx || pCtx.progressiveRender) 24382 && task.__idxInPipeline > pipeline.blockIndex; 24383 24384 var step = incremental ? pipeline.step : null; 24385 var modDataCount = pCtx && pCtx.modDataCount; 24386 var modBy = modDataCount != null ? Math.ceil(modDataCount / step): null; 24387 24388 return {step: step, modBy: modBy, modDataCount: modDataCount}; 24389 }; 24390 24391 proto.getPipeline = function (pipelineId) { 24392 return this._pipelineMap.get(pipelineId); 24393 }; 24394 24395 /** 24396 * Current, progressive rendering starts from visual and layout. 24397 * Always detect render mode in the same stage, avoiding that incorrect 24398 * detection caused by data filtering. 24399 * Caution: 24400 * `updateStreamModes` use `seriesModel.getData()`. 24401 */ 24402 proto.updateStreamModes = function (seriesModel, view) { 24403 var pipeline = this._pipelineMap.get(seriesModel.uid); 24404 var data = seriesModel.getData(); 24405 var dataLen = data.count(); 24406
24407 // `progressiveRender` means that can render progressively in each 24408 // animation frame. Note that some types of series do not provide 24409 // `view.incrementalPrepareRender` but support `chart.appendData`. We 24410 // use the term `incremental` but not `progressive` to describe the 24411 // case that `chart.appendData`. 24412 var progressiveRender = pipeline.progressiveEnabled 24413 && view.incrementalPrepareRender 24414 && dataLen >= pipeline.threshold; 24415 24416 var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); 24417 24418 // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint. 24419 // see `test/candlestick-large3.html` 24420 var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null; 24421 24422 seriesModel.pipelineContext = pipeline.context = { 24423 progressiveRender: progressiveRender, 24424 modDataCount: modDataCount, 24425 large: large 24426 }; 24427 }; 24428 24429 proto.restorePipelines = function (ecModel) { 24430 var scheduler = this; 24431 var pipelineMap = scheduler._pipelineMap = createHashMap(); 24432 24433 ecModel.eachSeries(function (seriesModel) { 24434 var progressive = seriesModel.getProgressive(); 24435 var pipelineId = seriesModel.uid; 24436 24437 pipelineMap.set(pipelineId, { 24438 id: pipelineId, 24439 head: null, 24440 tail: null, 24441 threshold: seriesModel.getProgressiveThreshold(), 24442 progressiveEnabled: progressive 24443 && !(seriesModel.preventIncremental && seriesModel.preventIncremental()), 24444 blockIndex: -1, 24445 step: Math.round(progressive || 700), 24446 count: 0 24447 }); 24448 24449 pipe(scheduler, seriesModel, seriesModel.dataTask); 24450 }); 24451 }; 24452 24453 proto.prepareStageTasks = function () { 24454 var stageTaskMap = this._stageTaskMap; 24455 var ecModel = this.ecInstance.getModel(); 24456 var api = this.api; 24457 24458 each$1(this._allHandlers, function (handler) { 24459 var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, []); 24460 24461 handler.reset && createSeriesStageTask(this, handler, record, ecModel, api); 24462 handler.overallReset && createOverallStageTask(this, handler, record, ecModel, api); 24463 }, this); 24464 }; 24465 24466 proto.prepareView = function (view, model, ecModel, api) { 24467 var renderTask = view.renderTask; 24468 var context = renderTask.context; 24469 24470 context.model = model; 24471 context.ecModel = ecModel; 24472 context.api = api; 24473 24474 renderTask.__block = !view.incrementalPrepareRender; 24475 24476 pipe(this, model, renderTask); 24477 }; 24478 24479 24480 proto.performDataProcessorTasks = function (ecModel, payload) { 24481 // If we do not use `block` here, it should be considered when to update modes. 24482 performStageTasks(this, this._dataProcessorHandlers, ecM
24482odel, payload, {block: true}); 24483 }; 24484 24485// opt 24486// opt.visualType: 'visual' or 'layout' 24487// opt.setDirty 24488 proto.performVisualTasks = function (ecModel, payload, opt) { 24489 performStageTasks(this, this._visualHandlers, ecModel, payload, opt); 24490 }; 24491 24492 function performStageTasks(scheduler, stageHandlers, ecModel, payload, opt) { 24493 opt = opt || {}; 24494 var unfinished; 24495 24496 each$1(stageHandlers, function (stageHandler, idx) { 24497 if (opt.visualType && opt.visualType !== stageHandler.visualType) { 24498 return; 24499 } 24500 24501 var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid); 24502 var seriesTaskMap = stageHandlerRecord.seriesTaskMap; 24503 var overallTask = stageHandlerRecord.overallTask; 24504 24505 if (overallTask) { 24506 var overallNeedDirty; 24507 var agentStubMap = overallTask.agentStubMap; 24508 agentStubMap.each(function (stub) { 24509 if (needSetDirty(opt, stub)) { 24510 stub.dirty(); 24511 overallNeedDirty = true; 24512 } 24513 }); 24514 overallNeedDirty && overallTask.dirty(); 24515 updatePayload(overallTask, payload); 24516 var performArgs = scheduler.getPerformArgs(overallTask, opt.block); 24517 // Execute stubs firstly, which may set the overall task dirty, 24518 // then execute the overall task. And stub will call seriesModel.setData, 24519 // which ensures that in the overallTask seriesModel.getData() will not 24520 // return incorrect data. 24521 agentStubMap.each(function (stub) { 24522 stub.perform(performArgs); 24523 }); 24524 unfinished |= overallTask.perform(performArgs); 24525 } 24526 else if (seriesTaskMap) { 24527 seriesTaskMap.each(function (task, pipelineId) { 24528 if (needSetDirty(opt, task)) { 24529 task.dirty(); 24530 } 24531 var performArgs = scheduler.getPerformArgs(task, opt.block); 24532 performArgs.skip = !stageHandler.performRawSeries 24533 && ecModel.isSeriesFiltered(task.context.model); 24534 updatePayload(task, payload); 24535 unfinished |= task.perform(performArgs); 24536 }); 24537 } 24538 }); 24539 24540 function needSetDirty(opt, task) { 24541 return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id)); 24542 } 24543 24544 scheduler.unfinished |= unfinished; 24545 } 24546 24547 proto.performSeriesTasks = function (ecModel) { 24548 var unfinished; 24549 24550 ecModel.eachSeries(function (seriesModel) { 24551 // Progress to the end for dataInit and dataRestore. 24552 unfinished |= seriesModel.dataTask.perform(); 24553 }); 24554 24555 this.unfinished |= unfinished; 24556 }; 24557 24558 proto.plan = function () { 24559 // Travel pipelines, check block. 24560 this._pipelineMap.each(function (pipeline) { 24561 var task = pipeline.tail; 24562 do { 24563 if (task.__block) { 24564 pipeline.blockIndex = task.__idxInPipeline; 24565 break; 24566 } 24567 task = task.getUpstream(); 24568 } 24569 while (task); 24570 }); 24571 }; 24572 24573 var updatePayload = proto.updatePayload = function (task, payload) { 24574 payload !== 'remain' && (task.context.payload = payload); 24575 }; 24576 24577 function createSeriesStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) { 24578 var seriesTaskMap = stageHandlerRecord.seriesTaskMap 24579 || (stageHandlerRecord.seriesTaskMap = createHashMap()); 24580 var seriesType = stageHandler.seriesType; 24581 var getTargetSeries = stageHandler.getTargetSeries; 24582 24583 // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily, 24584 // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`, 24585 // it works but it may cause other irrelevant charts blocked. 24586 if (stageHandler.createOnAllSeries) { 24587 ecModel.eachRawSeries(create); 24588 } 24589 else if (seriesType) { 24590 ecModel.eachRawSeriesByType(seriesType, create); 24591 } 24592 else if (getTargetSeries) { 24593 getTargetSeries(ecModel, api).each(create); 24594 } 24595 24596 function create(seriesModel) { 24597 var pipelineId = seriesModel.uid; 24598 24599 // Init tasks for each seriesModel only once. 24600 // Reuse original task instance. 24601 var task = seriesTaskMap.get(pipelineId) 24602 || seriesTaskMap.set(pipelineId, createTask({ 24603 plan: seriesTaskPlan, 24604 reset: seriesTaskReset, 24605 count: seriesTaskCount 24606 })); 24607 task.context = { 24608 model: seriesModel, 24609 ecModel: ecModel, 24610 api: api, 24611 useClearVisual: stageHandler.isVisual && !stageHandler.isLayout, 24612 plan: stageHandler.plan, 24613 reset: stageHandler.reset, 24614 scheduler: scheduler 24615 }; 24616 pipe(scheduler, seriesModel, task); 24617 } 24618 24619 // Clear unused series tasks. 24620 var pipelineMap = scheduler._pipelineMap; 24621 seriesTaskMap.each(function (task, pipelineId) { 24622 if (!pipelineMap.get(pipelineId)) { 24623 task.dispose(); 24624 seriesTaskMap.removeKey(pipelineId); 24625 } 24626 }); 24627 } 24628 24629 function createOverallStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) { 24630 var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask 24631 // For overall task, the function only be called on reset stage. 24632 || createTask({reset: overallTaskReset}); 24633 24634 overallTask.context = { 24635 ecModel: ecModel, 24636 api: api, 24637 overallReset: stageHandler.overallReset, 24638 scheduler: scheduler 24639 }; 24640 24641 // Reuse orignal stubs. 24642 var agentStubMap = overallTask.agentStubMap = overallTask.agentStubMap || createHashMap(); 24643 24644 var seriesType = stageHandler.seriesType; 24645 var getTargetSeries = stageHandler.getTargetSeries; 24646 var overallProgress = true; 24647 var modifyOutputEnd = stageHandler.modifyOutputEnd; 24648 24649 // An overall task with seriesType detected or has `getTargetSeries`, we add 24650 // stub in each pipelines, it will set the overall task dirty when the pipeline 24651 // progress. Moreover, to avoid call the overall task each frame (too frequent), 24652 // we set the pipeline block. 24653 if (seriesType) { 24654 ecModel.eachRawSeriesByType(seriesType, createStub); 24655 } 24656 else if (getTargetSeries) { 24657 getTargetSeries(ecModel, api).each(createStub); 24658 } 24659 // Otherwise, (usually it is legancy case), the overall task will only be 24660 // executed when upstream dirty. Otherwise the progressive rendering of all 24661 // pipelines will be disabled unexpectedly. But it still needs stubs to receive 24662 // dirty info from upsteam. 24663 else { 24664 overallProgress = false; 24665 each$1(ecModel.getSeries(), createStub); 24666 } 24667 24668 function createStub(seriesModel) { 24669 var pipelineId = seriesModel.uid; 24670 var stub = agentStubMap.get(pipelineId); 24671 if (!stub) { 24672 stub = agentStubMap.set(pipelineId, createTask( 24673 {reset: stubReset, onDirty: stubOnDirty} 24674 )); 24675 // When the result of `getTargetSeries` changed, the overallTask 24676 // should be set as dirty and re-performed. 24677 overallTask.dirty(); 24678 } 24679 stub.context = { 24680 model: seriesModel, 24681 overallProgress: overallProgress, 24682 modifyOutputEnd: modifyOutputEnd 24683 }; 24684 stub.agent = overallTask; 24685 stub.__block = overallProgress; 24686 24687 pipe(scheduler, seriesModel, stub); 24688 } 24689 24690 // Clear unused stubs. 24691 var pipelineMap = scheduler._pipelineMap; 24692 agentStubMap.each(function (stub, pipelineId) { 24693 if (!pipelineMap.get(pipelineId)) { 24694 stub.dispose(); 24695 // When the result of `getTargetSeries` changed, the overallTask 24696 // should be set as dirty and re-performed. 24697 overallTask.dirty(); 24698 agentStubMap.removeKey(pipelineId); 24699 } 24700 }); 24701 } 24702 24703 function overallTaskReset(context) { 24704 context.overallReset( 24705 context.ecModel, context.api, context.payload 24706 ); 24707 } 24708 24709 function stubReset(context, upstreamContext) { 24710 return context.overallProgress && stubProgress; 24711 } 24712 24713 function stubProgress() { 24714 this.agent.dirty(); 24715 this.getDownstream().dirty(); 24716 } 24717 24718 function stubOnDirty() { 24719 this.agent && this.agent.dirty(); 24720 } 24721 24722 function seriesTaskPlan(context) { 24723 return context.plan && context.plan( 24724 context.model, context.ecModel, context.api, context.payload 24725 ); 24726 } 24727 24728 function seriesTaskReset(context) { 24729 if (context.useClearVisual) { 24730 context.data.clearAllVisual(); 24731 } 24732 var resetDefines = context.resetDefines = normalizeToArray(context.reset( 24733 context.model, context.ecModel, context.api, context.payload 24734 )); 24735 return resetDefines.length > 1 24736 ? map(resetDefines, function (v, idx) { 24737 return makeSeriesTaskProgress(idx); 24738 }) 24739 : singleSeriesTaskProgress; 24740 } 24741 24742 var singleSeriesTaskProgress = makeSeriesTaskProgress(0); 24743 24744 function makeSeriesTaskProgress(resetDefineIdx) { 24745 return function (params, context) { 24746 var data = context.data; 24747 var resetDefine = context.resetDefines[resetDefineIdx]; 24748 24749 if (resetDefine && resetDefine.dataEach) { 24750 for (var i = params.start; i < params.end; i++) { 24751 resetDefine.dataEach(data, i); 24752 } 24753 } 24754 else if (resetDefine && resetDefine.progress) { 24755 resetDefine.progress(params, data); 24756 } 24757 }; 24758 } 24759 24760 function seriesTaskCount(context) { 24761 return context.data.count(); 24762 } 24763 24764 function pipe(scheduler, seriesModel, task) { 24765 var pipelineId = seriesModel.uid; 24766 var pipeline = scheduler._pipelineMap.get(pipelineId); 24767 !pipeline.head && (pipeline.head = task); 24768 pipeline.tail && pipeline.tail.pipe(task); 24769 pipeline.tail = task; 24770 task.__idxInPipeline = pipeline.count++; 24771 task.__pipeline = pipeline; 24772 } 24773 24774 Scheduler.wrapStageHandler = function (stageHandler, visualType) { 24775 if (isFunction$1(stageHandler)) { 24776 stageHandler = { 24777 overallReset: stageHandler, 24778 seriesType: detectSeriseType(stageHandler) 24779 }; 24780 } 24781 24782 stageHandler.uid = getUID('stageHandler'); 24783 visualType && (stageHandler.visualType = visualType); 24784 24785 return stageHandler; 24786 }; 24787 24788 24789 24790 /** 24791 * Only some legacy stage handlers (usually in echarts extensions) are pure function. 24792 * To ensure that they can work normally, they should work in block mode, that is, 24793 * they should not be started util the previous tasks finished. So they cause the 24794 * progressive rendering disabled. We try to detect the series type, to narrow down 24795 * the block range to only the series type they concern, but not all series. 24796 */ 24797 function detectSeriseType(legacyFunc) { 24798 seriesType = null; 24799 try { 24800 // Assume there is no async when calling `eachSeriesByType`.
24801 legacyFunc(ecModelMock, apiMock); 24802 } 24803 catch (e) { 24804 } 24805 return seriesType; 24806 } 24807 24808 var ecModelMock = {}; 24809 var apiMock = {}; 24810 var seriesType; 24811 24812 mockMethods(ecModelMock, GlobalModel); 24813 mockMethods(apiMock, ExtensionAPI); 24814 ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) { 24815 seriesType = type; 24816 }; 24817 ecModelMock.eachComponent = function (cond) { 24818 if (cond.mainType === 'series' && cond.subType) { 24819 seriesType = cond.subType; 24820 } 24821 }; 24822 24823 function mockMethods(target, Clz) { 24824 for (var name in Clz.prototype) { 24825 // Do not use hasOwnProperty 24826 target[name] = noop; 24827 } 24828 } 24829 24830 /* 24831* Licensed to the Apache Software Foundation (ASF) under one 24832* or more contributor license agreements. See the NOTICE file 24833* distributed with this work for additional information 24834* regarding copyright ownership. The ASF licenses this file 24835* to you under the Apache License, Version 2.0 (the 24836* "License"); you may not use this file except in compliance 24837* with the License. You may obtain a copy of the License at 24838* 24839* http://www.apache.org/licenses/LICENSE-2.0 24840* 24841* Unless required by applicable law or agreed to in writing, 24842* software distributed under the License is distributed on an 24843* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 24844* KIND, either express or implied. See the License for the 24845* specific language governing permissions and limitations 24846* under the License. 24847*/ 24848 24849 var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C','#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF']; 24850 24851 var lightTheme = { 24852 24853 color: colorAll, 24854 24855 colorLayer: [ 24856 ['#37A2DA', '#ffd85c', '#fd7b5f'], 24857 ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], 24858 ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], 24859 colorAll 24860 ] 24861 }; 24862 24863 /* 24864* Licensed to the Apache Software Foundation (ASF) under one 24865* or more contributor license agreements. See the NOTICE file 24866* distributed with this work for additional information 24867* regarding copyright ownership. The ASF licenses this file 24868* to you under the Apache License, Version 2.0 (the 24869* "License"); you may not use this file except in compliance 24870* with the License. You may obtain a copy of the License at 24871* 24872* http://www.apache.org/licenses/LICENSE-2.0 24873* 24874* Unless required by applicable law or agreed to in writing, 24875* software distributed under the License is distributed on an 24876* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 24877* KIND, either express or implied. See the License for the 24878* specific language governing permissions and limitations 24879* under the License. 24880*/ 24881 24882 var contrastColor = '#eee'; 24883 var axisCommon = function () { 24884 return { 24885 axisLine: { 24886 lineStyle: { 24887 color: contrastColor 24888 } 24889 }, 24890 axisTick: { 24891 lineStyle: { 24892 color: contrastColor 24893 } 24894 }, 24895 axisLabel: { 24896 textStyle: { 24897 color: contrastColor 24898 } 24899 }, 24900 splitLine: { 24901 lineStyle: { 24902 type: 'dashed', 24903 color: '#aaa' 24904 } 24905 }, 24906 splitArea: { 24907 areaStyle: { 24908 color: contrastColor 24909 } 24910 } 24911 }; 24912 }; 24913 24914 var colorPalette = ['#dd6b66','#759aa0','#e69d87','#8dc1a9','#ea7e53','#eedd78','#73a373','#73b9bc','#7289ab', '#91ca8c','#f49f42']; 24915 var theme = { 24916 color: colorPalette, 24917 backgroundColor: '#333', 24918 tooltip: { 24919 axisPointer: { 24920 lineStyle: { 24921 color: contrastColor 24922 }, 24923 crossStyle: { 24924 color: contrastColor 24925 } 24926 } 24927 }, 24928 legend: { 24929 textStyle: { 24930 color: contrastColor 24931 } 24932 }, 24933 textStyle: { 24934 color: contrastColor 24935 }, 24936 title: { 24937 textStyle: { 24938 color: contrastColor 24939 } 24940 }, 24941 toolbox: { 24942 iconStyle: { 24943 normal: { 24944 borderColor: contrastColor 24945 } 24946 } 24947 }, 24948 dataZoom: { 24949 textStyle: { 24950 color: contrastColor 24951 } 24952 }, 24953 visualMap: { 24954 textStyle: { 24955 color: contrastColor 24956 } 24957 }, 24958 timeline: { 24959 lineStyle: { 24960 color: contrastColor 24961 }, 24962 itemStyle: { 24963 normal: { 24964 color: colorPalette[1] 24965 } 24966 }, 24967 label: { 24968 normal: { 24969 textStyle: { 24970 color: contrastColor 24971 } 24972 } 24973 }, 24974 controlStyle: { 24975 normal: { 24976 color: contrastColor, 24977 borderColor: contrastColor 24978 } 24979 } 24980 }, 24981 timeAxis: axisCommon(), 24982 logAxis: axisCommon(), 24983 valueAxis: axisCommon(), 24984 categoryAxis: axisCommon(), 24985 24986 line: { 24987 symbol: 'circle' 24988 }, 24989 graph: { 24990 color: colorPalette 24991 }, 24992 gauge: { 24993 title: { 24994 textStyle: { 24995 color: contrastColor 24996 } 24997 } 24998 }, 24999 candlestick: { 25000 itemStyle: { 25001 normal: { 25002 color: '#FD1050', 25003 color0: '#0CF49B', 25004 borderColor: '#FD1050', 25005 borderColor0: '#0CF49B' 25006 } 25007 } 25008 } 25009 }; 25010 theme.categoryAxis.splitLine.show = false; 25011 25012 /* 25013* Licensed to the Apache Software Foundation (ASF) under one 25014* or more contributor license agreements. See the NOTICE file 25015* distributed with this work for additional information 25016* regarding copyright ownership. The ASF licenses this file 25017* to you under the Apache License, Version 2.0 (the 25018* "License");
25018 you may not use this file except in compliance 25019* with the License. You may obtain a copy of the License at 25020* 25021* http://www.apache.org/licenses/LICENSE-2.0 25022* 25023* Unless required by applicable law or agreed to in writing, 25024* software distributed under the License is distributed on an 25025* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 25026* KIND, either express or implied. See the License for the 25027* specific language governing permissions and limitations 25028* under the License. 25029*/ 25030 25031 /** 25032 * This module is imported by echarts directly. 25033 * 25034 * Notice: 25035 * Always keep this file exists for backward compatibility. 25036 * Because before 4.1.0, dataset is an optional component, 25037 * some users may import this module manually. 25038 */ 25039 25040 ComponentModel.extend({ 25041 25042 type: 'dataset', 25043 25044 /** 25045 * @protected 25046 */ 25047 defaultOption: { 25048 25049 // 'row', 'column' 25050 seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, 25051 25052 // null/'auto': auto detect header, see "module:echarts/data/helper/sourceHelper" 25053 sourceHeader: null, 25054 25055 dimensions: null, 25056 25057 source: null 25058 }, 25059 25060 optionUpdated: function () { 25061 detectSourceFormat(this); 25062 } 25063 25064 }); 25065 25066 Component.extend({ 25067 25068 type: 'dataset' 25069 25070 }); 25071 25072 /* 25073* Licensed to the Apache Software Foundation (ASF) under one 25074* or more contributor license agreements. See the NOTICE file 25075* distributed with this work for additional information 25076* regarding copyright ownership. The ASF licenses this file 25077* to you under the Apache License, Version 2.0 (the 25078* "License"); you may not use this file except in compliance 25079* with the License. You may obtain a copy of the License at 25080* 25081* http://www.apache.org/licenses/LICENSE-2.0 25082* 25083* Unless required by applicable law or agreed to in writing, 25084* software distributed under the License is distributed on an 25085* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 25086* KIND, either express or implied. See the License for the 25087* specific language governing permissions and limitations 25088* under the License. 25089*/ 25090 var assert = assert$1; 25091 var each = each$1; 25092 var isFunction = isFunction$1; 25093 var isObject = isObject$1; 25094 var parseClassType = ComponentModel.parseClassType; 25095 25096 var version = '4.1.0'; 25097 25098 var dependencies = { 25099 zrender: '4.0.4' 25100 }; 25101 25102 var TEST_FRAME_REMAIN_TIME = 1; 25103 25104 var PRIORITY_PROCESSOR_FILTER = 1000; 25105 var PRIORITY_PROCESSOR_STATISTIC = 5000; 25106 25107 var PRIORITY_VISUAL_LAYOUT = 1000; 25108 var PRIORITY_VISUAL_GLOBAL = 2000; 25109 var PRIORITY_VISUAL_CHART = 3000; 25110 var PRIORITY_VISUAL_COMPONENT = 4000; 25111// FIXME 25112// necessary? 25113 var PRIORITY_VISUAL_BRUSH = 5000; 25114 25115 var PRIORITY = { 25116 PROCESSOR: { 25117 FILTER: PRIORITY_PROCESSOR_FILTER, 25118 STATISTIC: PRIORITY_PROCESSOR_STATISTIC 25119 }, 25120 VISUAL: { 25121 LAYOUT: PRIORITY_VISUAL_LAYOUT, 25122 GLOBAL: PRIORITY_VISUAL_GLOBAL, 25123 CHART: PRIORITY_VISUAL_CHART, 25124 COMPONENT: PRIORITY_VISUAL_COMPONENT, 25125 BRUSH: PRIORITY_VISUAL_BRUSH 25126 } 25127 }; 25128 25129// Main process have three entries: `setOption`, `dispatchAction` and `resize`, 25130// where they must not be invoked nestedly, except the only case: invoke 25131// dispatchAction with updateMethod "none" in main process. 25132// This flag is used to carry out this rule. 25133// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]). 25134 var IN_MAIN_PROCESS = '__flagInMainProcess'; 25135 var OPTION_UPDATED = '__optionUpdated'; 25136 var ACTION_REG = /^[a-zA-Z0-9_]+$/; 25137 25138 25139 function createRegisterEventWithLowercaseName(method) { 25140 return function (eventName, handler, context) { 25141 // Event name is all lowercase 25142 eventName = eventName && eventName.toLowerCase(); 25143 Eventful.prototype[method].call(this, eventName, handler, context); 25144 }; 25145 } 25146 25147 /** 25148 * @module echarts~MessageCenter 25149 */ 25150 function MessageCenter() { 25151 Eventful.call(this); 25152 } 25153 MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on'); 25154 MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off'); 25155 MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one'); 25156 mixin(MessageCenter, Eventful); 25157 25158 /** 25159 * @module echarts~ECharts 25160 */ 25161 function ECharts(dom, theme$$1, opts) { 25162 opts = opts || {}; 25163 25164 // Get theme by name 25165 if (typeof theme$$1 === 'string') { 25166 theme$$1 = themeStorage[theme$$1]; 25167 } 25168 25169 /** 25170 * @type {string} 25171 */ 25172 this.id; 25173 25174 /** 25175 * Group id 25176 * @type {string} 25177 */ 25178 this.group; 25179 25180 /** 25181 * @type {HTMLElement} 25182 * @private 25183 */ 25184 this._dom = dom; 25185 25186 var defaultRenderer = 'canvas'; 25187 if (__DEV__) { 25188 defaultRenderer = ( 25189 typeof window === 'undefined' ? global : window 25190 ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer; 25191 } 25192 25193 /** 25194 * @type {module:zrender/ZRender} 25195 * @private 25196 */ 25197 var zr = this._zr = init$1(dom, { 25198 renderer: opts.renderer || defaultRenderer, 25199 devicePixelRatio: opts.devicePixelRatio, 25200 width: opts.width, 25201 height: opts.height 25202 }); 25203 25204 /** 25205 * Expect 60 pfs. 25206 * @type {Function} 25207 * @private 25208 */ 25209 this._throttledZrFlush = throttle(bind(zr.flush, zr), 17); 25210 25211 var theme$$1 = clone(theme$$1); 25212 theme$$1 && backwardCompat(theme$$1, true); 25213 /** 25214 * @type {Object} 25215 * @private 25216 */ 25217 this._theme = theme$$1; 25218 25219 /** 25220 * @type {Array.<module:echarts/view/Chart>} 25221 * @private 25222 */ 25223 this._chartsViews = []; 25224 25225 /** 25226 * @type {Object.<string, module:echarts/view/Chart>} 25227 * @private 25228 */ 25229 this._chartsMap = {}; 25230 25231 /** 25232 * @type {Array.<module:echarts/view/Component>} 25233 * @private 25234 */ 25235 this._componentsViews = []; 25236 25237 /** 25238 * @type {Object.<string, module:echarts/view/Component>} 25239 * @private 25240 */ 25241 this._componentsMap = {}; 25242 25243 /** 25244 * @type {module:echarts/CoordinateSystem} 25245 * @private 25246 */ 25247 this._coordSysMgr = new CoordinateSystemManager(); 25248 25249 /** 25250 * @type {module:echarts/ExtensionAPI} 25251 * @private 25252 */ 25253 var api = this._api = createExtensionAPI(this); 25254 25255 // Sort on demand 25256 function prioritySortFunc(a, b) { 25257 return a.__prio - b.__prio; 25258 } 25259 sort(visualFuncs, prioritySortFunc); 25260 sort(dataProcessorFuncs, prioritySortFunc); 25261 25262 /** 25263 * @type {module:echarts/stream/Scheduler} 25264 */ 25265 this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs); 25266 25267 Eventful.call(this); 25268 25269 /** 25270 * @type {module:echarts~MessageCenter} 25271 * @private 25272 */ 25273 this._messageCenter = new MessageCenter(); 25274 25275 // Init mouse events 25276 this._initEvents(); 25277 25278 // In case some people write `window.onresize = chart.resize` 25279 this.resize = bind(this.resize, this); 25280 25281 // Can't dispatch action during rendering procedure 25282 this._pendingActions = []; 25283 25284 zr.animation.on('frame', this._onframe, this); 25285 25286 bindRenderedEvent(zr, this); 25287 25288 // ECharts instance can be used as value. 25289 setAsPrimitive(this); 25290 } 25291 25292 var echartsProto = ECharts.prototype; 25293 25294 echartsProto._onframe = function () { 25295 if (this._disposed) { 25296 return; 25297 } 25298 25299 var scheduler = this._scheduler; 25300 25301 // Lazy update 25302 if (this[OPTION_UPDATED]) { 25303 var silent = this[OPTION_UPDATED].silent; 25304 25305 this[IN_MAIN_PROCESS] = true; 25306 25307 prepare(this); 25308 updateMethods.update.call(this); 25309 25310 this[IN_MAIN_PROCESS] = false; 25311 25312 this[OPTION_UPDATED] = false; 25313 25314 flushPendingActions.call(this, silent); 25315 25316 triggerUpdatedEvent.call(this, silent); 25317 } 25318 // Avoid do both lazy update and progress in one frame. 25319 else if (scheduler.unfinished) { 25320 // Stream progress. 25321 var remainTime = TEST_FRAME_REMAIN_TIME; 25322 var ecModel = this._model; 25323 var api = this._api; 25324 scheduler.unfinished = false; 25325 do { 25326 var startTime = +new Date(); 25327 25328 scheduler.performSeriesTasks(ecModel); 25329 25330 // Currently dataProcessorFuncs do not check threshold. 25331 scheduler.performDataProcessorTasks(ecModel); 25332 25333 updateStreamModes(this, ecModel); 25334
25335 // Do not update coordinate system here. Because that coord system update in 25336 // each frame is not a good user experience. So we follow the rule that 25337 // the extent of the coordinate system is determin in the first frame (the 25338 // frame is executed immedietely after task reset. 25339 // this._coordSysMgr.update(ecModel, api); 25340 25341 // console.log('--- ec frame visual ---', remainTime); 25342 scheduler.performVisualTasks(ecModel); 25343 25344 renderSeries(this, this._model, api, 'remain'); 25345 25346 remainTime -= (+new Date() - startTime); 25347 } 25348 while (remainTime > 0 && scheduler.unfinished); 25349 25350 // Call flush explicitly for trigger finished event. 25351 if (!scheduler.unfinished) { 25352 this._zr.flush(); 25353 } 25354 // Else, zr flushing be ensue within the same frame, 25355 // because zr flushing is after onframe event. 25356 } 25357 }; 25358 25359 /** 25360 * @return {HTMLElement} 25361 */ 25362 echartsProto.getDom = function () { 25363 return this._dom; 25364 }; 25365 25366 /** 25367 * @return {module:zrender~ZRender} 25368 */ 25369 echartsProto.getZr = function () { 25370 return this._zr; 25371 }; 25372 25373 /** 25374 * Usage: 25375 * chart.setOption(option, notMerge, lazyUpdate); 25376 * chart.setOption(option, { 25377 * notMerge: ..., 25378 * lazyUpdate: ..., 25379 * silent: ... 25380 * }); 25381 * 25382 * @param {Object} option 25383 * @param {Object|boolean} [opts] opts or notMerge. 25384 * @param {boolean} [opts.notMerge=false] 25385 * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently. 25386 */ 25387 echartsProto.setOption = function (option, notMerge, lazyUpdate) { 25388 if (__DEV__) { 25389 assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.'); 25390 } 25391 25392 var silent; 25393 if (isObject(notMerge)) { 25394 lazyUpdate = notMerge.lazyUpdate; 25395 silent = notMerge.silent; 25396 notMerge = notMerge.notMerge; 25397 } 25398 25399 this[IN_MAIN_PROCESS] = true; 25400 25401 if (!this._model || notMerge) { 25402 var optionManager = new OptionManager(this._api); 25403 var theme$$1 = this._theme; 25404 var ecModel = this._model = new GlobalModel(null, null, theme$$1, optionManager); 25405 ecModel.scheduler = this._scheduler; 25406 ecModel.init(null, null, theme$$1, optionManager); 25407 } 25408 25409 this._model.setOption(option, optionPreprocessorFuncs); 25410 25411 if (lazyUpdate) { 25412 this[OPTION_UPDATED] = {silent: silent}; 25413 this[IN_MAIN_PROCESS] = false; 25414 } 25415 else { 25416 prepare(this); 25417 25418 updateMethods.update.call(this); 25419 25420 // Ensure zr refresh sychronously, and then pixel in canvas can be 25421 // fetched after `setOption`. 25422 this._zr.flush(); 25423 25424 this[OPTION_UPDATED] = false; 25425 this[IN_MAIN_PROCESS] = false; 25426 25427 flushPendingActions.call(this, silent); 25428 triggerUpdatedEvent.call(this, silent); 25429 } 25430 }; 25431 25432 /** 25433 * @DEPRECATED 25434 */ 25435 echartsProto.setTheme = function () { 25436 console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); 25437 }; 25438 25439 /** 25440 * @return {module:echarts/model/Global} 25441 */ 25442 echartsProto.getModel = function () { 25443 return this._model; 25444 }; 25445 25446 /** 25447 * @return {Object} 25448 */ 25449 echartsProto.getOption = function () { 25450 return this._model && this._model.getOption(); 25451 }; 25452 25453 /** 25454 * @return {number} 25455 */ 25456 echartsProto.getWidth = function () { 25457 return this._zr.getWidth(); 25458 }; 25459 25460 /** 25461 * @return {number} 25462 */ 25463 echartsProto.getHeight = function () { 25464 return this._zr.getHeight(); 25465 }; 25466 25467 /** 25468 * @return {number} 25469 */ 25470 echartsProto.getDevicePixelRatio = function () { 25471 return this._zr.painter.dpr || window.devicePixelRatio || 1; 25472 }; 25473 25474 /** 25475 * Get canvas which has all thing rendered 25476 * @param {Object} opts 25477 * @param {string} [opts.backgroundColor] 25478 * @return {string} 25479 */ 25480 echartsProto.getRenderedCanvas = function (opts) { 25481 if (!env$1.canvasSupported) { 25482 return; 25483 } 25484 opts = opts || {}; 25485 opts.pixelRatio = opts.pixelRatio || 1; 25486 opts.backgroundColor = opts.backgroundColor 25487 || this._model.get('backgroundColor'); 25488 var zr = this._zr; 25489 // var list = zr.storage.getDisplayList(); 25490 // Stop animations 25491 // Never works before in init animation, so remove it. 25492 // zrUtil.each(list, function (el) { 25493 // el.stopAnimation(true); 25494 // }); 25495 return zr.painter.getRenderedCanvas(opts); 25496 }; 25497 25498 /** 25499 * Get svg data url 25500 * @return {string} 25501 */ 25502 echartsProto.getSvgDataUrl = function () { 25503 if (!env$1.svgSupported) { 25504 return; 25505 } 25506 25507 var zr = this._zr; 25508 var list = zr.storage.getDisplayList(); 25509 // Stop animations 25510 each$1(list, function (el) { 25511 el.stopAnimation(true); 25512 }); 25513 25514 return zr.painter.pathToDataUrl(); 25515 }; 25516 25517 /** 25518 * @return {string} 25519 * @param {Object} opts 25520 * @param {string} [opts.type='png'] 25521 * @param {string} [opts.pixelRatio=1] 25522 * @param {string} [opts.backgroundColor] 25523 * @param {string} [opts.excludeComponents] 25524 */ 25525 echartsProto.getDataURL = function (opts) { 25526 opts = opts || {}; 25527 var excludeComponents = opts.excludeComponents; 25528 var ecModel = this._model; 25529 var excludesComponentViews = []; 25530 var self = this; 25531 25532 each(excludeComponents, function (componentType) { 25533 ecModel.eachComponent({ 25534 mainType: componentType 25535 }, function (component) { 25536 var view = self._componentsMap[component.__viewId]; 25537 if (!view.group.ignore) { 25538 excludesComponentViews.push(view); 25539 view.group.ignore = true; 25540 } 25541 }); 25542 }); 25543 25544 var url = this._zr.painter.getType() === 'svg'
25545 ? this.getSvgDataUrl() 25546 : this.getRenderedCanvas(opts).toDataURL( 25547 'image/' + (opts && opts.type || 'png') 25548 ); 25549 25550 each(excludesComponentViews, function (view) { 25551 view.group.ignore = false; 25552 }); 25553 25554 return url; 25555 }; 25556 25557 25558 /** 25559 * @return {string} 25560 * @param {Object} opts 25561 * @param {string} [opts.type='png'] 25562 * @param {string} [opts.pixelRatio=1] 25563 * @param {string} [opts.backgroundColor] 25564 */ 25565 echartsProto.getConnectedDataURL = function (opts) { 25566 if (!env$1.canvasSupported) { 25567 return; 25568 } 25569 var groupId = this.group; 25570 var mathMin = Math.min; 25571 var mathMax = Math.max; 25572 var MAX_NUMBER = Infinity; 25573 if (connectedGroups[groupId]) { 25574 var left = MAX_NUMBER; 25575 var top = MAX_NUMBER; 25576 var right = -MAX_NUMBER; 25577 var bottom = -MAX_NUMBER; 25578 var canvasList = []; 25579 var dpr = (opts && opts.pixelRatio) || 1; 25580 25581 each$1(instances, function (chart, id) { 25582 if (chart.group === groupId) { 25583 var canvas = chart.getRenderedCanvas( 25584 clone(opts) 25585 ); 25586 var boundingRect = chart.getDom().getBoundingClientRect(); 25587 left = mathMin(boundingRect.left, left); 25588 top = mathMin(boundingRect.top, top); 25589 right = mathMax(boundingRect.right, right); 25590 bottom = mathMax(boundingRect.bottom, bottom); 25591 canvasList.push({ 25592 dom: canvas, 25593 left: boundingRect.left, 25594 top: boundingRect.top 25595 }); 25596 } 25597 }); 25598 25599 left *= dpr; 25600 top *= dpr; 25601 right *= dpr; 25602 bottom *= dpr; 25603 var width = right - left; 25604 var height = bottom - top; 25605 var targetCanvas = createCanvas(); 25606 targetCanvas.width = width; 25607 targetCanvas.height = height; 25608 var zr = init$1(targetCanvas); 25609 25610 each(canvasList, function (item) { 25611 var img = new ZImage({ 25612 style: { 25613 x: item.left * dpr - left, 25614 y: item.top * dpr - top, 25615 image: item.dom 25616 } 25617 }); 25618 zr.add(img); 25619 }); 25620 zr.refreshImmediately(); 25621 25622 return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png')); 25623 } 25624 else { 25625 return this.getDataURL(opts); 25626 } 25627 }; 25628 25629 /** 25630 * Convert from logical coordinate system to pixel coordinate system. 25631 * See CoordinateSystem#convertToPixel. 25632 * @param {string|Object} finder 25633 * If string, e.g., 'geo', means {geoIndex: 0}. 25634 * If Object, could contain some of these properties below: 25635 * { 25636 * seriesIndex / seriesId / seriesName, 25637 * geoIndex / geoId, geoName, 25638 * bmapIndex / bmapId / bmapName, 25639 * xAxisIndex / xAxisId / xAxisName, 25640 * yAxisIndex / yAxisId / yAxisName, 25641 * gridIndex / gridId / gridName, 25642 * ... (can be extended) 25643 * } 25644 * @param {Array|number} value 25645 * @return {Array|number} result 25646 */ 25647 echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel'); 25648 25649 /** 25650 * Convert from pixel coordinate system to logical coordinate system. 25651 * See CoordinateSystem#convertFromPixel. 25652 * @param {string|Object} finder 25653 * If string, e.g., 'geo', means {geoIndex: 0}. 25654 * If Object, could contain some of these properties below: 25655 * { 25656 * seriesIndex / seriesId / seriesName, 25657 * geoIndex / geoId / geoName, 25658 * bmapIndex / bmapId / bmapName, 25659 * xAxisIndex / xAxisId / xAxisName, 25660 * yAxisIndex / yAxisId / yAxisName 25661 * gridIndex / gridId / gridName, 25662 * ... (can be extended) 25663 * } 25664 * @param {Array|number} value 25665 * @return {Array|number} result 25666 */ 25667 echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel'); 25668 25669 function doConvertPixel(methodName, finder, value) { 25670 var ecModel = this._model; 25671 var coordSysList = this._coordSysMgr.getCoordinateSystems();
25672 var result; 25673 25674 finder = parseFinder(ecModel, finder); 25675 25676 for (var i = 0; i < coordSysList.length; i++) { 25677 var coordSys = coordSysList[i]; 25678 if (coordSys[methodName] 25679 && (result = coordSys[methodName](ecModel, finder, value)) != null 25680 ) { 25681 return result; 25682 } 25683 } 25684 25685 if (__DEV__) { 25686 console.warn( 25687 'No coordinate system that supports ' + methodName + ' found by the given finder.' 25688 ); 25689 } 25690 } 25691 25692 /** 25693 * Is the specified coordinate systems or components contain the given pixel point. 25694 * @param {string|Object} finder 25695 * If string, e.g., 'geo', means {geoIndex: 0}. 25696 * If Object, could contain some of these properties below: 25697 * { 25698 * seriesIndex / seriesId / seriesName, 25699 * geoIndex / geoId / geoName, 25700 * bmapIndex / bmapId / bmapName, 25701 * xAxisIndex / xAxisId / xAxisName, 25702 * yAxisIndex / yAxisId / yAxisName, 25703 * gridIndex / gridId / gridName, 25704 * ... (can be extended) 25705 * } 25706 * @param {Array|number} value 25707 * @return {boolean} result 25708 */ 25709 echartsProto.containPixel = function (finder, value) { 25710 var ecModel = this._model; 25711 var result; 25712 25713 finder = parseFinder(ecModel, finder); 25714 25715 each$1(finder, function (models, key) { 25716 key.indexOf('Models') >= 0 && each$1(models, function (model) { 25717 var coordSys = model.coordinateSystem; 25718 if (coordSys && coordSys.containPoint) { 25719 result |= !!coordSys.containPoint(value); 25720 } 25721 else if (key === 'seriesModels') { 25722 var view = this._chartsMap[model.__viewId]; 25723 if (view && view.containPoint) { 25724 result |= view.containPoint(value, model); 25725 } 25726 else { 25727 if (__DEV__) { 25728 console.warn(key + ': ' + (view 25729 ? 'The found component do not support containPoint.' 25730 : 'No view mapping to the found component.' 25731 )); 25732 } 25733 } 25734 } 25735 else { 25736 if (__DEV__) { 25737 console.warn(key + ': containPoint is not supported'); 25738 } 25739 } 25740 }, this); 25741 }, this); 25742 25743 return !!result; 25744 }; 25745 25746 /** 25747 * Get visual from series or data. 25748 * @param {string|Object} finder 25749 * If string, e.g., 'series', means {seriesIndex: 0}. 25750 * If Object, could contain some of these properties below: 25751 * { 25752 * seriesIndex / seriesId / seriesName, 25753 * dataIndex / dataIndexInside 25754 * } 25755 * If dataIndex is not specified, series visual will be fetched, 25756 * but not data item visual. 25757 * If all of seriesIndex, seriesId, seriesName are not specified, 25758 * visual will be fetched from first series. 25759 * @param {string} visualType 'color', 'symbol', 'symbolSize' 25760 */ 25761 echartsProto.getVisual = function (finder, visualType) { 25762 var ecModel = this._model; 25763 25764 finder = parseFinder(ecModel, finder, {defaultMainType: 'series'}); 25765 25766 var seriesModel = finder.seriesModel; 25767 25768 if (__DEV__) { 25769 if (!seriesModel) { 25770 console.warn('There is no specified seires model'); 25771 } 25772 } 25773 25774 var data = seriesModel.getData(); 25775 25776 var dataIndexInside = finder.hasOwnProperty('dataIndexInside') 25777 ? finder.dataIndexInside 25778 : finder.hasOwnProperty('dataIndex') 25779 ? data.indexOfRawIndex(finder.dataIndex) 25780 : null; 25781 25782 return dataIndexInside != null 25783 ? data.getItemVisual(dataIndexInside, visualType) 25784 : data.getVisual(visualType); 25785 }; 25786 25787 /** 25788 * Get view of corresponding component model 25789 * @param {module:echarts/model/Component} componentModel 25790 * @return {module:echarts/view/Component} 25791 */ 25792 echartsProto.getViewOfComponentModel = function (componentModel) { 25793 return this._componentsMap[componentModel.__viewId]; 25794 }; 25795 25796 /** 25797 * Get view of corresponding series model 25798 * @param {module:echarts/model/Series} seriesModel 25799 * @return {module:echarts/view/Chart} 25800 */ 25801 echartsProto.getViewOfSeriesModel = function (seriesModel) { 25802 return this._chartsMap[seriesModel.__viewId]; 25803 }; 25804 25805 var updateMethods = { 25806 25807 prepareAndUpdate: function (payload) { 25808 prepare(this); 25809 updateMethods.update.call(this, payload); 25810 }, 25811 25812 /** 25813 * @param {Object} payload 25814 * @private 25815 */ 25816 update: function (payload) { 25817 // console.profile && console.profile('update'); 25818 25819 var ecModel = this._model; 25820 var api = this._api; 25821 var zr = this._zr; 25822 var coordSysMgr = this._coordSysMgr; 25823 var scheduler = this._scheduler; 25824 25825 // update before setOption 25826 if (!ecModel) { 25827 return; 25828 } 25829 25830 scheduler.restoreData(ecModel, payload); 25831 25832 scheduler.performSeriesTasks(ecModel); 25833 25834 // TODO 25835 // Save total ecModel here for undo/redo (after restoring data and before processing data). 25836 // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call. 25837 25838 // Create new coordinate system each update 25839 // In LineView may save the old coordinate system and use it to get the orignal point 25840 coordSysMgr.create(ecModel, api); 25841 25842 scheduler.performDataProcessorTasks(ecModel, payload); 25843 25844 // Current stream render is not supported in data process. So we can update 25845 // stream modes after data processing, where the filtered data is used to 25846 // deteming whether use progressive rendering. 25847 updateStreamModes(this, ecModel); 25848 25849 // We update stream modes before coordinate system updated, then the modes info 25850 // can be fetched when coord sys updating (consider the barGrid extent fix). But 25851 // the drawback is the full coord info can not be fetched. Fortunately this full 25852 // coord is not requied in stream mode updater currently. 25853 coordSysMgr.update(ecModel, api); 25854
25855 clearColorPalette(ecModel); 25856 scheduler.performVisualTasks(ecModel, payload); 25857 25858 render(this, ecModel, api, payload); 25859 25860 // Set background 25861 var backgroundColor = ecModel.get('backgroundColor') || 'transparent'; 25862 25863 // In IE8 25864 if (!env$1.canvasSupported) { 25865 var colorArr = parse(backgroundColor); 25866 backgroundColor = stringify(colorArr, 'rgb'); 25867 if (colorArr[3] === 0) { 25868 backgroundColor = 'transparent'; 25869 } 25870 } 25871 else { 25872 zr.setBackgroundColor(backgroundColor); 25873 } 25874 25875 performPostUpdateFuncs(ecModel, api); 25876 25877 // console.profile && console.profileEnd('update'); 25878 }, 25879 25880 /** 25881 * @param {Object} payload 25882 * @private 25883 */ 25884 updateTransform: function (payload) { 25885 var ecModel = this._model; 25886 var ecIns = this; 25887 var api = this._api; 25888 25889 // update before setOption 25890 if (!ecModel) { 25891 return; 25892 } 25893 25894 // ChartView.markUpdateMethod(payload, 'updateTransform'); 25895 25896 var componentDirtyList = []; 25897 ecModel.eachComponent(function (componentType, componentModel) { 25898 var componentView = ecIns.getViewOfComponentModel(componentModel); 25899 if (componentView && componentView.__alive) { 25900 if (componentView.updateTransform) { 25901 var result = componentView.updateTransform(componentModel, ecModel, api, payload); 25902 result && result.update && componentDirtyList.push(componentView); 25903 } 25904 else { 25905 componentDirtyList.push(componentView); 25906 } 25907 } 25908 }); 25909 25910 var seriesDirtyMap = createHashMap(); 25911 ecModel.eachSeries(function (seriesModel) { 25912 var chartView = ecIns._chartsMap[seriesModel.__viewId]; 25913 if (chartView.updateTransform) { 25914 var result = chartView.updateTransform(seriesModel, ecModel, api, payload); 25915 result && result.update && seriesDirtyMap.set(seriesModel.uid, 1); 25916 } 25917 else { 25918 seriesDirtyMap.set(seriesModel.uid, 1); 25919 } 25920 }); 25921 25922 clearColorPalette(ecModel); 25923 // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. 25924 // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); 25925 this._scheduler.performVisualTasks( 25926 ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap} 25927 ); 25928 25929 // Currently, not call render of components. Geo render cost a lot. 25930 // renderComponents(ecIns, ecModel, api, payload, componentDirtyList); 25931 renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap); 25932 25933 performPostUpdateFuncs(ecModel, this._api); 25934 }, 25935 25936 /** 25937 * @param {Object} payload 25938 * @private 25939 */ 25940 updateView: function (payload) { 25941 var ecModel = this._model; 25942 25943 // update before setOption 25944 if (!ecModel) { 25945 return; 25946 } 25947 25948 Chart.markUpdateMethod(payload, 'updateView'); 25949 25950 clearColorPalette(ecModel); 25951 25952 // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. 25953 this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true}); 25954 25955 render(this, this._model, this._api, payload); 25956 25957 performPostUpdateFuncs(ecModel, this._api); 25958 }, 25959 25960 /** 25961 * @param {Object} payload 25962 * @private 25963 */ 25964 updateVisual: function (payload) { 25965 updateMethods.update.call(this, payload); 25966 25967 // var ecModel = this._model; 25968 25969 // // update before setOption 25970 // if (!ecModel) { 25971 // return; 25972 // } 25973 25974 // ChartView.markUpdateMethod(payload, 'updateVisual'); 25975
25976 // clearColorPalette(ecModel); 25977 25978 // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. 25979 // this._scheduler.performVisualTasks(ecModel, payload, {visualType: 'visual', setDirty: true}); 25980 25981 // render(this, this._model, this._api, payload); 25982 25983 // performPostUpdateFuncs(ecModel, this._api); 25984 }, 25985 25986 /** 25987 * @param {Object} payload 25988 * @private 25989 */ 25990 updateLayout: function (payload) { 25991 updateMethods.update.call(this, payload); 25992 25993 // var ecModel = this._model; 25994 25995 // // update before setOption 25996 // if (!ecModel) { 25997 // return; 25998 // } 25999 26000 // ChartView.markUpdateMethod(payload, 'updateLayout'); 26001 26002 // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. 26003 // // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); 26004 // this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true}); 26005 26006 // render(this, this._model, this._api, payload); 26007 26008 // performPostUpdateFuncs(ecModel, this._api); 26009 } 26010 }; 26011 26012 function prepare(ecIns) { 26013 var ecModel = ecIns._model; 26014 var scheduler = ecIns._scheduler; 26015 26016 scheduler.restorePipelines(ecModel); 26017 26018 scheduler.prepareStageTasks(); 26019 26020 prepareView(ecIns, 'component', ecModel, scheduler); 26021 26022 prepareView(ecIns, 'chart', ecModel, scheduler); 26023 26024 scheduler.plan(); 26025 } 26026 26027 /** 26028 * @private 26029 */ 26030 function updateDirectly(ecIns, method, payload, mainType, subType) { 26031 var ecModel = ecIns._model; 26032 26033 // broadcast 26034 if (!mainType) { 26035 // FIXME 26036 // Chart will not be update directly here, except set dirty. 26037 // But there is no such scenario now. 26038 each(ecIns._componentsViews.concat(ecIns._chartsViews), callView); 26039 return; 26040 } 26041 26042 var query = {}; 26043 query[mainType + 'Id'] = payload[mainType + 'Id']; 26044 query[mainType + 'Index'] = payload[mainType + 'Index']; 26045 query[mainType + 'Name'] = payload[mainType + 'Name']; 26046 26047 var condition = {mainType: mainType, query: query}; 26048 subType && (condition.subType = subType); // subType may be '' by parseClassType; 26049 26050 var excludeSeriesId = payload.excludeSeriesId; 26051 if (excludeSeriesId != null) { 26052 excludeSeriesId = createHashMap(normalizeToArray(excludeSeriesId)); 26053 } 26054 26055 // If dispatchAction before setOption, do nothing. 26056 ecModel && ecModel.eachComponent(condition, function (model) { 26057 if (!excludeSeriesId || excludeSeriesId.get(model.id) == null) { 26058 callView(ecIns[ 26059 mainType === 'series' ? '_chartsMap' : '_componentsMap' 26060 ][model.__viewId]); 26061 } 26062 }, ecIns); 26063 26064 function callView(view) { 26065 view && view.__alive && view[method] && view[method]( 26066 view.__model, ecModel, ecIns._api, payload 26067 ); 26068 } 26069 } 26070 26071 /** 26072 * Resize the chart 26073 * @param {Object} opts 26074 * @param {number} [opts.width] Can be 'auto' (the same as null/undefined) 26075 * @param {number} [opts.height] Can be 'auto' (the same as null/undefined) 26076 * @param {boolean} [opts.silent=false] 26077 */ 26078 echartsProto.resize = function (opts) { 26079 if (__DEV__) { 26080 assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.'); 26081 } 26082 26083 this._zr.resize(opts); 26084 26085 var ecModel = this._model; 26086 26087 // Resize loading effect 26088 this._loadingFX && this._loadingFX.resize(); 26089 26090 if (!ecModel) { 26091 return; 26092 } 26093 26094 var optionChanged = ecModel.resetOption('media'); 26095 26096 var silent = opts && opts.silent; 26097 26098 this[IN_MAIN_PROCESS] = true; 26099 26100 optionChanged && prepare(this); 26101 updateMethods.update.call(this); 26102 26103 this[IN_MAIN_PROCESS] = false; 26104 26105 flushPendingActions.call(this, silent); 26106 26107 triggerUpdatedEvent.call(this, silent); 26108 }; 26109 26110 function updateStreamModes(ecIns, ecModel) {
26111 var chartsMap = ecIns._chartsMap; 26112 var scheduler = ecIns._scheduler; 26113 ecModel.eachSeries(function (seriesModel) { 26114 scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]); 26115 }); 26116 } 26117 26118 /** 26119 * Show loading effect 26120 * @param {string} [name='default'] 26121 * @param {Object} [cfg] 26122 */ 26123 echartsProto.showLoading = function (name, cfg) { 26124 if (isObject(name)) { 26125 cfg = name; 26126 name = ''; 26127 } 26128 name = name || 'default'; 26129 26130 this.hideLoading(); 26131 if (!loadingEffects[name]) { 26132 if (__DEV__) { 26133 console.warn('Loading effects ' + name + ' not exists.'); 26134 } 26135 return; 26136 } 26137 var el = loadingEffects[name](this._api, cfg); 26138 var zr = this._zr; 26139 this._loadingFX = el; 26140 26141 zr.add(el); 26142 }; 26143 26144 /** 26145 * Hide loading effect 26146 */ 26147 echartsProto.hideLoading = function () { 26148 this._loadingFX && this._zr.remove(this._loadingFX); 26149 this._loadingFX = null; 26150 }; 26151 26152 /** 26153 * @param {Object} eventObj 26154 * @return {Object} 26155 */ 26156 echartsProto.makeActionFromEvent = function (eventObj) { 26157 var payload = extend({}, eventObj); 26158 payload.type = eventActionMap[eventObj.type]; 26159 return payload; 26160 }; 26161 26162 /** 26163 * @pubilc 26164 * @param {Object} payload 26165 * @param {string} [payload.type] Action type 26166 * @param {Object|boolean} [opt] If pass boolean, means opt.silent 26167 * @param {boolean} [opt.silent=false] Whether trigger events. 26168 * @param {boolean} [opt.flush=undefined] 26169 * true: Flush immediately, and then pixel in canvas can be fetched 26170 * immediately. Caution: it might affect performance. 26171 * false: Not not flush. 26172 * undefined: Auto decide whether perform flush. 26173 */ 26174 echartsProto.dispatchAction = function (payload, opt) { 26175 if (!isObject(opt)) { 26176 opt = {silent: !!opt}; 26177 } 26178 26179 if (!actions[payload.type]) { 26180 return; 26181 } 26182 26183 // Avoid dispatch action before setOption. Especially in `connect`. 26184 if (!this._model) { 26185 return; 26186 } 26187 26188 // May dispatchAction in rendering procedure 26189 if (this[IN_MAIN_PROCESS]) { 26190 this._pendingActions.push(payload); 26191 return; 26192 } 26193 26194 doDispatchAction.call(this, payload, opt.silent); 26195 26196 if (opt.flush) { 26197 this._zr.flush(true); 26198 } 26199 else if (opt.flush !== false && env$1.browser.weChat) { 26200 // In WeChat embeded browser, `requestAnimationFrame` and `setInterval` 26201 // hang when sliding page (on touch event), which cause that zr does not 26202 // refresh util user interaction finished, which is not expected. 26203 // But `dispatchAction` may be called too frequently when pan on touch 26204 // screen, which impacts performance if do not throttle them. 26205 this._throttledZrFlush(); 26206 } 26207 26208 flushPendingActions.call(this, opt.silent); 26209 26210 triggerUpdatedEvent.call(this, opt.silent); 26211 }; 26212 26213 function doDispatchAction(payload, silent) { 26214 var payloadType = payload.type; 26215 var escapeConnect = payload.escapeConnect; 26216 var actionWrap = actions[payloadType]; 26217 var actionInfo = actionWrap.actionInfo; 26218 26219 var cptType = (actionInfo.update || 'update').split(':'); 26220 var updateMethod = cptType.pop(); 26221 cptType = cptType[0] != null && parseClassType(cptType[0]); 26222 26223 this[IN_MAIN_PROCESS] = true; 26224 26225 var payloads = [payload]; 26226 var batched = false; 26227 // Batch action 26228 if (payload.batch) { 26229 batched = true; 26230 payloads = map(payload.batch, function (item) { 26231 item = defaults(extend({}, item), payload); 26232 item.batch = null; 26233 return item; 26234 }); 26235 } 26236 26237 var eventObjBatch = []; 26238 var eventObj; 26239 var isHighDown = payloadType === 'highlight' || payloadType === 'downplay'; 26240 26241 each(payloads, function (batchItem) { 26242 // Action can specify the event by return it. 26243 eventObj = actionWrap.action(batchItem, this._model, this._api); 26244 // Emit event outside 26245 eventObj = eventObj || extend({}, batchItem); 26246 // Convert type to eventType 26247 eventObj.type = actionInfo.event || eventObj.type; 26248 eventObjBatch.push(eventObj); 26249 26250 // light update does not perform data process, layout and visual. 26251 if (isHighDown) { 26252 // method, payload, mainType, subType 26253 updateDirectly(this, updateMethod, batchItem, 'series'); 26254 } 26255 else if (cptType) { 26256 updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub); 26257 } 26258 }, this); 26259 26260 if (updateMethod !== 'none' && !isHighDown && !cptType) { 26261 // Still dirty 26262 if (this[OPTION_UPDATED]) { 26263 // FIXME Pass payload ? 26264 prepare(this); 26265 updateMethods.update.call(this, payload); 26266 this[OPTION_UPDATED] = false; 26267 } 26268 else { 26269 updateMethods[updateMethod].call(this, payload); 26270 } 26271 } 26272 26273 // Follow the rule of action batch 26274 if (batched) { 26275 eventObj = { 26276 type: actionInfo.event || payloadType, 26277 escapeConnect: escapeConnect, 26278 batch: eventObjBatch 26279 }; 26280 } 26281 else { 26282 eventObj = eventObjBatch[0]; 26283 } 26284 26285 this[IN_MAIN_PROCESS] = false; 26286 26287 !silent && this._messageCenter.trigger(eventObj.type, eventObj); 26288 } 26289 26290 function flushPendingActions(silent) { 26291 var pendingActions = this._pendingActions; 26292 while (pendingActions.length) { 26293 var payload = pendingActions.shift();
26294 doDispatchAction.call(this, payload, silent); 26295 } 26296 } 26297 26298 function triggerUpdatedEvent(silent) { 26299 !silent && this.trigger('updated'); 26300 } 26301 26302 /** 26303 * Event `rendered` is triggered when zr 26304 * rendered. It is useful for realtime 26305 * snapshot (reflect animation). 26306 * 26307 * Event `finished` is triggered when: 26308 * (1) zrender rendering finished. 26309 * (2) initial animation finished. 26310 * (3) progressive rendering finished. 26311 * (4) no pending action. 26312 * (5) no delayed setOption needs to be processed. 26313 */ 26314 function bindRenderedEvent(zr, ecIns) { 26315 zr.on('rendered', function () { 26316 26317 ecIns.trigger('rendered'); 26318 26319 // The `finished` event should not be triggered repeatly, 26320 // so it should only be triggered when rendering indeed happend 26321 // in zrender. (Consider the case that dipatchAction is keep 26322 // triggering when mouse move). 26323 if ( 26324 // Although zr is dirty if initial animation is not finished 26325 // and this checking is called on frame, we also check 26326 // animation finished for robustness. 26327 zr.animation.isFinished() 26328 && !ecIns[OPTION_UPDATED] 26329 && !ecIns._scheduler.unfinished 26330 && !ecIns._pendingActions.length 26331 ) { 26332 ecIns.trigger('finished'); 26333 } 26334 }); 26335 } 26336 26337 /** 26338 * @param {Object} params 26339 * @param {number} params.seriesIndex 26340 * @param {Array|TypedArray} params.data 26341 */ 26342 echartsProto.appendData = function (params) { 26343 var seriesIndex = params.seriesIndex; 26344 var ecModel = this.getModel(); 26345 var seriesModel = ecModel.getSeriesByIndex(seriesIndex); 26346 26347 if (__DEV__) { 26348 assert(params.data && seriesModel); 26349 } 26350 26351 seriesModel.appendData(params); 26352 26353 // Note: `appendData` does not support that update extent of coordinate 26354 // system, util some scenario require that. In the expected usage of 26355 // `appendData`, the initial extent of coordinate system should better 26356 // be fixed by axis `min`/`max` setting or initial data, otherwise if 26357 // the extent changed while `appendData`, the location of the painted 26358 // graphic elements have to be changed, which make the usage of 26359 // `appendData` meaningless. 26360 26361 this._scheduler.unfinished = true; 26362 }; 26363 26364 /** 26365 * Register event 26366 * @method 26367 */ 26368 echartsProto.on = createRegisterEventWithLowercaseName('on'); 26369 echartsProto.off = createRegisterEventWithLowercaseName('off'); 26370 echartsProto.one = createRegisterEventWithLowercaseName('one'); 26371 26372 /** 26373 * Prepare view instances of charts and components 26374 * @param {module:echarts/model/Global} ecModel 26375 * @private 26376 */ 26377 function prepareView(ecIns, type, ecModel, scheduler) { 26378 var isComponent = type === 'component'; 26379 var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews; 26380 var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap; 26381 var zr = ecIns._zr; 26382 var api = ecIns._api; 26383 26384 for (var i = 0; i < viewList.length; i++) { 26385 viewList[i].__alive = false; 26386 } 26387 26388 isComponent 26389 ? ecModel.eachComponent(function (componentType, model) { 26390 componentType !== 'series' && doPrepare(model); 26391 }) 26392 : ecModel.eachSeries(doPrepare); 26393 26394 function doPrepare(model) { 26395 // Consider: id same and type changed. 26396 var viewId = '_ec_' + model.id + '_' + model.type; 26397 var view = viewMap[viewId]; 26398 if (!view) { 26399 var classType = parseClassType(model.type); 26400 var Clazz = isComponent 26401 ? Component.getClass(classType.main, classType.sub) 26402 : Chart.getClass(classType.sub); 26403 26404 if (__DEV__) { 26405 assert(Clazz, classType.sub + ' does not exist.'); 26406 } 26407 26408 view = new Clazz(); 26409 view.init(ecModel, api); 26410 viewMap[viewId] = view; 26411 viewList.push(view); 26412 zr.add(view.group); 26413 } 26414 26415 model.__viewId = view.__id = viewId; 26416 view.__alive = true; 26417 view.__model = model; 26418 view.group.__ecComponentInfo = { 26419 mainType: model.mainType, 26420 index: model.componentIndex 26421 }; 26422 !isComponent && scheduler.prepareView(view, model, ecModel, api); 26423 } 26424 26425 for (var i = 0; i < viewList.length;) { 26426 var view = viewList[i]; 26427 if (!view.__alive) { 26428 !isComponent && view.renderTask.dispose(); 26429 zr.remove(view.group); 26430 view.dispose(ecModel, api); 26431 viewList.splice(i, 1); 26432 delete viewMap[view.__id]; 26433 view.__id = view.group.__ecComponentInfo = null; 26434 } 26435 else { 26436 i++; 26437 } 26438 } 26439 } 26440 26441// /** 26442// * Encode visual infomation from data after data processing 26443// * 26444// * @param {module:echarts/model/Global} ecModel 26445// * @param {object} layout 26446// * @param {boolean} [layoutFilter] `true`: only layout, 26447// * `false`: only not layout, 26448// * `null`/`undefined`: all. 26449// * @param {string} taskBaseTag 26450// * @private 26451// */ 26452// function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) { 26453// each(visualFuncs, function (visual, index) { 26454// var isLayout = visual.isLayout; 26455// if (layoutFilter == null 26456// || (layoutFilter === false && !isLayout) 26457// || (layoutFilter === true && isLayout) 26458// ) { 26459// visual.func(ecModel, api, payload);
26460// } 26461// }); 26462// } 26463 26464 function clearColorPalette(ecModel) { 26465 ecModel.clearColorPalette(); 26466 ecModel.eachSeries(function (seriesModel) { 26467 seriesModel.clearColorPalette(); 26468 }); 26469 } 26470 26471 function render(ecIns, ecModel, api, payload) { 26472 26473 renderComponents(ecIns, ecModel, api, payload); 26474 26475 each(ecIns._chartsViews, function (chart) { 26476 chart.__alive = false; 26477 }); 26478 26479 renderSeries(ecIns, ecModel, api, payload); 26480 26481 // Remove groups of unrendered charts 26482 each(ecIns._chartsViews, function (chart) { 26483 if (!chart.__alive) { 26484 chart.remove(ecModel, api); 26485 } 26486 }); 26487 } 26488 26489 function renderComponents(ecIns, ecModel, api, payload, dirtyList) { 26490 each(dirtyList || ecIns._componentsViews, function (componentView) { 26491 var componentModel = componentView.__model; 26492 componentView.render(componentModel, ecModel, api, payload); 26493 26494 updateZ(componentModel, componentView); 26495 }); 26496 } 26497 26498 /** 26499 * Render each chart and component 26500 * @private 26501 */
26502 function renderSeries(ecIns, ecModel, api, payload, dirtyMap) { 26503 // Render all charts 26504 var scheduler = ecIns._scheduler; 26505 var unfinished; 26506 ecModel.eachSeries(function (seriesModel) { 26507 var chartView = ecIns._chartsMap[seriesModel.__viewId]; 26508 chartView.__alive = true; 26509 26510 var renderTask = chartView.renderTask; 26511 scheduler.updatePayload(renderTask, payload); 26512 26513 if (dirtyMap && dirtyMap.get(seriesModel.uid)) { 26514 renderTask.dirty(); 26515 } 26516 26517 unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask)); 26518 26519 chartView.group.silent = !!seriesModel.get('silent'); 26520 26521 updateZ(seriesModel, chartView); 26522 26523 updateBlend(seriesModel, chartView); 26524 }); 26525 scheduler.unfinished |= unfinished; 26526 26527 // If use hover layer 26528 updateHoverLayerStatus(ecIns._zr, ecModel); 26529 26530 // Add aria 26531 aria(ecIns._zr.dom, ecModel); 26532 } 26533 26534 function performPostUpdateFuncs(ecModel, api) { 26535 each(postUpdateFuncs, function (func) { 26536 func(ecModel, api); 26537 }); 26538 } 26539 26540 26541 var MOUSE_EVENT_NAMES = [ 26542 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 26543 'mousedown', 'mouseup', 'globalout', 'contextmenu' 26544 ]; 26545 26546 /** 26547 * @private 26548 */ 26549 echartsProto._initEvents = function () { 26550 each(MOUSE_EVENT_NAMES, function (eveName) { 26551 this._zr.on(eveName, function (e) { 26552 var ecModel = this.getModel(); 26553 var el = e.target; 26554 var params; 26555 26556 // no e.target when 'globalout'. 26557 if (eveName === 'globalout') { 26558 params = {}; 26559 } 26560 else if (el && el.dataIndex != null) { 26561 var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex); 26562 params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {}; 26563 } 26564 // If element has custom eventData of components 26565 else if (el && el.eventData) { 26566 params = extend({}, el.eventData); 26567 } 26568 26569 if (params) { 26570 params.event = e; 26571 params.type = eveName; 26572 this.trigger(eveName, params); 26573 } 26574 26575 }, this); 26576 }, this); 26577 26578 each(eventActionMap, function (actionType, eventType) { 26579 this._messageCenter.on(eventType, function (event) { 26580 this.trigger(eventType, event); 26581 }, this); 26582 }, this); 26583 }; 26584 26585 /** 26586 * @return {boolean} 26587 */ 26588 echartsProto.isDisposed = function () { 26589 return this._disposed; 26590 }; 26591 26592 /** 26593 * Clear 26594 */ 26595 echartsProto.clear = function () { 26596 this.setOption({ series: [] }, true); 26597 }; 26598 26599 /** 26600 * Dispose instance 26601 */ 26602 echartsProto.dispose = function () { 26603 if (this._disposed) { 26604 if (__DEV__) { 26605 console.warn('Instance ' + this.id + ' has been disposed'); 26606 } 26607 return; 26608 } 26609 this._disposed = true; 26610 26611 setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, ''); 26612 26613 var api = this._api; 26614 var ecModel = this._model; 26615 26616 each(this._componentsViews, function (component) { 26617 component.dispose(ecModel, api); 26618 }); 26619 each(this._chartsViews, function (chart) { 26620 chart.dispose(ecModel, api); 26621 }); 26622 26623 // Dispose after all views disposed 26624 this._zr.dispose(); 26625 26626 delete instances[this.id]; 26627 }; 26628 26629 mixin(ECharts, Eventful); 26630 26631 function updateHoverLayerStatus(zr, ecModel) { 26632 var storage = zr.storage; 26633 var elCount = 0; 26634 storage.traverse(function (el) { 26635 if (!el.isGroup) { 26636 elCount++; 26637 } 26638 }); 26639 if (elCount > ecModel.get('hoverLayerThreshold') && !env$1.node) { 26640 storage.traverse(function (el) { 26641 if (!el.isGroup) { 26642 // Don't switch back. 26643 el.useHoverLayer = true; 26644 } 26645 }); 26646 } 26647 } 26648 26649 /** 26650 * Update chart progressive and blend. 26651 * @param {module:echarts/model/Series|module:echarts/model/Component} model 26652 * @param {module:echarts/view/Component|module:echarts/view/Chart} view 26653 */
26654 function updateBlend(seriesModel, chartView) { 26655 var blendMode = seriesModel.get('blendMode') || null; 26656 if (__DEV__) { 26657 if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') { 26658 console.warn('Only canvas support blendMode'); 26659 } 26660 } 26661 chartView.group.traverse(function (el) { 26662 // FIXME marker and other components 26663 if (!el.isGroup) { 26664 // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender. 26665 if (el.style.blend !== blendMode) { 26666 el.setStyle('blend', blendMode); 26667 } 26668 } 26669 if (el.eachPendingDisplayable) { 26670 el.eachPendingDisplayable(function (displayable) { 26671 displayable.setStyle('blend', blendMode); 26672 }); 26673 } 26674 }); 26675 } 26676 26677 /** 26678 * @param {module:echarts/model/Series|module:echarts/model/Component} model 26679 * @param {module:echarts/view/Component|module:echarts/view/Chart} view 26680 */ 26681 function updateZ(model, view) { 26682 var z = model.get('z'); 26683 var zlevel = model.get('zlevel'); 26684 // Set z and zlevel 26685 view.group.traverse(function (el) { 26686 if (el.type !== 'group') { 26687 z != null && (el.z = z); 26688 zlevel != null && (el.zlevel = zlevel); 26689 } 26690 }); 26691 } 26692 26693 function createExtensionAPI(ecInstance) { 26694 var coordSysMgr = ecInstance._coordSysMgr; 26695 return extend(new ExtensionAPI(ecInstance), { 26696 // Inject methods 26697 getCoordinateSystems: bind( 26698 coordSysMgr.getCoordinateSystems, coordSysMgr 26699 ), 26700 getComponentByElement: function (el) { 26701 while (el) { 26702 var modelInfo = el.__ecComponentInfo; 26703 if (modelInfo != null) { 26704 return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index); 26705 } 26706 el = el.parent; 26707 } 26708 } 26709 }); 26710 } 26711 26712 /** 26713 * @type {Object} key: actionType. 26714 * @inner 26715 */ 26716 var actions = {}; 26717 26718 /** 26719 * Map eventType to actionType 26720 * @type {Object} 26721 */ 26722 var eventActionMap = {}; 26723 26724 /** 26725 * Data processor functions of each stage 26726 * @type {Array.<Object.<string, Function>>} 26727 * @inner 26728 */ 26729 var dataProcessorFuncs = []; 26730 26731 /** 26732 * @type {Array.<Function>} 26733 * @inner 26734 */ 26735 var optionPreprocessorFuncs = []; 26736 26737 /** 26738 * @type {Array.<Function>} 26739 * @inner 26740 */ 26741 var postUpdateFuncs = []; 26742 26743 /** 26744 * Visual encoding functions of each stage 26745 * @type {Array.<Object.<string, Function>>} 26746 */ 26747 var visualFuncs = []; 26748 26749 /** 26750 * Theme storage 26751 * @type {Object.<key, Object>} 26752 */ 26753 var themeStorage = {}; 26754 /** 26755 * Loading effects 26756 */ 26757 var loadingEffects = {}; 26758 26759 var instances = {}; 26760 var connectedGroups = {}; 26761 26762 var idBase = new Date() - 0; 26763 var groupIdBase = new Date() - 0; 26764 var DOM_ATTRIBUTE_KEY = '_echarts_instance_'; 26765 26766 var mapDataStores = {}; 26767 26768 function enableConnect(chart) { 26769 var STATUS_PENDING = 0; 26770 var STATUS_UPDATING = 1; 26771 var STATUS_UPDATED = 2; 26772 var STATUS_KEY = '__connectUpdateStatus'; 26773 26774 function updateConnectedChartsStatus(charts, status) { 26775 for (var i = 0; i < charts.length; i++) { 26776 var otherChart = charts[i]; 26777 otherChart[STATUS_KEY] = status; 26778 } 26779 } 26780 26781 each(eventActionMap, function (actionType, eventType) { 26782 chart._messageCenter.on(eventType, function (event) { 26783 if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) { 26784 if (event && event.escapeConnect) { 26785 return; 26786 } 26787 26788 var action = chart.makeActionFromEvent(event); 26789 var otherCharts = []; 26790 26791 each(instances, function (otherChart) { 26792 if (otherChart !== chart && otherChart.group === chart.group) { 26793 otherCharts.push(otherChart); 26794 } 26795 }); 26796 26797 updateConnectedChartsStatus(otherCharts, STATUS_PENDING); 26798 each(otherCharts, function (otherChart) { 26799 if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
26800 otherChart.dispatchAction(action); 26801 } 26802 }); 26803 updateConnectedChartsStatus(otherCharts, STATUS_UPDATED); 26804 } 26805 }); 26806 }); 26807 } 26808 26809 /** 26810 * @param {HTMLElement} dom 26811 * @param {Object} [theme] 26812 * @param {Object} opts 26813 * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default 26814 * @param {string} [opts.renderer] Currently only 'canvas' is supported. 26815 * @param {number} [opts.width] Use clientWidth of the input `dom` by default. 26816 * Can be 'auto' (the same as null/undefined) 26817 * @param {number} [opts.height] Use clientHeight of the input `dom` by default. 26818 * Can be 'auto' (the same as null/undefined) 26819 */ 26820 function init(dom, theme$$1, opts) { 26821 if (__DEV__) { 26822 // Check version 26823 if ((version$1.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) { 26824 throw new Error( 26825 'zrender/src ' + version$1 26826 + ' is too old for ECharts ' + version 26827 + '. Current version need ZRender ' 26828 + dependencies.zrender + '+' 26829 ); 26830 } 26831 26832 if (!dom) { 26833 throw new Error('Initialize failed: invalid dom.'); 26834 } 26835 } 26836 26837 var existInstance = getInstanceByDom(dom); 26838 if (existInstance) { 26839 if (__DEV__) { 26840 console.warn('There is a chart instance already initialized on the dom.'); 26841 } 26842 return existInstance; 26843 } 26844 26845 if (__DEV__) { 26846 if (isDom(dom) 26847 && dom.nodeName.toUpperCase() !== 'CANVAS' 26848 && ( 26849 (!dom.clientWidth && (!opts || opts.width == null)) 26850 || (!dom.clientHeight && (!opts || opts.height == null)) 26851 ) 26852 ) { 26853 console.warn('Can\'t get dom width or height'); 26854 } 26855 } 26856 26857 var chart = new ECharts(dom, theme$$1, opts); 26858 chart.id = 'ec_' + idBase++; 26859 instances[chart.id] = chart; 26860 26861 setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id); 26862 26863 enableConnect(chart); 26864 26865 return chart; 26866 } 26867 26868 /** 26869 * @return {string|Array.<module:echarts~ECharts>} groupId 26870 */ 26871 function connect(groupId) { 26872 // Is array of charts 26873 if (isArray(groupId)) { 26874 var charts = groupId; 26875 groupId = null; 26876 // If any chart has group 26877 each(charts, function (chart) { 26878 if (chart.group != null) { 26879 groupId = chart.group; 26880 } 26881 }); 26882 groupId = groupId || ('g_' + groupIdBase++); 26883 each(charts, function (chart) { 26884 chart.group = groupId; 26885 }); 26886 } 26887 connectedGroups[groupId] = true; 26888 return groupId; 26889 } 26890 26891 /** 26892 * @DEPRECATED 26893 * @return {string} groupId 26894 */ 26895 function disConnect(groupId) { 26896 connectedGroups[groupId] = false; 26897 } 26898 26899 /** 26900 * @return {string} groupId 26901 */ 26902 var disconnect = disConnect; 26903 26904 /** 26905 * Dispose a chart instance 26906 * @param {module:echarts~ECharts|HTMLDomElement|string} chart 26907 */ 26908 function dispose(chart) { 26909 if (typeof chart === 'string') { 26910 chart = instances[chart]; 26911 } 26912 else if (!(chart instanceof ECharts)){ 26913 // Try to treat as dom 26914 chart = getInstanceByDom(chart); 26915 } 26916 if ((chart instanceof ECharts) && !chart.isDisposed()) { 26917 chart.dispose(); 26918 } 26919 } 26920 26921 /** 26922 * @param {HTMLElement} dom 26923 * @return {echarts~ECharts} 26924 */ 26925 function getInstanceByDom(dom) { 26926 return instances[getAttribute(dom, DOM_ATTRIBUTE_KEY)]; 26927 } 26928 26929 /** 26930 * @param {string} key 26931 * @return {echarts~ECharts} 26932 */ 26933 function getInstanceById(key) { 26934 return instances[key]; 26935 } 26936 26937 /** 26938 * Register theme 26939 */ 26940 function registerTheme(name, theme$$1) { 26941 themeStorage[name] = theme$$1; 26942 } 26943 26944 /** 26945 * Register option preprocessor 26946 * @param {Function} preprocessorFunc 26947 */ 26948 function registerPreprocessor(preprocessorFunc) { 26949 optionPreprocessorFuncs.push(preprocessorFunc); 26950 } 26951 26952 /** 26953 * @param {number} [priority=1000] 26954 * @param {Object|Function} processor 26955 */ 26956 function registerProcessor(priority, processor) { 26957 normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER); 26958 } 26959 26960 /** 26961 * Register postUpdater 26962 * @param {Function} postUpdateFunc 26963 */ 26964 function registerPostUpdate(postUpdateFunc) { 26965 postUpdateFuncs.push(postUpdateFunc); 26966 } 26967 26968 /** 26969 * Usage: 26970 * registerAction('someAction', 'someEvent', function () { ... }); 26971 * registerAction('someAction', function () { ... }); 26972 * registerAction( 26973 * {type: 'someAction', event: 'someEvent', update: 'updateView'}, 26974 * function () { ... } 26975 * ); 26976 * 26977 * @param {(string|Object)} actionInfo 26978 * @param {string} actionInfo.type 26979 * @param {string} [actionInfo.event] 26980 * @param {string} [actionInfo.update] 26981 * @param {string} [eventName] 26982 * @param {Function} action 26983 */ 26984 function registerAction(actionInfo, eventName, action) { 26985 if (typeof eventName === 'function') { 26986 action = eventName; 26987 eventName = ''; 26988 } 26989 var actionType = isObject(actionInfo) 26990 ? actionInfo.type 26991 : ([actionInfo, actionInfo = { 26992 event: eventName 26993 }][0]); 26994 26995 // Event name is all lowercase 26996 actionInfo.event = (actionInfo.event || actionType).toLowerCase(); 26997 eventName = actionInfo.event; 26998 26999 // Validate action type and event name. 27000 assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName)); 27001 27002 if (!actions[actionType]) { 27003 actions[actionType] = {action: action, actionInfo: actionInfo}; 27004 } 27005 eventActionMap[eventName] = actionType; 27006 } 27007 27008 /** 27009 * @param {string} type 27010 * @param {*} CoordinateSystem 27011 */ 27012 function registerCoordinateSystem(type, CoordinateSystem$$1) { 27013 CoordinateSystemManager.register(type, CoordinateSystem$$1); 27014 } 27015 27016 /** 27017 * Get dimensions of specified coordinate system. 27018 * @param {string} type 27019 * @return {Array.<string|Object>} 27020 */ 27021 function getCoordinateSystemDimensions(type) { 27022 var coordSysCreator = CoordinateSystemManager.get(type); 27023 if (coordSysCreator) { 27024 return coordSysCreator.getDimensionsInfo 27025 ? coordSysCreator.getDimensionsInfo() 27026 : coordSysCreator.dimensions.slice(); 27027 } 27028 } 27029 27030 /** 27031 * Layout is a special stage of visual encoding 27032 * Most visual encoding like color are common for different chart 27033 * But each chart has it's own layout algorithm 27034 * 27035 * @param {number} [priority=1000] 27036 * @param {Function} layoutTask 27037 */ 27038 function registerLayout(priority, layoutTask) { 27039 normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout'); 27040 } 27041 27042 /** 27043 * @param {number} [priority=3000] 27044 * @param {module:echarts/stream/Task} visualTask 27045 */ 27046 function registerVisual(priority, visualTask) { 27047 normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual'); 27048 } 27049 27050 /** 27051 * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset} 27052 */ 27053 function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) { 27054 if (isFunction(priority) || isObject(priority)) { 27055 fn = priority; 27056 priority = defaultPriority; 27057 } 27058 27059 if (__DEV__) { 27060 if (isNaN(priority) || priority == null) { 27061 throw new Error('Illegal priority'); 27062 } 27063 // Check duplicate 27064 each(targetList, function (wrap) { 27065 assert(wrap.__raw !== fn); 27066 }); 27067 } 27068 27069 var stageHandler = Scheduler.wrapStageHandler(fn, visualType); 27070 27071 stageHandler.__prio = priority; 27072 stageHandler.__raw = fn; 27073 targetList.push(stageHandler); 27074 27075 return stageHandler; 27076 } 27077 27078 /** 27079 * @param {string} name 27080 */ 27081 function registerLoading(name, loadingFx) { 27082 loadingEffects[name] = loadingFx; 27083 } 27084 27085 /** 27086 * @param {Object} opts 27087 * @param {string} [superClass] 27088 */ 27089 function extendComponentModel(opts/*, superClass*/) { 27090 // var Clazz = ComponentModel; 27091 // if (superClass) { 27092 // var classType = parseClassType(superClass); 27093 // Clazz = ComponentModel.getClass(classType.main, classType.sub, true); 27094 // } 27095 return ComponentModel.extend(opts); 27096 } 27097 27098 /** 27099 * @param {Object} opts 27100 * @param {string} [superClass] 27101 */ 27102 function extendComponentView(opts/*, superClass*/) { 27103 // var Clazz = ComponentView; 27104 // if (superClass) { 27105 // var classType = parseClassType(superClass); 27106 // Clazz = ComponentView.getClass(classType.main, classType.sub, true); 27107 // } 27108 return Component.extend(opts); 27109 } 27110 27111 /** 27112 * @param {Object} opts 27113 * @param {string} [superClass] 27114 */ 27115 function extendSeriesModel(opts/*, superClass*/) { 27116 // var Clazz = SeriesModel; 27117 // if (superClass) { 27118 // superClass = 'series.' + superClass.replace('series.', ''); 27119 // var classType = parseClassType(superClass); 27120 // Clazz = ComponentModel.getClass(classType.main, classType.sub, true); 27121 // } 27122 return SeriesModel.extend(opts); 27123 } 27124 27125 /** 27126 * @param {Object} opts 27127 * @param {string} [superClass] 27128 */ 27129 function extendChartView(opts/*, superClass*/) { 27130 // var Clazz = ChartView; 27131 // if (superClass) { 27132 // superClass = superClass.replace('series.', ''); 27133 // var classType = parseClassType(superClass); 27134 // Clazz = ChartView.getClass(classType.main, true); 27135 // } 27136 return Chart.extend(opts); 27137 } 27138 27139 /** 27140 * ZRender need a canvas context to do measureText. 27141 * But in node environment canvas may be created by node-canvas. 27142 * So we need to specify how to create a canvas instead of using document.createElement('canvas') 27143 * 27144 * Be careful of using it in the browser. 27145 * 27146 * @param {Function} creator 27147 * @example 27148 * var Canvas = require('canvas'); 27149 * var echarts = require('echarts'); 27150 * echarts.setCanvasCreator(function () { 27151 * // Small size is enough. 27152 * return new Canvas(32, 32); 27153 * }); 27154 */ 27155 function setCanvasCreator(creator) { 27156 $override('createCanvas', creator); 27157 } 27158 27159 /** 27160 * @param {string} mapName 27161 * @param {Object|string} geoJson 27162 * @param {Object} [specialAreas] 27163 * 27164 * @example 27165 * $.get('USA.json', function (geoJson) { 27166 * echarts.registerMap('USA', geoJson); 27167 * // Or 27168 * echarts.registerMap('USA', { 27169 * geoJson: geoJson, 27170 * specialAreas: {} 27171 * }) 27172 * }); 27173 */
27174 function registerMap(mapName, geoJson, specialAreas) { 27175 if (geoJson.geoJson && !geoJson.features) { 27176 specialAreas = geoJson.specialAreas; 27177 geoJson = geoJson.geoJson; 27178 } 27179 if (typeof geoJson === 'string') { 27180 geoJson = (typeof JSON !== 'undefined' && JSON.parse) 27181 ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))(); 27182 } 27183 mapDataStores[mapName] = { 27184 geoJson: geoJson, 27185 specialAreas: specialAreas 27186 }; 27187 } 27188 27189 /** 27190 * @param {string} mapName 27191 * @return {Object} 27192 */ 27193 function getMap(mapName) { 27194 return mapDataStores[mapName]; 27195 } 27196 27197 registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor); 27198 registerPreprocessor(backwardCompat); 27199 registerProcessor(PRIORITY_PROCESSOR_STATISTIC, dataStack); 27200 registerLoading('default', loadingDefault); 27201 27202// Default actions 27203 27204 registerAction({ 27205 type: 'highlight', 27206 event: 'highlight', 27207 update: 'highlight' 27208 }, noop); 27209 27210 registerAction({ 27211 type: 'downplay', 27212 event: 'downplay', 27213 update: 'downplay' 27214 }, noop); 27215 27216// Default theme 27217 registerTheme('light', lightTheme); 27218 registerTheme('dark', theme); 27219 27220// For backward compatibility, where the namespace `dataTool` will 27221// be mounted on `echarts` is the extension `dataTool` is imported. 27222 var dataTool = {}; 27223 27224 /* 27225* Licensed to the Apache Software Foundation (ASF) under one 27226* or more contributor license agreements. See the NOTICE file 27227* distributed with this work for additional information 27228* regarding copyright ownership. The ASF licenses this file 27229* to you under the Apache License, Version 2.0 (the 27230* "License"); you may not use this file except in compliance 27231* with the License. You may obtain a copy of the License at 27232* 27233* http://www.apache.org/licenses/LICENSE-2.0 27234* 27235* Unless required by applicable law or agreed to in writing, 27236* software distributed under the License is distributed on an 27237* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 27238* KIND, either express or implied. See the License for the 27239* specific language governing permissions and limitations 27240* under the License. 27241*/ 27242 27243 27244 function defaultKeyGetter(item) { 27245 return item; 27246 } 27247 27248 /** 27249 * @param {Array} oldArr 27250 * @param {Array} newArr 27251 * @param {Function} oldKeyGetter 27252 * @param {Function} newKeyGetter 27253 * @param {Object} [context] Can be visited by this.context in callback. 27254 */ 27255 function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) { 27256 this._old = oldArr; 27257 this._new = newArr; 27258 27259 this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; 27260 this._newKeyGetter = newKeyGetter || defaultKeyGetter; 27261 27262 this.context = context; 27263 } 27264 27265 DataDiffer.prototype = { 27266 27267 constructor: DataDiffer, 27268 27269 /** 27270 * Callback function when add a data 27271 */ 27272 add: function (func) { 27273 this._add = func; 27274 return this; 27275 }, 27276 27277 /** 27278 * Callback function when update a data 27279 */ 27280 update: function (func) { 27281 this._update = func; 27282 return this; 27283 }, 27284 27285 /** 27286 * Callback function when remove a data 27287 */ 27288 remove: function (func) { 27289 this._remove = func; 27290 return this; 27291 }, 27292 27293 execute: function () { 27294 var oldArr = this._old; 27295 var newArr = this._new; 27296 27297 var oldDataIndexMap = {}; 27298 var newDataIndexMap = {}; 27299 var oldDataKeyArr = []; 27300 var newDataKeyArr = []; 27301 var i; 27302 27303 initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this); 27304 initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this); 27305 27306 // Travel by inverted order to make sure order consistency 27307 // when duplicate keys exists (consider newDataIndex.pop() below). 27308 // For performance consideration, these code below do not look neat. 27309 for (i = 0; i < oldArr.length; i++) { 27310 var key = oldDataKeyArr[i]; 27311 var idx = newDataIndexMap[key]; 27312 27313 // idx can never be empty array here. see 'set null' logic below. 27314 if (idx != null) { 27315 // Consider there is duplicate key (for example, use dataItem.name as key). 27316 // We should make sure every item in newArr and oldArr can be visited. 27317 var len = idx.length; 27318 if (len) { 27319 len === 1 && (newDataIndexMap[key] = null); 27320 idx = idx.unshift(); 27321 } 27322 else { 27323 newDataIndexMap[key] = null; 27324 } 27325 this._update && this._update(idx, i); 27326 } 27327 else { 27328 this._remove && this._remove(i); 27329 } 27330 } 27331 27332 for (var i = 0; i < newDataKeyArr.length; i++) { 27333 var key = newDataKeyArr[i]; 27334 if (newDataIndexMap.hasOwnProperty(key)) { 27335 var idx = newDataIndexMap[key]; 27336 if (idx == null) { 27337 continue; 27338 } 27339 // idx can never be empty array here. see 'set null' logic above. 27340 if (!idx.length) { 27341 this._add && this._add(idx); 27342 } 27343 else { 27344 for (var j = 0, len = idx.length; j < len; j++) { 27345 this._add && this._add(idx[j]); 27346 } 27347 } 27348 } 27349 } 27350 } 27351 }; 27352 27353 function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) { 27354 for (var i = 0; i < arr.length; i++) { 27355 // Add prefix to avoid conflict with Object.prototype. 27356 var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i); 27357 var existence = map[key]; 27358 if (existence == null) {
27359 keyArr.push(key); 27360 map[key] = i; 27361 } 27362 else { 27363 if (!existence.length) { 27364 map[key] = existence = [existence]; 27365 } 27366 existence.push(i); 27367 } 27368 } 27369 } 27370 27371 /* 27372* Licensed to the Apache Software Foundation (ASF) under one 27373* or more contributor license agreements. See the NOTICE file 27374* distributed with this work for additional information 27375* regarding copyright ownership. The ASF licenses this file 27376* to you under the Apache License, Version 2.0 (the 27377* "License"); you may not use this file except in compliance 27378* with the License. You may obtain a copy of the License at 27379* 27380* http://www.apache.org/licenses/LICENSE-2.0 27381* 27382* Unless required by applicable law or agreed to in writing, 27383* software distributed under the License is distributed on an 27384* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 27385* KIND, either express or implied. See the License for the 27386* specific language governing permissions and limitations 27387* under the License. 27388*/ 27389 27390 var OTHER_DIMENSIONS = createHashMap([ 27391 'tooltip', 'label', 'itemName', 'itemId', 'seriesName' 27392 ]); 27393 27394 function summarizeDimensions(data) { 27395 var summary = {}; 27396 var encode = summary.encode = {}; 27397 var notExtraCoordDimMap = createHashMap(); 27398 var defaultedLabel = []; 27399 var defaultedTooltip = []; 27400 27401 each$1(data.dimensions, function (dimName) { 27402 var dimItem = data.getDimensionInfo(dimName); 27403 27404 var coordDim = dimItem.coordDim; 27405 if (coordDim) { 27406 if (__DEV__) { 27407 assert$1(OTHER_DIMENSIONS.get(coordDim) == null); 27408 } 27409 var coordDimArr = encode[coordDim]; 27410 if (!encode.hasOwnProperty(coordDim)) { 27411 coordDimArr = encode[coordDim] = []; 27412 } 27413 coordDimArr[dimItem.coordDimIndex] = dimName; 27414 27415 if (!dimItem.isExtraCoord) { 27416 notExtraCoordDimMap.set(coordDim, 1); 27417 27418 // Use the last coord dim (and label friendly) as default label, 27419 // because when dataset is used, it is hard to guess which dimension 27420 // can be value dimension. If both show x, y on label is not look good, 27421 // and conventionally y axis is focused more. 27422 if (mayLabelDimType(dimItem.type)) { 27423 defaultedLabel[0] = dimName; 27424 } 27425 } 27426 if (dimItem.defaultTooltip) { 27427 defaultedTooltip.push(dimName); 27428 } 27429 } 27430 27431 OTHER_DIMENSIONS.each(function (v, otherDim) { 27432 var otherDimArr = encode[otherDim]; 27433 if (!encode.hasOwnProperty(otherDim)) { 27434 otherDimArr = encode[otherDim] = []; 27435 } 27436 27437 var dimIndex = dimItem.otherDims[otherDim]; 27438 if (dimIndex != null && dimIndex !== false) { 27439 otherDimArr[dimIndex] = dimItem.name; 27440 } 27441 }); 27442 }); 27443 27444 var dataDimsOnCoord = []; 27445 var encodeFirstDimNotExtra = {}; 27446 27447 notExtraCoordDimMap.each(function (v, coordDim) { 27448 var dimArr = encode[coordDim]; 27449 // ??? FIXME extra coord should not be set in dataDimsOnCoord. 27450 // But should fix the case that radar axes: simplify the logic 27451 // of `completeDimension`, remove `extraPrefix`. 27452 encodeFirstDimNotExtra[coordDim] = dimArr[0]; 27453 // Not necessary to remove duplicate, because a data 27454 // dim canot on more than one coordDim. 27455 dataDimsOnCoord = dataDimsOnCoord.concat(dimArr); 27456 }); 27457 27458 summary.dataDimsOnCoord = dataDimsOnCoord; 27459 summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra; 27460 27461 var encodeLabel = encode.label; 27462 // FIXME `encode.label` is not recommanded, because formatter can not be set 27463 // in this way. Use label.formatter instead. May be remove this approach someday. 27464 if (encodeLabel && encodeLabel.length) { 27465 defaultedLabel = encodeLabel.slice(); 27466 } 27467 27468 var encodeTooltip = encode.tooltip; 27469 if (encodeTooltip && encodeTooltip.length) { 27470 defaultedTooltip = encodeTooltip.slice(); 27471 } 27472 else if (!defaultedTooltip.length) { 27473 defaultedTooltip = defaultedLabel.slice(); 27474 } 27475 27476 encode.defaultedLabel = defaultedLabel; 27477 encode.defaultedTooltip = defaultedTooltip; 27478 27479 return summary; 27480 } 27481 27482 function getDimensionTypeByAxis(axisType) { 27483 return axisType === 'category' 27484 ? 'ordinal' 27485 : axisType === 'time' 27486 ? 'time' 27487 : 'float'; 27488 } 27489 27490 function mayLabelDimType(dimType) { 27491 // In most cases, ordinal and time do not suitable for label. 27492 // Ordinal info can be displayed on axis. Time is too long. 27493 return !(dimType === 'ordinal' || dimType === 'time'); 27494 } 27495 27496// function findTheLastDimMayLabel(data) { 27497// // Get last value dim 27498// var dimensions = data.dimensions.slice(); 27499// var valueType; 27500// var valueDim; 27501// while (dimensions.length && ( 27502// valueDim = dimensions.pop(), 27503// valueType = data.getDimensionInfo(valueDim).type,
27504// valueType === 'ordinal' || valueType === 'time' 27505// )) {} // jshint ignore:line 27506// return valueDim; 27507// } 27508 27509 /* 27510* Licensed to the Apache Software Foundation (ASF) under one 27511* or more contributor license agreements. See the NOTICE file 27512* distributed with this work for additional information 27513* regarding copyright ownership. The ASF licenses this file 27514* to you under the Apache License, Version 2.0 (the 27515* "License"); you may not use this file except in compliance 27516* with the License. You may obtain a copy of the License at 27517* 27518* http://www.apache.org/licenses/LICENSE-2.0 27519* 27520* Unless required by applicable law or agreed to in writing, 27521* software distributed under the License is distributed on an 27522* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 27523* KIND, either express or implied. See the License for the 27524* specific language governing permissions and limitations 27525* under the License. 27526*/ 27527 27528 /** 27529 * List for data storage 27530 * @module echarts/data/List 27531 */ 27532 27533 var isObject$4 = isObject$1; 27534 27535 var UNDEFINED = 'undefined'; 27536 27537// Use prefix to avoid index to be the same as otherIdList[idx], 27538// which will cause weird udpate animation. 27539 var ID_PREFIX = 'e\0\0'; 27540 27541 var dataCtors = { 27542 'float': typeof Float64Array === UNDEFINED 27543 ? Array : Float64Array, 27544 'int': typeof Int32Array === UNDEFINED 27545 ? Array : Int32Array, 27546 // Ordinal data type can be string or int 27547 'ordinal': Array, 27548 'number': Array, 27549 'time': Array 27550 }; 27551 27552// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is 27553// different from the Ctor of typed array. 27554 var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array; 27555 var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array; 27556 27557 function getIndicesCtor(list) { 27558 // The possible max value in this._indicies is always this._rawCount despite of filtering. 27559 return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array; 27560 } 27561 27562 function cloneChunk(originalChunk) { 27563 var Ctor = originalChunk.constructor; 27564 // Only shallow clone is enough when Array. 27565 return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk); 27566 } 27567 27568 var TRANSFERABLE_PROPERTIES = [ 27569 'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', 27570 '_rawData', '_chunkSize', '_chunkCount', '_dimValueGetter', 27571 '_count', '_rawCount', '_nameDimIdx', '_idDimIdx' 27572 ]; 27573 var CLONE_PROPERTIES = [ 27574 '_extent', '_approximateExtent', '_rawExtent' 27575 ]; 27576 27577 function transferProperties(target, source) { 27578 each$1(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) { 27579 if (source.hasOwnProperty(propName)) { 27580 target[propName] = source[propName]; 27581 } 27582 }); 27583 27584 target.__wrappedMethods = source.__wrappedMethods; 27585 27586 each$1(CLONE_PROPERTIES, function (propName) { 27587 target[propName] = clone(source[propName]); 27588 }); 27589 27590 target._calculationInfo = extend(source._calculationInfo); 27591 } 27592 27593 27594 27595 27596 27597 /** 27598 * @constructor 27599 * @alias module:echarts/data/List 27600 * 27601 * @param {Array.<string|Object>} dimensions 27602 * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...]. 27603 * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius 27604 * Spetial fields: { 27605 * ordinalMeta: <module:echarts/data/OrdinalMeta> 27606 * createInvertedIndices: <boolean> 27607 * } 27608 * @param {module:echarts/model/Model} hostModel 27609 */ 27610 var List = function (dimensions, hostModel) { 27611 27612 dimensions = dimensions || ['x', 'y']; 27613 27614 var dimensionInfos = {}; 27615 var dimensionNames = []; 27616 var invertedIndicesMap = {}; 27617 27618 for (var i = 0; i < dimensions.length; i++) { 27619 // Use the original dimensions[i], where other flag props may exists. 27620 var dimensionInfo = dimensions[i]; 27621 27622 if (isString(dimensionInfo)) { 27623 dimensionInfo = {name: dimensionInfo}; 27624 } 27625 27626 var dimensionName = dimensionInfo.name; 27627 dimensionInfo.type = dimensionInfo.type || 'float'; 27628 if (!dimensionInfo.coordDim) { 27629 dimensionInfo.coordDim = dimensionName; 27630 dimensionInfo.coordDimIndex = 0; 27631 } 27632 27633 dimensionInfo.otherDims = dimensionInfo.otherDims || {}; 27634 dimensionNames.push(dimensionName); 27635 dimensionInfos[dimensionName] = dimensionInfo; 27636 27637 dimensionInfo.index = i; 27638 27639 if (dimensionInfo.createInvertedIndices) { 27640 invertedIndicesMap[dimensionName] = []; 27641 } 27642 } 27643 27644 /** 27645 * @readOnly 27646 * @type {Array.<string>} 27647 */ 27648 this.dimensions = dimensionNames; 27649 27650 /** 27651 * Infomation of each data dimension, like data type. 27652 * @type {Object} 27653 */ 27654 this._dimensionInfos = dimensionInfos; 27655 27656 /** 27657 * @type {module:echarts/model/Model} 27658 */ 27659 this.hostModel = hostModel; 27660 27661 /** 27662 * @type {module:echarts/model/Model} 27663 */ 27664 this.dataType; 27665 27666 /** 27667 * Indices stores the indices of data subset after filtered. 27668 * This data subset will be used in chart. 27669 * @type {Array.<number>} 27670 * @readOnly 27671 */ 27672 this._indices = null; 27673 27674 this._count = 0; 27675 this._rawCount = 0; 27676 27677 /** 27678 * Data storage 27679 * @type {Object.<key, Array.<TypedArray|Array>>} 27680 * @private 27681 */ 27682 this._storage = {}; 27683 27684 /** 27685 * @type {Array.<string>} 27686 */ 27687 this._nameList = []; 27688 /** 27689 * @type {Array.<string>} 27690 */ 27691 this._idList = []; 27692 27693 /** 27694 * Models of data option is stored sparse for optimizing memory cost 27695 * @type {Array.<module:echarts/model/Model>} 27696 * @private 27697 */ 27698 this._optionModels = []; 27699 27700 /** 27701 * Global visual properties after visual coding 27702 * @type {Object} 27703 * @private 27704 */ 27705 this._visual = {}; 27706 27707 /** 27708 * Globel layout properties. 27709 * @type {Object} 27710 * @private 27711 */ 27712 this._layout = {}; 27713 27714 /** 27715 * Item visual properties after visual coding 27716 * @type {Array.<Object>} 27717 * @private 27718 */ 27719 this._itemVisuals = []; 27720 27721 /** 27722 * Key: visual type, Value: boolean 27723 * @type {Object} 27724 * @readOnly 27725 */ 27726 this.hasItemVisual = {}; 27727 27728 /** 27729 * Item layout properties after layout 27730 * @type {Array.<Object>} 27731 * @private 27732 */ 27733 this._itemLayouts = []; 27734 27735 /** 27736 * Graphic elemnents 27737 * @type {Array.<module:zrender/Element>} 27738 * @private 27739 */ 27740 this._graphicEls = []; 27741 27742 /** 27743 * Max size of each chunk. 27744 * @type {number} 27745 * @private 27746 */ 27747 this._chunkSize = 1e5; 27748 27749 /** 27750 * @type {number} 27751 * @private 27752 */ 27753 this._chunkCount = 0; 27754 27755 /** 27756 * @type {Array.<Array|Object>} 27757 * @private 27758 */ 27759 this._rawData; 27760 27761 /** 27762 * Raw extent will not be cloned, but only transfered. 27763 * It will not be calculated util needed. 27764 * key: dim, 27765 * value: {end: number, extent: Array.<number>} 27766 * @type {Object} 27767 * @private 27768 */ 27769 this._rawExtent = {}; 27770 27771 /** 27772 * @type {Object} 27773 * @private 27774 */ 27775 this._extent = {}; 27776 27777 /** 27778 * key: dim 27779 * value: extent 27780 * @type {Object} 27781 * @private 27782 */ 27783 this._approximateExtent = {}; 27784 27785 /** 27786 * Cache summary info for fast visit. See "dimensionHelper". 27787 * @type {Object} 27788 * @private 27789 */ 27790 this._dimensionsSummary = summarizeDimensions(this); 27791 27792 /** 27793 * @type {Object.<Array|TypedArray>} 27794 * @private 27795 */ 27796 this._invertedIndicesMap = invertedIndicesMap; 27797 27798 /** 27799 * @type {Object} 27800 * @private 27801 */ 27802 this._calculationInfo = {}; 27803 }; 27804 27805 var listProto = List.prototype; 27806 27807 listProto.type = 'list'; 27808 27809 /** 27810 * If each data item has it's own option 27811 * @type {boolean} 27812 */ 27813 listProto.hasItemOption = true; 27814 27815 /** 27816 * Get dimension name 27817 * @param {string|number} dim 27818 * Dimension can be concrete names like x, y, z, lng, lat, angle, radius 27819 * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radiu
27819s' 27820 * @return {string} Concrete dim name. 27821 */ 27822 listProto.getDimension = function (dim) { 27823 if (!isNaN(dim)) { 27824 dim = this.dimensions[dim] || dim; 27825 } 27826 return dim; 27827 }; 27828 27829 /** 27830 * Get type and calculation info of particular dimension 27831 * @param {string|number} dim 27832 * Dimension can be concrete names like x, y, z, lng, lat, angle, radius 27833 * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' 27834 */ 27835 listProto.getDimensionInfo = function (dim) { 27836 // Do not clone, because there may be categories in dimInfo. 27837 return this._dimensionInfos[this.getDimension(dim)]; 27838 }; 27839 27840 /** 27841 * @return {Array.<string>} concrete dimension name list on coord. 27842 */ 27843 listProto.getDimensionsOnCoord = function () { 27844 return this._dimensionsSummary.dataDimsOnCoord.slice(); 27845 }; 27846 27847 /** 27848 * @param {string} coordDim 27849 * @param {number} [idx] A coordDim may map to more than one data dim. 27850 * If idx is `true`, return a array of all mapped dims. 27851 * If idx is not specified, return the first dim not extra. 27852 * @return {string|Array.<string>} concrete data dim. 27853 * If idx is number, and not found, return null/undefined. 27854 * If idx is `true`, and not found, return empty array (always return array). 27855 */ 27856 listProto.mapDimension = function (coordDim, idx) { 27857 var dimensionsSummary = this._dimensionsSummary; 27858 27859 if (idx == null) { 27860 return dimensionsSummary.encodeFirstDimNotExtra[coordDim]; 27861 } 27862 27863 var dims = dimensionsSummary.encode[coordDim]; 27864 return idx === true 27865 // always return array if idx is `true` 27866 ? (dims || []).slice() 27867 : (dims && dims[idx]); 27868 }; 27869 27870 /** 27871 * Initialize from data 27872 * @param {Array.<Object|number|Array>} data source or data or data provider. 27873 * @param {Array.<string>} [nameLIst] The name of a datum is used on data diff and 27874 * defualt label/tooltip. 27875 * A name can be specified in encode.itemName, 27876 * or dataItem.name (only for series option data), 27877 * or provided in nameList from outside. 27878 * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number 27879 */ 27880 listProto.initData = function (data, nameList, dimValueGetter) { 27881 27882 var notProvider = Source.isInstance(data) || isArrayLike(data); 27883 if (notProvider) { 27884 data = new DefaultDataProvider(data, this.dimensions.length); 27885 } 27886 27887 if (__DEV__) { 27888 if (!notProvider && (typeof data.getItem != 'function' || typeof data.count != 'function')) { 27889 throw new Error('Inavlid data provider.'); 27890 } 27891 } 27892 27893 this._rawData = data; 27894 27895 // Clear 27896 this._storage = {}; 27897 this._indices = null; 27898 27899 this._nameList = nameList || []; 27900 27901 this._idList = []; 27902 27903 this._nameRepeatCount = {}; 27904 27905 if (!dimValueGetter) { 27906 this.hasItemOption = false; 27907 } 27908 27909 /** 27910 * @readOnly 27911 */ 27912 this.defaultDimValueGetter = defaultDimValueGetters[ 27913 this._rawData.getSource().sourceFormat 27914 ]; 27915 27916 // Default dim value getter 27917 this._dimValueGetter = dimValueGetter = dimValueGetter 27918 || this.defaultDimValueGetter; 27919 27920 // Reset raw extent. 27921 this._rawExtent = {}; 27922 27923 this._initDataFromProvider(0, data.count()); 27924 27925 // If data has no item option. 27926 if (data.pure) { 27927 this.hasItemOption = false; 27928 } 27929 }; 27930 27931 listProto.getProvider = function () { 27932 return this._rawData; 27933 }; 27934 27935 listProto.appendData = function (data) { 27936 if (__DEV__) { 27937 assert$1(!this._indices, 'appendData can only be called on raw data.'); 27938 } 27939 27940 var rawData = this._rawData; 27941 var start = this.count(); 27942 rawData.appendData(data); 27943 var end = rawData.count(); 27944 if (!rawData.persistent) { 27945 end += start; 27946 } 27947 this._initDataFromProvider(start, end); 27948 }; 27949 27950 listProto._initDataFromProvider = function (start, end) { 27951 // Optimize. 27952 if (start >= end) { 27953 return; 27954 } 27955 27956 var chunkSize = this._chunkSize; 27957 var rawData = this._rawData; 27958 var storage = this._storage; 27959 var dimensions = this.dimensions; 27960 var dimLen = dimensions.length; 27961 var dimensionInfoMap = this._dimensionInfos; 27962 var nameList = this._nameList; 27963 var idList = this._idList; 27964 var rawExtent = this._rawExtent; 27965 var nameRepeatCount = this._nameRepeatCount = {}; 27966 var nameDimIdx; 27967 27968 var chunkCount = this._chunkCount; 27969 var lastChunkIndex = chunkCount - 1; 27970 for (var i = 0; i < dimLen; i++) { 27971 var dim = dimensions[i]; 27972 if (!rawExtent[dim]) { 27973 rawExtent[dim] = getInitialExtent(); 27974 } 27975 27976 var dimInfo = dimensionInfoMap[dim]; 27977 if (dimInfo.otherDims.itemName === 0) { 27978 nameDimIdx = this._nameDimIdx = i; 27979 } 27980 if (dimInfo.otherDims.itemId === 0) { 27981 this._idDimIdx = i; 27982 } 27983 var DataCtor = dataCtors[dimInfo.type]; 27984 27985 if (!storage[dim]) { 27986 storage[dim] = []; 27987 } 27988 var resizeChunkArray = storage[dim][lastChunkIndex]; 27989 if (resizeChunkArray && resizeChunkArray.length < chunkSize) { 27990 var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize)); 27991 // The cost of the copy is probably inconsiderable 27992 // within the initial chunkSize. 27993 for (var j = 0; j < resizeChunkArray.length; j++) { 27994 newStore[j] = resizeChunkArray[j]; 27995 } 27996 storage[dim][lastChunkIndex] = newStore; 27997 } 27998 27999 // Create new chunks. 28000 for (var k = chunkCount * chunkSize; k < end; k += chunkSize) { 28001 storage[dim].push(new DataCtor(Math.min(end - k, chunkSize))); 28002 } 28003 this._chunkCount = storage[dim].length; 28004 } 28005 28006 var dataItem = new Array(dimLen); 28007 for (var idx = start; idx < end; idx++) { 28008 // NOTICE: Try not to write things into dataItem 28009 dataItem = rawData.getItem(idx, dataItem); 28010 // Each data item is value 28011 // [1, 2] 28012 // 2 28013 // Bar chart, line chart which uses category axis 28014 // only gives the 'y' value. 'x' value is the indices of c
28014ategory 28015 // Use a tempValue to normalize the value to be a (x, y) value 28016 var chunkIndex = Math.floor(idx / chunkSize); 28017 var chunkOffset = idx % chunkSize; 28018 28019 // Store the data by dimensions 28020 for (var k = 0; k < dimLen; k++) { 28021 var dim = dimensions[k]; 28022 var dimStorage = storage[dim][chunkIndex]; 28023 // PENDING NULL is empty or zero 28024 var val = this._dimValueGetter(dataItem, dim, idx, k); 28025 dimStorage[chunkOffset] = val; 28026 28027 var dimRawExtent = rawExtent[dim]; 28028 if (val < dimRawExtent[0]) { 28029 dimRawExtent[0] = val; 28030 } 28031 if (val > dimRawExtent[1]) { 28032 dimRawExtent[1] = val; 28033 } 28034 } 28035 28036 // ??? FIXME not check by pure but sourceFormat? 28037 // TODO refactor these logic. 28038 if (!rawData.pure) { 28039 var name = nameList[idx]; 28040 28041 if (dataItem && name == null) { 28042 // If dataItem is {name: ...}, it has highest priority. 28043 // That is appropriate for many common cases. 28044 if (dataItem.name != null) { 28045 // There is no other place to persistent dataItem.name, 28046 // so save it to nameList. 28047 nameList[idx] = name = dataItem.name; 28048 } 28049 else if (nameDimIdx != null) { 28050 var nameDim = dimensions[nameDimIdx]; 28051 var nameDimChunk = storage[nameDim][chunkIndex]; 28052 if (nameDimChunk) { 28053 name = nameDimChunk[chunkOffset]; 28054 var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta; 28055 if (ordinalMeta && ordinalMeta.categories.length) { 28056 name = ordinalMeta.categories[name]; 28057 } 28058 } 28059 } 28060 } 28061 28062 // Try using the id in option 28063 // id or name is used on dynamical data, mapping old and new items. 28064 var id = dataItem == null ? null : dataItem.id; 28065 28066 if (id == null && name != null) { 28067 // Use name as id and add counter to avoid same name 28068 nameRepeatCount[name] = nameRepeatCount[name] || 0; 28069 id = name; 28070 if (nameRepeatCount[name] > 0) { 28071 id += '__ec__' + nameRepeatCount[name]; 28072 } 28073 nameRepeatCount[name]++; 28074 } 28075 id != null && (idList[idx] = id); 28076 } 28077 } 28078 28079 if (!rawData.persistent && rawData.clean) { 28080 // Clean unused data if data source is typed array. 28081 rawData.clean(); 28082 } 28083 28084 this._rawCount = this._count = end; 28085 28086 // Reset data extent 28087 this._extent = {}; 28088 28089 prepareInvertedIndex(this); 28090 }; 28091 28092 function prepareInvertedIndex(list) { 28093 var invertedIndicesMap = list._invertedIndicesMap; 28094 each$1(invertedIndicesMap, function (invertedIndices, dim) { 28095 var dimInfo = list._dimensionInfos[dim]; 28096 28097 // Currently, only dimensions that has ordinalMeta can create inverted indices. 28098 var ordinalMeta = dimInfo.ordinalMeta; 28099 if (ordinalMeta) { 28100 invertedIndices = invertedIndicesMap[dim] = new CtorUint32Array( 28101 ordinalMeta.categories.length 28102 ); 28103 // The default value of TypedArray is 0. To avoid miss 28104 // mapping to 0, we should set it as NaN. 28105 for (var i = 0; i < invertedIndices.length; i++) { 28106 invertedIndices[i] = NaN; 28107 } 28108 for (var i = 0; i < list._count; i++) { 28109 // Only support the case that all values are distinct. 28110 invertedIndices[list.get(dim, i)] = i; 28111 } 28112 } 28113 }); 28114 } 28115 28116 function getRawValueFromStore(list, dimIndex, rawIndex) { 28117 var val; 28118 if (dimIndex != null) { 28119 var chunkSize = list._chunkSize; 28120 var chunkIndex = Math.floor(rawIndex / chunkSize); 28121 var chunkOffset = rawIndex % chunkSize; 28122 var dim = list.dimensions[dimIndex]; 28123 var chunk = list._storage[dim][chunkIndex]; 28124 if (chunk) { 28125 val = chunk[chunkOffset]; 28126 var ordinalMeta = list._dimensionInfos[dim].ordinalMeta; 28127 if (ordinalMeta && ordinalMeta.categories.length) { 28128 val = ordinalMeta.categories[val]; 28129 } 28130 } 28131 } 28132 return val; 28133 } 28134 28135 /** 28136 * @return {number} 28137 */ 28138 listProto.count = function () { 28139 return this._count; 28140 }; 28141 28142 listProto.getIndices = function () { 28143 var newIndices; 28144 28145 var indices = this._indices; 28146 if (indices) { 28147 var Ctor = indices.constructor; 28148 var thisCount = this._count; 28149 // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`. 28150 if (Ctor === Array) { 28151 newIndices = new Ctor(thisCount); 28152 for (var i = 0; i < thisCount; i++) { 28153 newIndices[i] = indices[i]; 28154 } 28155 } 28156 else { 28157 newIndices = new Ctor(indices.buffer, 0, thisCount); 28158 } 28159 } 28160 else { 28161 var Ctor = getIndicesCtor(this); 28162 var newIndices = new Ctor(this.count()); 28163 for (var i = 0; i < newIndices.length; i++) { 28164 newIndices[i] = i; 28165 } 28166 } 28167 28168 return newIndices; 28169 }; 28170 28171 /** 28172 * Get value. Return NaN if idx is out of range. 28173 * @param {string} dim Dim must be concrete name. 28174 * @param {number} idx 28175 * @param {boolean} stack 28176 * @return {number} 28177 */ 28178 listProto.get = function (dim, idx /*, stack */) { 28179 if (!(idx >= 0 && idx < this._count)) { 28180 return NaN; 28181 } 28182 var storage = this._storage; 28183 if (!storage[dim]) { 28184 // TODO Warn ? 28185 return NaN; 28186 } 28187 28188 idx = this.getRawIndex(idx); 28189 28190 var chunkIndex = Math.floor(idx / this._chunkSize); 28191 var chunkOffset = idx % this._chunkSize; 28192 28193 var chunkStore = storage[dim][chunkIndex]; 28194 var value = chunkStore[chunkOffset]; 28195 // FIXME ordinal data type is not stackable 28196 // if (stack) { 28197 // var dimensionInfo = this._dimensionInfos[dim]; 28198 // if (dimensionInfo && dimensionInfo.stackable) { 28199 // var stackedOn = this.stackedOn; 28200 // while (stackedOn) { 28201 // // Get no stacked data of stacked on 28202 // var stackedValue = stackedOn.get(dim, idx); 28203 // // Considering positive stack, negative stack and empty data 28204 // if ((value >= 0 && stackedValue > 0) // Positive stack 28205 // || (value <= 0 && stackedValue < 0) // Negative stack 28206 // ) { 28207 // value += stackedValue; 28208 // } 28209 // stackedOn = stackedOn.stackedOn; 28210 // } 28211 // } 28212 // } 28213 28214 return value; 28215 }; 28216 28217 /** 28218 * @param {string} dim concrete dim 28219 * @param {number} rawIndex 28220 * @return {number|string} 28221 */ 28222 listProto.getByRawIndex = function (dim, rawIdx) { 28223 if (!(rawIdx >= 0 && rawIdx < this._rawCount)) { 28224 return NaN; 28225 } 28226 var dimStore = this._storage[dim]; 28227 if (!dimStore) { 28228 // TODO Warn ? 28229 return NaN; 28230 } 28231 28232 var chunkIndex = Math.floor(rawIdx / this._chunkSize); 28233 var chunkOffset = rawIdx % this._chunkSize; 28234 var chunkStore = dimStore[chunkIndex]; 28235 return chunkStore[chunkOffset]; 28236 }; 28237 28238 /** 28239 * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange). 28240 * Hack a much simpler _getFast 28241 * @private 28242 */ 28243 listProto._getFast = function (dim, rawIdx) { 28244 var chunkIndex = Math.floor(rawIdx / this._chunkSize); 28245 var chunkOffset = rawIdx % this._chunkSize; 28246 var chunkStore = this._storage[dim][chunkIndex]; 28247 return chunkStore[chunkOffset]; 28248 }; 28249 28250 /** 28251 * Get value for multi dimensions. 28252 * @param {Array.<string>} [dimensions] If ignored, using all dimensions. 28253 * @param {number} idx 28254 * @return {number} 28255 */ 28256 listProto.getValues = function (dimensions, idx /*, stack */) { 28257 var values = []; 28258 28259 if (!isArray(dimensions)) { 28260 // stack = idx; 28261 idx = dimensions; 28262 dimensions = this.dimensions; 28263 } 28264 28265 for (var i = 0, len = dimensions.length; i < len; i++) { 28266 values.push(this.get(dimensions[i], idx /*, stack */)); 28267 } 28268 28269 return values; 28270 }; 28271 28272 /** 28273 * If value is NaN. Inlcuding '-' 28274 * Only check the coord dimensions. 28275 * @param {string} dim 28276 * @param {number} idx 28277 * @return {number} 28278 */ 28279 listProto.hasValue = function (idx) { 28280 var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord; 28281 var dimensionInfos = this._dimensionInfos; 28282 for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) { 28283 if (
28284 // Ordinal type can be string or number 28285 dimensionInfos[dataDimsOnCoord[i]].type !== 'ordinal' 28286 // FIXME check ordinal when using index? 28287 && isNaN(this.get(dataDimsOnCoord[i], idx)) 28288 ) { 28289 return false; 28290 } 28291 } 28292 return true; 28293 }; 28294 28295 /** 28296 * Get extent of data in one dimension 28297 * @param {string} dim 28298 * @param {boolean} stack 28299 */ 28300 listProto.getDataExtent = function (dim /*, stack */) { 28301 // Make sure use concrete dim as cache name. 28302 dim = this.getDimension(dim); 28303 var dimData = this._storage[dim]; 28304 var initialExtent = getInitialExtent(); 28305 28306 // stack = !!((stack || false) && this.getCalculationInfo(dim)); 28307 28308 if (!dimData) { 28309 return initialExtent; 28310 } 28311 28312 // Make more strict checkings to ensure hitting cache. 28313 var currEnd = this.count(); 28314 // var cacheName = [dim, !!stack].join('_'); 28315 // var cacheName = dim; 28316 28317 // Consider the most cases when using data zoom, `getDataExtent` 28318 // happened before filtering. We cache raw extent, which is not 28319 // necessary to be cleared and recalculated when restore data. 28320 var useRaw = !this._indices; // && !stack; 28321 var dimExtent; 28322 28323 if (useRaw) { 28324 return this._rawExtent[dim].slice(); 28325 } 28326 dimExtent = this._extent[dim]; 28327 if (dimExtent) { 28328 return dimExtent.slice(); 28329 } 28330 dimExtent = initialExtent; 28331 28332 var min = dimExtent[0]; 28333 var max = dimExtent[1]; 28334 28335 for (var i = 0; i < currEnd; i++) { 28336 // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i)); 28337 var value = this._getFast(dim, this.getRawIndex(i)); 28338 value < min && (min = value); 28339 value > max && (max = value); 28340 } 28341 28342 dimExtent = [min, max]; 28343 28344 this._extent[dim] = dimExtent; 28345 28346 return dimExtent; 28347 }; 28348 28349 /** 28350 * Optimize for the scenario that data is filtered by a given extent. 28351 * Consider that if data amount is more than hundreds of thousand, 28352 * extent calculation will cost more than 10ms and the cache will 28353 * be erased because of the filtering. 28354 */ 28355 listProto.getApproximateExtent = function (dim /*, stack */) { 28356 dim = this.getDimension(dim); 28357 return this._approximateExtent[dim] || this.getDataExtent(dim /*, stack */); 28358 }; 28359 28360 listProto.setApproximateExtent = function (extent, dim /*, stack */) { 28361 dim = this.getDimension(dim); 28362 this._approximateExtent[dim] = extent.slice(); 28363 }; 28364 28365 /** 28366 * @param {string} key 28367 * @return {*} 28368 */ 28369 listProto.getCalculationInfo = function (key) { 28370 return this._calculationInfo[key]; 28371 }; 28372 28373 /** 28374 * @param {string|Object} key or k-v object 28375 * @param {*} [value] 28376 */ 28377 listProto.setCalculationInfo = function (key, value) { 28378 isObject$4(key) 28379 ? extend(this._calculationInfo, key) 28380 : (this._calculationInfo[key] = value); 28381 }; 28382 28383 /** 28384 * Get sum of data in one dimension 28385 * @param {string} dim 28386 */ 28387 listProto.getSum = function (dim /*, stack */) { 28388 var dimData = this._storage[dim]; 28389 var sum = 0; 28390 if (dimData) { 28391 for (var i = 0, len = this.count(); i < len; i++) { 28392 var value = this.get(dim, i /*, stack */); 28393 if (!isNaN(value)) { 28394 sum += value; 28395 } 28396 } 28397 } 28398 return sum; 28399 }; 28400 28401 /** 28402 * Get median of data in one dimension 28403 * @param {string} dim 28404 */ 28405 listProto.getMedian = function (dim /*, stack */) { 28406 var dimDataArray = []; 28407 // map all data of one dimension 28408 this.each(dim, function (val, idx) { 28409 if (!isNaN(val)) { 28410 dimDataArray.push(val); 28411 } 28412 }); 28413 28414 // TODO 28415 // Use quick select? 28416 28417 // immutability & sort 28418 var sortedDimDataArray = [].concat(dimDataArray).sort(function(a, b) { 28419 return a - b; 28420 }); 28421 var len = this.count(); 28422 // calculate median 28423 return len === 0 ? 0 : 28424 len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : 28425 (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2; 28426 }; 28427 28428// /** 28429// * Retreive the index with given value 28430// * @param {string} dim Concrete dimension. 28431// * @param {number} value 28432// * @return {number} 28433// */ 28434// Currently incorrect: should return dataIndex but not rawIndex. 28435// Do not fix it until this method is to be used somewhere. 28436// FIXME Precision of float value 28437// listProto.indexOf = function (dim, value) { 28438// var storage = this._storage; 28439// var dimData = storage[dim]; 28440// var chunkSize = this._chunkSize; 28441// if (dimData) { 28442// for (var i = 0, len = this.count(); i < len; i++) { 28443// var chunkIndex = Math.floor(i / chunkSize); 28444// var chunkOffset = i % chunkSize; 28445// if (dimData[chunkIndex][chunkOffset] === value) { 28446// return i; 28447// } 28448// } 28449// } 28450// return -1; 28451// }; 28452 28453 /** 28454 * Only support the dimension which inverted index created. 28455 * Do not support other cases until required. 28456 * @param {string} concrete dim 28457 * @param {number|string} value 28458 * @return {number} rawIndex 28459 */ 28460 listProto.rawIndexOf = function (dim, value) { 28461 var invertedIndices = dim && this._invertedIndicesMap[dim]; 28462 if (__DEV__) { 28463 if (!invertedIndices) { 28464 throw new Error('Do not supported yet'); 28465 } 28466 } 28467 var rawIndex = invertedIndices[value]; 28468 if (rawIndex == null || isNaN(rawIndex)) { 28469 return -1; 28470 } 28471 return rawIndex; 28472 }; 28473 28474 /** 28475 * Retreive the index with given name 28476 * @param {number} idx 28477 * @param {number} name 28478 * @return {number} 28479 */ 28480 listProto.indexOfName = function (name) { 28481 for (var i = 0, len = this.count(); i < len; i++) { 28482 if (this.getName(i) === name) { 28483 return i; 28484 } 28485 } 28486 28487 return -1; 28488 }; 28489 28490 /** 28491 * Retreive the index with given raw data index 28492 * @param {number} idx 28493 * @param {number} name 28494 * @return {number} 28495 */ 28496 listProto.indexOfRawIndex = function (rawIndex) { 28497 if (!this._indices) { 28498 return rawIndex; 28499 } 28500 28501 if (rawIndex >= this._rawCount || rawIndex < 0) { 28502 return -1; 28503 } 28504 28505 // Indices are ascending 28506 var indices = this._indices; 28507 28508 // If rawIndex === dataIndex 28509 var rawDataIndex = indices[rawIndex]; 28510 if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) { 28511 return rawIndex; 28512 } 28513 28514 var left = 0; 28515 var right = this._count - 1; 28516 while (left <= right) { 28517 var mid = (left + right) / 2 | 0; 28518 if (indices[mid] < rawIndex) { 28519 left = mid + 1; 28520 } 28521 else if (indices[mid] > rawIndex) { 28522 right = mid - 1; 28523 } 28524 else { 28525 return mid; 28526 } 28527 } 28528 return -1; 28529 }; 28530 28531 /** 28532 * Retreive the index of nearest value 28533 * @param {string} dim 28534 * @param {number} value 28535 * @param {number} [maxDistance=Infinity] 28536 * @return {Array.<number>} Considere multiple points has the same value. 28537 */ 28538 listProto.indicesOfNearest = function (dim, value, maxDistance) { 28539 var storage = this._storage; 28540 var dimData = storage[dim]; 28541 var nearestIndices = []; 28542 28543 if (!dimData) { 28544 return nearestIndices; 28545 } 28546 28547 if (maxDistance == null) { 28548 maxDistance = Infinity; 28549 } 28550 28551 var minDist = Number.MAX_VALUE; 28552 var minDiff = -1; 28553 for (var i = 0, len = this.count(); i < len; i++) { 28554 var diff = value - this.get(dim, i /*, stack */); 28555 var dist = Math.abs(diff); 28556 if (diff <= maxDistance && dist <= minDist) { 28557 // For the case of two data are same on xAxis, which has sequence data. 28558 // Show the nearest index 28559 // https://github.com/ecomfe/echarts/issues/2869 28560 if (dist < minDist || (diff >= 0 && minDiff < 0)) { 28561 minDist = dist; 28562 minDiff = diff; 28563 nearestIndices.length = 0; 28564 } 28565 nearestIndices.push(i); 28566 } 28567 } 28568 return nearestIndices; 28569 }; 28570 28571 /** 28572 * Get raw data index 28573 * @param {number} idx 28574 * @return {number} 28575 */ 28576 listProto.getRawIndex = getRawIndexWithoutIndices; 28577 28578 function getRawIndexWithoutIndices(idx) { 28579 return idx; 28580 } 28581 28582 function getRawIndexWithIndices(idx) { 28583 if (idx < this._count && idx >= 0) { 28584 return this._indices[idx]; 28585 } 28586 return -1; 28587 } 28588 28589 /** 28590 * Get raw data item 28591 * @param {number} idx 28592 * @return {number} 28593 */ 28594 listProto.getRawDataItem = function (idx) { 28595 if (!this._rawData.persistent) { 28596 var val = []; 28597 for (var i = 0; i < this.dimensions.length; i++) { 28598 var dim = this.dimensions[i]; 28599 val.push(this.get(dim, idx)); 28600 } 28601 return val; 28602 } 28603 else { 28604 return this._rawData.getItem(this.getRawIndex(idx)); 28605 } 28606 }; 28607 28608 /** 28609 * @param {number} idx 28610 * @param {boolean} [notDefaultIdx=false] 28611 * @return {string} 28612 */ 28613 listProto.getName = function (idx) { 28614 var rawIndex = this.getRawIndex(idx); 28615 return this._nameList[rawIndex] 28616 || getRawValueFromStore(this, this._nameDimIdx, rawIndex) 28617 || ''; 28618 }; 28619 28620 /** 28621 * @param {number} idx 28622 * @param {boolean} [notDefaultIdx=false] 28623 * @return {string} 28624 */ 28625 listProto.getId = function (idx) { 28626 return getId(this, this.getRawIndex(idx)); 28627 }; 28628 28629 function getId(list, rawIndex) { 28630 var id = list._idList[rawIndex]; 28631 if (id == null) { 28632 id = getRawValueFromStore(list, list._idDimIdx, rawIndex); 28633 } 28634 if (id == null) { 28635 // FIXME Check the usage in graph, should not use prefix. 28636 id = ID_PREFIX + rawIndex; 28637 } 28638 return id; 28639 } 28640 28641 function normalizeDimensions(dimensions) { 28642 if (!isArray(dimensions)) { 28643 dimensions = [dimensions]; 28644 } 28645 return dimensions; 28646 } 28647 28648 function validateDimensions(list, dims) { 28649 for (var i = 0; i < dims.length; i++) { 28650 // stroage may be empty when no data, so use 28651 // dimensionInfos to check. 28652 if (!list._dimensionInfos[dims[i]]) { 28653 console.error('Unkown dimension ' + dims[i]); 28654 } 28655 } 28656 } 28657 28658 /** 28659 * Data iteration 28660 * @param {string|Array.<string>} 28661 * @param {Function} cb 28662 * @param {*} [context=this] 28663 * 28664 * @example 28665 * list.each('x', function (x, idx) {}); 28666 * list.each(['x', 'y'], function (x, y, idx) {}); 28667 * list.each(function (idx) {}) 28668 */ 28669 listProto.each = function (dims, cb, context, contextCompat) { 28670 'use strict'; 28671 28672 if (!this._count) { 28673 return; 28674 } 28675 28676 if (typeof dims === 'function') { 28677 contextCompat = context; 28678 context = cb; 28679 cb = dims; 28680 dims = []; 28681 } 28682 28683 // contextCompat just for compat echarts3 28684 context = context || contextCompat || this; 28685 28686 dims = map(normalizeDimensions(dims), this.getDimension, this); 28687 28688 if (__DEV__) { 28689 validateDimensions(this, dims); 28690 } 28691 28692 var dimSize = dims.length; 28693 28694 for (var i = 0; i < this.count(); i++) { 28695 // Simple optimization 28696 switch (dimSize) { 28697 case 0: 28698 cb.call(context, i); 28699 break; 28700 case 1: 28701 cb.call(context, this.get(dims[0], i), i); 28702 break; 28703 case 2: 28704 cb.call(context, this.get(dims[0], i), this.get(dims[1], i), i); 28705 break; 28706 default: 28707 var k = 0; 28708 var value = []; 28709 for (; k < dimSize; k++) { 28710 value[k] = this.get(dims[k], i); 28711 } 28712 // Index 28713 value[k] = i; 28714 cb.apply(context, value); 28715 } 28716 } 28717 }; 28718 28719 /** 28720 * Data filter 28721 * @param {string|Array.<string>} 28722 * @param {Function} cb 28723 * @param {*} [context=this] 28724 */ 28725 listProto.filterSelf = function (dimensions, cb, context, contextCompat) { 28726 'use strict'; 28727 28728 if (!this._count) { 28729 return; 28730 } 28731 28732 if (typeof dimensions === 'function') { 28733 contextCompat = context; 28734 context = cb; 28735 cb = dimensions; 28736 dimensions = []; 28737 } 28738 28739 // contextCompat just for compat echarts3 28740 context = context || contextCompat || this; 28741 28742 dimensions = map( 28743 normalizeDimensions(dimensions), this.getDimension, this 28744 ); 28745 28746 if (__DEV__) { 28747 validateDimensions(this, dimensions); 28748 } 28749 28750 28751 var count = this.count(); 28752 var Ctor = getIndicesCtor(this); 28753 var newIndices = new Ctor(count); 28754 var value = []; 28755 var dimSize = dimensions.length; 28756 28757 var offset = 0; 28758 var dim0 = dimensions[0]; 28759 28760 for (var i = 0; i < count; i++) { 28761 var keep; 28762 var rawIdx = this.getRawIndex(i); 28763 // Simple optimization 28764 if (dimSize === 0) { 28765 keep = cb.call(context, i); 28766 } 28767 else if (dimSize === 1) { 28768 var val = this._getFast(dim0, rawIdx); 28769 keep = cb.call(context, val, i); 28770 } 28771 else { 28772 for (var k = 0; k < dimSize; k++) { 28773 value[k] = this._getFast(dim0, rawIdx); 28774 } 28775 value[k] = i; 28776 keep = cb.apply(context, value); 28777 } 28778 if (keep) { 28779 newIndices[offset++] = rawIdx; 28780 } 28781 } 28782 28783 // Set indices after filtered. 28784 if (offset < count) { 28785 this._indices = newIndices; 28786 } 28787 this._count = offset; 28788 // Reset data extent 28789 this._extent = {}; 28790 28791 this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; 28792 28793 return this; 28794 }; 28795 28796 /** 28797 * Select data in range. (For optimization of filter) 28798 * (Manually inline code, support 5 million data filtering in data zoom.) 28799 */ 28800 listProto.selectRange = function (range) { 28801 'use strict'; 28802 28803 if (!this._count) { 28804 return; 28805 } 28806 28807 var dimensions = []; 28808 for (var dim in range) { 28809 if (range.hasOwnProperty(dim)) { 28810 dimensions.push(dim); 28811 } 28812 } 28813 28814 if (__DEV__) { 28815 validateDimensions(this, dimensions); 28816 } 28817 28818 var dimSize = dimensions.length; 28819 if (!dimSize) { 28820 return; 28821 } 28822 28823 var originalCount = this.count(); 28824 var Ctor = getIndicesCtor(this); 28825 var newIndices = new Ctor(originalCount); 28826 28827 var offset = 0; 28828 var dim0 = dimensions[0]; 28829 28830 var min = range[dim0][0]; 28831 var max = range[dim0][1]; 28832 28833 var quickFinished = false; 28834 if (!this._indices) { 28835 // Extreme optimization for common case. About 2x faster in chrome. 28836 var idx = 0; 28837 if (di
28837mSize === 1) { 28838 var dimStorage = this._storage[dimensions[0]]; 28839 for (var k = 0; k < this._chunkCount; k++) { 28840 var chunkStorage = dimStorage[k]; 28841 var len = Math.min(this._count - k * this._chunkSize, this._chunkSize); 28842 for (var i = 0; i < len; i++) { 28843 var val = chunkStorage[i]; 28844 // NaN will not be filtered. Consider the case, in line chart, empty 28845 // value indicates the line should be broken. But for the case like 28846 // scatter plot, a data item with empty value will not be rendered, 28847 // but the axis extent may be effected if some other dim of the data 28848 // item has value. Fortunately it is not a significant negative effect. 28849 if ( 28850 (val >= min && val <= max) || isNaN(val) 28851 ) { 28852 newIndices[offset++] = idx; 28853 } 28854 idx++; 28855 } 28856 } 28857 quickFinished = true; 28858 } 28859 else if (dimSize === 2) { 28860 var dimStorage = this._storage[dim0]; 28861 var dimStorage2 = this._storage[dimensions[1]]; 28862 var min2 = range[dimensions[1]][0]; 28863 var max2 = range[dimensions[1]][1]; 28864 for (var k = 0; k < this._chunkCount; k++) { 28865 var chunkStorage = dimStorage[k]; 28866 var chunkStorage2= dimStorage2[k]; 28867 var len = Math.min(this._count - k * this._chunkSize, this._chunkSize); 28868 for (var i = 0; i < len; i++) { 28869 var val = chunkStorage[i]; 28870 var val2 = chunkStorage2[i]; 28871 // Do not filter NaN, see comment above. 28872 if (( 28873 (val >= min && val <= max) || isNaN(val) 28874 ) 28875 && ( 28876 (val2 >= min2 && val2 <= max2) || isNaN(val2) 28877 ) 28878 ) { 28879 newIndices[offset++] = idx; 28880 } 28881 idx++; 28882 } 28883 } 28884 quickFinished = true; 28885 } 28886 } 28887 if (!quickFinished) { 28888 if (dimSize === 1) { 28889 for (var i = 0; i < originalCount; i++) { 28890 var rawIndex = this.getRawIndex(i); 28891 var val = this._getFast(dim0, rawIndex); 28892 // Do not filter NaN, see comment above. 28893 if ( 28894 (val >= min && val <= max) || isNaN(val) 28895 ) { 28896 newIndices[offset++] = rawIndex; 28897 } 28898 } 28899 } 28900 else { 28901 for (var i = 0; i < originalCount; i++) { 28902 var keep = true; 28903 var rawIndex = this.getRawIndex(i); 28904 for (var k = 0; k < dimSize; k++) { 28905 var dimk = dimensions[k]; 28906 var val = this._getFast(dim, rawIndex); 28907 // Do not filter NaN, see comment above. 28908 if (val < range[dimk][0] || val > range[dimk][1]) { 28909 keep = false; 28910 } 28911 } 28912 if (keep) { 28913 newIndices[offset++] = this.getRawIndex(i); 28914 } 28915 } 28916 } 28917 } 28918 28919 // Set indices after filtered. 28920 if (offset < originalCount) { 28921 this._indices = newIndices; 28922 } 28923 this._count = offset; 28924 // Reset data extent 28925 this._extent = {}; 28926 28927 this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; 28928 28929 return this; 28930 }; 28931 28932 /** 28933 * Data mapping to a plain array 28934 * @param {string|Array.<string>} [dimensions] 28935 * @param {Function} cb 28936 * @param {*} [context=this] 28937 * @return {Array} 28938 */ 28939 listProto.mapArray = function (dimensions, cb, context, contextCompat) { 28940 'use strict'; 28941 28942 if (typeof dimensions === 'function') { 28943 contextCompat = context; 28944 context = cb; 28945 cb = dimensions; 28946 dimensions = []; 28947 } 28948 28949 // contextCompat just for compat echarts3 28950 context = context || contextCompat || this; 28951 28952 var result = []; 28953 this.each(dimensions, function () { 28954 result.push(cb && cb.apply(this, arguments)); 28955 }, context); 28956 return result; 28957 }; 28958 28959// Data in excludeDimensions is copied, otherwise transfered. 28960 function cloneListForMapAndSample(original, excludeDimensions) { 28961 var allDimensions = original.dimensions; 28962 var list = new List( 28963 map(allDimensions, original.getDimensionInfo, original), 28964 original.hostModel 28965 ); 28966 // FIXME If needs stackedOn, value may already been stacked 28967 transferProperties(list, original); 28968 28969 var storage = list._storage = {}; 28970 var originalStorage = original._storage; 28971 28972 // Init storage 28973 for (var i = 0; i < allDimensions.length; i++) { 28974 var dim = allDimensions[i]; 28975 if (originalStorage[dim]) { 28976 // Notice that we do not reset invertedIndicesMap here, becuase 28977 // there is no scenario of mapping or sampling ordinal dimension. 28978 if (indexOf(excludeDimensions, dim) >= 0) { 28979 storage[dim] = cloneDimStore(originalStorage[dim]); 28980 list._rawExtent[dim] = getInitialExtent(); 28981 list._extent[dim] = null; 28982 } 28983 else { 28984 // Direct reference for other dimensions 28985 storage[dim] = originalStorage[dim]; 28986 } 28987 } 28988 } 28989 return list; 28990 } 28991 28992 function cloneDimStore(originalDimStore) { 28993 var newDimStore = new Array(originalDimStore.length); 28994 for (var j = 0; j < originalDimStore.length; j++) { 28995 newDimStore[j] = cloneChunk(originalDimStore[j]); 28996 } 28997 return newDimStore; 28998 } 28999 29000 function getInitialExtent() { 29001 return [Infinity, -Infinity]; 29002 } 29003 29004 /** 29005 * Data mapping to a new List with given dimensions 29006 * @param {string|Array.<string>} dimensions 29007 * @param {Function} cb 29008 * @param {*} [context=this] 29009 * @return {Array} 29010 */ 29011 listProto.map = function (dimensions, cb, context, contextCompat) { 29012 'use strict'; 29013 29014 // contextCompat just for compat echarts3 29015 context = context || contextCompat || this; 29016 29017 dimensions = map( 29018 normalizeDimensions(dimensions), this.getDimension, this 29019 ); 29020 29021 if (__DEV__) { 29022 validateDimensions(this, dimensions); 29023 } 29024 29025 var list = cloneListForMapAndSample(this, dimensions); 29026 29027 // Following properties are all immutable. 29028 // So we can reference to the same value 29029 list._indices = this._indices; 29030 list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; 29031 29032 var storage = list._storage; 29033 29034 var tmpRetValue = []; 29035 var chunkSize = this._chunkSize; 29036 var dimSize = dimensions.length; 29037 var dataCount = this.count(); 29038 var values = []; 29039 var rawExtent = list._rawExtent; 29040 29041 for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) { 29042 for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) { 29043 values[dimIndex] = this.get(dimensions[dimIndex], dataIndex /*, stack */); 29044 }
29045 values[dimSize] = dataIndex; 29046 29047 var retValue = cb && cb.apply(context, values); 29048 if (retValue != null) { 29049 // a number or string (in oridinal dimension)? 29050 if (typeof retValue !== 'object') { 29051 tmpRetValue[0] = retValue; 29052 retValue = tmpRetValue; 29053 } 29054 29055 var rawIndex = this.getRawIndex(dataIndex); 29056 var chunkIndex = Math.floor(rawIndex / chunkSize); 29057 var chunkOffset = rawIndex % chunkSize; 29058 29059 for (var i = 0; i < retValue.length; i++) { 29060 var dim = dimensions[i]; 29061 var val = retValue[i]; 29062 var rawExtentOnDim = rawExtent[dim]; 29063 29064 var dimStore = storage[dim]; 29065 if (dimStore) { 29066 dimStore[chunkIndex][chunkOffset] = val; 29067 } 29068 29069 if (val < rawExtentOnDim[0]) { 29070 rawExtentOnDim[0] = val; 29071 } 29072 if (val > rawExtentOnDim[1]) { 29073 rawExtentOnDim[1] = val; 29074 } 29075 } 29076 } 29077 } 29078 29079 return list; 29080 }; 29081 29082 /** 29083 * Large data down sampling on given dimension 29084 * @param {string} dimension 29085 * @param {number} rate 29086 * @param {Function} sampleValue 29087 * @param {Function} sampleIndex Sample index for name and id 29088 */ 29089 listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) { 29090 var list = cloneListForMapAndSample(this, [dimension]); 29091 var targetStorage = list._storage; 29092 29093 var frameValues = []; 29094 var frameSize = Math.floor(1 / rate); 29095 29096 var dimStore = targetStorage[dimension]; 29097 var len = this.count(); 29098 var chunkSize = this._chunkSize; 29099 var rawExtentOnDim = list._rawExtent[dimension]; 29100 29101 var newIndices = new (getIndicesCtor(this))(len); 29102 29103 var offset = 0; 29104 for (var i = 0; i < len; i += frameSize) { 29105 // Last frame 29106 if (frameSize > len - i) { 29107 frameSize = len - i; 29108 frameValues.length = frameSize; 29109 } 29110 for (var k = 0; k < frameSize; k++) { 29111 var dataIdx = this.getRawIndex(i + k); 29112 var originalChunkIndex = Math.floor(dataIdx / chunkSize); 29113 var originalChunkOffset = dataIdx % chunkSize; 29114 frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset]; 29115 } 29116 var value = sampleValue(frameValues); 29117 var sampleFrameIdx = this.getRawIndex( 29118 Math.min(i + sampleIndex(frameValues, value) || 0, len - 1) 29119 ); 29120 var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize); 29121 var sampleChunkOffset = sampleFrameIdx % chunkSize; 29122 // Only write value on the filtered data 29123 dimStore[sampleChunkIndex][sampleChunkOffset] = value; 29124 29125 if (value < rawExtentOnDim[0]) { 29126 rawExtentOnDim[0] = value; 29127 } 29128 if (value > rawExtentOnDim[1]) { 29129 rawExtentOnDim[1] = value; 29130 } 29131 29132 newIndices[offset++] = sampleFrameIdx; 29133 } 29134 29135 list._count = offset; 29136 list._indices = newIndices; 29137 29138 list.getRawIndex = getRawIndexWithIndices; 29139 29140 return list; 29141 }; 29142 29143 /** 29144 * Get model of one data item. 29145 * 29146 * @param {number} idx 29147 */ 29148// FIXME Model proxy ? 29149 listProto.getItemModel = function (idx) { 29150 var hostModel = this.hostModel; 29151 return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel); 29152 }; 29153 29154 /** 29155 * Create a data differ 29156 * @param {module:echarts/data/List} otherList 29157 * @return {module:echarts/data/DataDiffer} 29158 */ 29159 listProto.diff = function (otherList) { 29160 var thisList = this; 29161 29162 return new DataDiffer( 29163 otherList ? otherList.getIndices() : [], 29164 this.getIndices(), 29165 function (idx) { 29166 return getId(otherList, idx); 29167 }, 29168 function (idx) { 29169 return getId(thisList, idx); 29170 } 29171 ); 29172 }; 29173 /** 29174 * Get visual property. 29175 * @param {string} key 29176 */ 29177 listProto.getVisual = function (key) { 29178 var visual = this._visual; 29179 return visual && visual[key]; 29180 }; 29181 29182 /** 29183 * Set visual property 29184 * @param {string|Object} key 29185 * @param {*} [value] 29186 * 29187 * @example 29188 * setVisual('color', color); 29189 * setVisual({ 29190 * 'color': color 29191 * }); 29192 */ 29193 listProto.setVisual = function (key, val) { 29194 if (isObject$4(key)) { 29195 for (var name in key) { 29196 if (key.hasOwnProperty(name)) { 29197 this.setVisual(name, key[name]); 29198 } 29199 } 29200 return; 29201 } 29202 this._visual = this._visual || {}; 29203 this._visual[key] = val; 29204 }; 29205 29206 /** 29207 * Set layout property. 29208 * @param {string|Object} key 29209 * @param {*} [val] 29210 */ 29211 listProto.setLayout = function (key, val) { 29212 if (isObject$4(key)) { 29213 for (var name in key) { 29214 if (key.hasOwnProperty(name)) { 29215 this.setLayout(name, key[name]); 29216 } 29217 } 29218 return; 29219 } 29220 this._layout[key] = val; 29221 }; 29222 29223 /** 29224 * Get layout property. 29225 * @param {string} key. 29226 * @return {*} 29227 */ 29228 listProto.getLayout = function (key) { 29229 return this._layout[key]; 29230 }; 29231 29232 /** 29233 * Get layout of single data item 29234 * @param {number} idx 29235 */ 29236 listProto.getItemLayout = function (idx) { 29237 return this._itemLayouts[idx]; 29238 }; 29239 29240 /** 29241 * Set layout of single data item 29242 * @param {number} idx 29243 * @param {Object} layout 29244 * @param {boolean=} [merge=false] 29245 */ 29246 listProto.setItemLayout = function (idx, layout, merge$$1) { 29247 this._itemLayouts[idx] = merge$$1 29248 ? extend(this._itemLayouts[idx] || {}, layout) 29249 : layout; 29250 }; 29251 29252 /** 29253 * Clear all layout of single data item 29254 */ 29255 listProto.clearItemLayouts = function () { 29256 this._itemLayouts.length = 0; 29257 }; 29258 29259 /** 29260 * Get visual property of single data item 29261 * @param {number} idx 29262 * @param {string} key 29263 * @param {boolean} [ignoreParent=false] 29264 */ 29265 listProto.getItemVisual = function (idx, key, ignoreParent) { 29266 var itemVisual = this._itemVisuals[idx]; 29267 var val = itemVisual && itemVisual[key]; 29268 if (val == null && !ignoreParent) { 29269 // Use global visual property 29270 return this.getVisual(key); 29271 } 29272 return val; 29273 }; 29274 29275 /** 29276 * Set visual property of single data item 29277 * 29278 * @param {number} idx 29279 * @param {string|Object} key 29280 * @param {*} [value] 29281 * 29282 * @example 29283 * setItemVisual(0, 'color', color); 29284 * setItemVisual(0, { 29285 * 'color': color 29286 * }); 29287 */ 29288 listProto.setItemVisual = function (idx, key, value) { 29289 var itemVisual = this._itemVisuals[idx] || {}; 29290 var hasItemVisual = this.hasItemVisual; 29291 this._itemVisuals[idx] = itemVisual; 29292 29293 if (isObject$4(key)) { 29294 for (var name in key) { 29295 if (key.hasOwnProperty(name)) { 29296 itemVisual[name] = key[name]; 29297 hasItemVisual[name] = true; 29298 } 29299 } 29300 return; 29301 } 29302 itemVisual[key] = value; 29303 hasItemVisual[key] = true; 29304 }; 29305 29306 /** 29307 * Clear itemVisuals and list visual. 29308 */ 29309 listProto.clearAllVisual = function () { 29310 this._visual = {}; 29311 this._itemVisuals = []; 29312 this.hasItemVisual = {}; 29313 }; 29314 29315 var setItemDataAndSeriesIndex = function (child) { 29316 child.seriesIndex = this.seriesIndex; 29317 child.dataIndex = this.dataIndex; 29318 child.dataType = this.dataType; 29319 }; 29320 /** 29321 * Set graphic element relative to data. It can be set as null 29322 * @param {number} idx 29323 * @param {module:zrender/Element} [el] 29324 */ 29325 listProto.setItemGraphicEl = function (idx, el) { 29326 var hostModel = this.hostModel; 29327 29328 if (el) { 29329 // Add data index and series index for indexing the data by element 29330 // Useful in tooltip 29331 el.dataIndex = idx; 29332 el.dataType = this.dataType; 29333 el.seriesIndex = hostModel && hostModel.seriesIndex; 29334 if (el.type === 'group') { 29335 el.traverse(setItemDataAndSeriesIndex, el); 29336 } 29337 } 29338 29339 this._graphicEls[idx] = el; 29340 }; 29341 29342 /** 29343 * @param {number} idx 29344 * @return {module:zrender/Element} 29345 */ 29346 listProto.getItemGraphicEl = function (idx) { 29347 return this._graphicEls[idx]; 29348 }; 29349 29350 /** 29351 * @param {Function} cb 29352 * @param {*} context 29353 */ 29354 listProto.eachItemGraphicEl = function (cb, context) { 29355 each$1(this._graphicEls, function (el, idx) { 29356 if (el) { 29357 cb && cb.call(context, el, idx); 29358 } 29359 }); 29360 }; 29361 29362 /** 29363 * Shallow clone a new list except visual and layout properties, and graph elements. 29364 * New list only change the indices. 29365 */ 29366 listProto.cloneShallow = function (list) { 29367 if (!list) { 29368 var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this); 29369 list = new List(dimensionInfoList, this.hostModel); 29370 } 29371 29372 // FIXME 29373 list._storage = this._storage; 29374 29375 transferProperties(list, this); 29376 29377 // Clone will not change the data extent and indices 29378 if (this._indices) { 29379 var Ctor = this._indices.constructor; 29380 list._indices = new Ctor(this._indices); 29381 } 29382 else { 29383 list._indices = null; 29384 } 29385 list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; 29386 29387 return list; 29388 }; 29389 29390 /** 29391 * Wrap some method to add more feature 29392 * @param {string} methodName 29393 * @param {Function} injectFunction 29394 */ 29395 listProto.wrapMethod = function (methodName, injectFunction) { 29396 var originalMethod = this[methodName]; 29397 if (typeof originalMethod !== 'function') { 29398 return; 29399 } 29400 this.__wrappedMethods = this.__wrappedMethods || []; 29401 this.__wrappedMethods.push(methodName); 29402 this[methodName] = function () { 29403 var res = originalMethod.apply(this, arguments); 29404 return injectFunction.apply(this, [res].concat(slice(arguments))); 29405 }; 29406 }; 29407 29408// Methods that create a new list based on this list should be listed here. 29409// Notice that those method should `RETURN` the new list. 29410 listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; 29411// Methods that change indices of this list should be listed here. 29412 listProto.CHANGABLE_METHODS = ['filterSelf', 'selectRange']; 29413 29414 /* 29415* Licensed to the Apache Software Foundation (ASF) under one 29416* or more contributor license agreements. See the NOTICE file 29417* distributed with this work for additional information 29418* regarding copyright ownership. The ASF licenses this file 29419* to you under the Apache License, Version 2.0 (the 29420* "License");
29420 you may not use this file except in compliance 29421* with the License. You may obtain a copy of the License at 29422* 29423* http://www.apache.org/licenses/LICENSE-2.0 29424* 29425* Unless required by applicable law or agreed to in writing, 29426* software distributed under the License is distributed on an 29427* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 29428* KIND, either express or implied. See the License for the 29429* specific language governing permissions and limitations 29430* under the License. 29431*/ 29432 29433 /** 29434 * @deprecated 29435 * Use `echarts/data/helper/createDimensions` instead. 29436 */ 29437 29438 /** 29439 * @see {module:echarts/test/ut/spec/data/completeDimensions} 29440 * 29441 * Complete the dimensions array, by user defined `dimension` and `encode`, 29442 * and guessing from the data structure. 29443 * If no 'value' dimension specified, the first no-named dimension will be 29444 * named as 'value'. 29445 * 29446 * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which 29447 * provides not only dim template, but also default order. 29448 * properties: 'name', 'type', 'displayName'. 29449 * `name` of each item provides default coord name. 29450 * [{dimsDef: [string|Object, ...]}, ...] dimsDef of sysDim item provides default dim name, and 29451 * provide dims count that the sysDim required. 29452 * [{ordinalMeta}] can be specified. 29453 * @param {module:echarts/data/Source|Array|Object} source or data (for compatibal with pervious) 29454 * @param {Object} [opt] 29455 * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions 29456 * For example: ['asdf', {name, type}, ...]. 29457 * @param {Object|HashMap} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3} 29458 * @param {string} [opt.generateCoord] Generate coord dim with the given name. 29459 * If not specified, extra dim names will be: 29460 * 'value', 'value0', 'value1', ... 29461 * @param {number} [opt.generateCoordCount] By default, the generated dim name is `generateCoord`. 29462 * If `generateCoordCount` specified, the generated dim names will be: 29463 * `generateCoord` + 0, `generateCoord` + 1, ... 29464 * can be Infinity, indicate that use all of the remain columns. 29465 * @param {number} [opt.dimCount] If not specified, guess by the first data item. 29466 * @param {number} [opt.encodeDefaulter] If not specified, auto find the next available data dim. 29467 * @return {Array.<Object>} [{ 29468 * name: string mandatory, 29469 * displayName: string, the origin name in dimsDef, see source helper. 29470 * If displayName given, the tooltip will displayed vertically. 29471 * coordDim: string mandatory, 29472 * coordDimIndex: number mandatory, 29473 * type: string optional, 29474 * otherDims: { never null/undefined 29475 * tooltip: number optional, 29476 * label: number optional, 29477 * itemName: number optional, 29478 * seriesName: number optional, 29479 * }, 29480 * isExtraCoord: boolean true if coord is generated 29481 * (not specified in encode and not series specified) 29482 * other props ... 29483 * }] 29484 */ 29485 function completeDimensions(sysDims, source, opt) { 29486 if (!Source.isInstance(source)) { 29487 source = Source.seriesDataToSource(source); 29488 } 29489 29490 opt = opt || {}; 29491 sysDims = (sysDims || []).slice(); 29492 var dimsDef = (opt.dimsDef || []).slice(); 29493 var encodeDef = createHashMap(opt.encodeDef); 29494 var dataDimNameMap = createHashMap(); 29495 var coordDimNameMap = createHashMap(); 29496 // var valueCandidate; 29497 var result = []; 29498 29499 var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimCount); 29500 29501 // Apply user defined dims (`name` and `type`) and init result. 29502 for (var i = 0; i < dimCount; i++) { 29503 var dimDefItem = dimsDef[i] = extend( 29504 {}, isObject$1(dimsDef[i]) ? dimsDef[i] : {name: dimsDef[i]} 29505 ); 29506 var userDimName = dimDefItem.name; 29507 var resultItem = result[i] = {otherDims: {}}; 29508 // Name will be applied later for avoiding duplication. 29509 if (userDimName != null && dataDimNameMap.get(userDimName) == null) { 29510 // Only if `series.dimensions` is defined in option 29511 // displayName, will be set, and dimension will be diplayed vertically in 29512 // tooltip by default. 29513 resultItem.name = resultItem.displayName = userDimName; 29514 dataDimNameMap.set(userDimName, i); 29515 } 29516 dimDefItem.type != null && (resultItem.type = dimDefItem.type); 29517 dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName); 29518 } 29519 29520 // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`. 29521 encodeDef.each(function (dataDims, coordDim) { 29522 dataDims = normalizeToArray(dataDims).slice();
29523 var validDataDims = encodeDef.set(coordDim, []); 29524 each$1(dataDims, function (resultDimIdx, idx) { 29525 // The input resultDimIdx can be dim name or index. 29526 isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx)); 29527 if (resultDimIdx != null && resultDimIdx < dimCount) { 29528 validDataDims[idx] = resultDimIdx; 29529 applyDim(result[resultDimIdx], coordDim, idx); 29530 } 29531 }); 29532 }); 29533 29534 // Apply templetes and default order from `sysDims`. 29535 var availDimIdx = 0; 29536 each$1(sysDims, function (sysDimItem, sysDimIndex) { 29537 var coordDim; 29538 var sysDimItem; 29539 var sysDimItemDimsDef; 29540 var sysDimItemOtherDims; 29541 if (isString(sysDimItem)) { 29542 coordDim = sysDimItem; 29543 sysDimItem = {}; 29544 } 29545 else { 29546 coordDim = sysDimItem.name; 29547 var ordinalMeta = sysDimItem.ordinalMeta; 29548 sysDimItem.ordinalMeta = null; 29549 sysDimItem = clone(sysDimItem); 29550 sysDimItem.ordinalMeta = ordinalMeta; 29551 // `coordDimIndex` should not be set directly. 29552 sysDimItemDimsDef = sysDimItem.dimsDef; 29553 sysDimItemOtherDims = sysDimItem.otherDims; 29554 sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex 29555 = sysDimItem.dimsDef = sysDimItem.otherDims = null; 29556 } 29557 29558 var dataDims = normalizeToArray(encodeDef.get(coordDim)); 29559 // dimensions provides default dim sequences. 29560 if (!dataDims.length) { 29561 for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) { 29562 while (availDimIdx < result.length && result[availDimIdx].coordDim != null) { 29563 availDimIdx++; 29564 } 29565 availDimIdx < result.length && dataDims.push(availDimIdx++); 29566 } 29567 } 29568 29569 // Apply templates. 29570 each$1(dataDims, function (resultDimIdx, coordDimIndex) { 29571 var resultItem = result[resultDimIdx]; 29572 applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex); 29573 if (resultItem.name == null && sysDimItemDimsDef) { 29574 var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex]; 29575 !isObject$1(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {name: sysDimItemDimsDefItem}); 29576 resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name; 29577 resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip; 29578 } 29579 // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}} 29580 sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims); 29581 }); 29582 }); 29583 29584 function applyDim(resultItem, coordDim, coordDimIndex) { 29585 if (OTHER_DIMENSIONS.get(coordDim) != null) { 29586 resultItem.otherDims[coordDim] = coordDimIndex; 29587 } 29588 else { 29589 resultItem.coordDim = coordDim; 29590 resultItem.coordDimIndex = coordDimIndex; 29591 coordDimNameMap.set(coordDim, true); 29592 } 29593 } 29594 29595 // Make sure the first extra dim is 'value'. 29596 var generateCoord = opt.generateCoord; 29597 var generateCoordCount = opt.generateCoordCount; 29598 var fromZero = generateCoordCount != null; 29599 generateCoordCount = generateCoord ? (generateCoordCount || 1) : 0; 29600 var extra = generateCoord || 'value'; 29601 29602 // Set dim `name` and other `coordDim` and other props. 29603 for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) { 29604 var resultItem = result[resultDimIdx] = result[resultDimIdx] || {}; 29605 var coordDim = resultItem.coordDim; 29606 29607 if (coordDim == null) { 29608 resultItem.coordDim = genName( 29609 extra, coordDimNameMap, fromZero 29610 ); 29611 resultItem.coordDimIndex = 0; 29612 if (!generateCoord || generateCoordCount <= 0) { 29613 resultItem.isExtraCoord = true; 29614 } 29615 generateCoordCount--; 29616 } 29617 29618 resultItem.name == null && (resultItem.name = genName( 29619 resultItem.coordDim, 29620 dataDimNameMap 29621 )); 29622 29623 if (resultItem.type == null && guessOrdinal(source, resultDimIdx, resultItem.name)) { 29624 resultItem.type = 'ordinal'; 29625 } 29626 } 29627 29628 return result; 29629 } 29630 29631// ??? TODO 29632// Originally detect dimCount by data[0]. Should we 29633// optimize it to only by sysDims and dimensions and encode. 29634// So only necessary dims will be initialized. 29635// But 29636// (1) custom series should be considered. where other dims 29637// may be visited. 29638// (2) sometimes user need to calcualte bubble size or use visualMap 29639// on other dimensions besides coordSys needed. 29640// So, dims that is not used by system, should be shared in storage? 29641 function getDimCount(source, sysDims, dimsDef, optDimCount) { 29642 // Note that the result dimCount should not small than columns count 29643 // of data, otherwise `dataDimNameMap` checking will be incorrect. 29644 var dimCount = Math.max( 29645 source.dimensionsDetectCount || 1, 29646 sysDims.length, 29647 dimsDef.length, 29648 optDimCount || 0 29649 ); 29650 each$1(sysDims, function (sysDimItem) { 29651 var sysDimItemDimsDef = sysDimItem.dimsDef; 29652 sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length)); 29653 }); 29654 return dimCount; 29655 } 29656 29657 function genName(name, map$$1, fromZero) { 29658 if (fromZero || map$$1.get(name) != null) { 29659 var i = 0; 29660 while (map$$1.get(name + i) != null) { 29661 i++; 29662 } 29663 name += i; 29664 } 29665 map$$1.set(name, true); 29666 return name; 29667 } 29668 29669 /* 29670* Licensed to the Apache Software Foundation (ASF) under one 29671* or more contributor license agreements. See the NOTICE file 29672* distributed with this work for additional information 29673* regarding copyright ownership. The ASF licenses this file 29674* to you under the Apache License, Version 2.0 (the 29675* "License");
29675 you may not use this file except in compliance 29676* with the License. You may obtain a copy of the License at 29677* 29678* http://www.apache.org/licenses/LICENSE-2.0 29679* 29680* Unless required by applicable law or agreed to in writing, 29681* software distributed under the License is distributed on an 29682* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 29683* KIND, either express or implied. See the License for the 29684* specific language governing permissions and limitations 29685* under the License. 29686*/ 29687 29688 /** 29689 * Substitute `completeDimensions`. 29690 * `completeDimensions` is to be deprecated. 29691 */ 29692 /** 29693 * @param {module:echarts/data/Source|module:echarts/data/List} source or data. 29694 * @param {Object|Array} [opt] 29695 * @param {Array.<string|Object>} [opt.coordDimensions=[]] 29696 * @param {number} [opt.dimensionsCount] 29697 * @param {string} [opt.generateCoord] 29698 * @param {string} [opt.generateCoordCount] 29699 * @param {Array.<string|Object>} [opt.dimensionsDefine=source.dimensionsDefine] Overwrite source define. 29700 * @param {Object|HashMap} [opt.encodeDefine=source.encodeDefine] Overwrite source define. 29701 * @return {Array.<Object>} dimensionsInfo 29702 */ 29703 var createDimensions = function (source, opt) { 29704 opt = opt || {}; 29705 return completeDimensions(opt.coordDimensions || [], source, { 29706 dimsDef: opt.dimensionsDefine || source.dimensionsDefine, 29707 encodeDef: opt.encodeDefine || source.encodeDefine, 29708 dimCount: opt.dimensionsCount, 29709 generateCoord: opt.generateCoord, 29710 generateCoordCount: opt.generateCoordCount 29711 }); 29712 }; 29713 29714 /* 29715* Licensed to the Apache Software Foundation (ASF) under one 29716* or more contributor license agreements. See the NOTICE file 29717* distributed with this work for additional information 29718* regarding copyright ownership. The ASF licenses this file 29719* to you under the Apache License, Version 2.0 (the 29720* "License"); you may not use this file except in compliance 29721* with the License. You may obtain a copy of the License at 29722* 29723* http://www.apache.org/licenses/LICENSE-2.0 29724* 29725* Unless required by applicable law or agreed to in writing, 29726* software distributed under the License is distributed on an 29727* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 29728* KIND, either express or implied. See the License for the 29729* specific language governing permissions and limitations 29730* under the License. 29731*/ 29732 29733 /** 29734 * Note that it is too complicated to support 3d stack by value 29735 * (have to create two-dimension inverted index), so in 3d case 29736 * we just support that stacked by index. 29737 * 29738 * @param {module:echarts/model/Series} seriesModel 29739 * @param {Array.<string|Object>} dimensionInfoList The same as the input of <module:echarts/data/List>. 29740 * The input dimensionInfoList will be modified. 29741 * @param {Object} [opt] 29742 * @param {boolean} [opt.stackedCoordDimension=''] Specify a coord dimension if needed. 29743 * @param {boolean} [opt.byIndex=false] 29744 * @return {Object} calculationInfo 29745 * { 29746 * stackedDimension: string 29747 * stackedByDimension: string 29748 * isStackedByIndex: boolean 29749 * stackedOverDimension: string 29750 * stackResultDimension: string 29751 * } 29752 */ 29753 function enableDataStack(seriesModel, dimensionInfoList, opt) { 29754 opt = opt || {}; 29755 var byIndex = opt.byIndex; 29756 var stackedCoordDimension = opt.stackedCoordDimension; 29757 29758 // Compatibal: when `stack` is set as '', do not stack. 29759 var mayStack = !!(seriesModel && seriesModel.get('stack')); 29760 var stackedByDimInfo; 29761 var stackedDimInfo; 29762 var stackResultDimension; 29763 var stackedOverDimension; 29764 29765 each$1(dimensionInfoList, function (dimensionInfo, index) { 29766 if (isString(dimensionInfo)) { 29767 dimensionInfoList[index] = dimensionInfo = {name: dimensionInfo}; 29768 } 29769 29770 if (mayStack && !dimensionInfo.isExtraCoord) { 29771 // Find the first ordinal dimension as the stackedByDimInfo. 29772 if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { 29773 stackedByDimInfo = dimensionInfo; 29774 } 29775 // Find the first stackable dimension as the stackedDimInfo. 29776 if (!stackedDimInfo 29777 && dimensionInfo.type !== 'ordinal' 29778 && dimensionInfo.type !== 'time' 29779 && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim) 29780 ) { 29781 stackedDimInfo = dimensionInfo; 29782 } 29783 } 29784 }); 29785 29786 if (stackedDimInfo && !byIndex && !stackedByDimInfo) { 29787 // Compatible with previous design, value axis (time axis) only stack by index. 29788 // It may make sense if the user provides elaborately constructed data. 29789 byIndex = true; 29790 } 29791 29792 // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`. 29793 // That put stack logic in List is for using conveniently in echarts extensions, but it 29794 // might not be a good way. 29795 if (stackedDimInfo) { 29796 // Use a weird name that not duplicated with other names. 29797 stackResultDimension = '__\0ecstackresult'; 29798 stackedOverDimension = '__\0ecstackedover'; 29799 29800 // Create inverted index to fast query index by value. 29801 if (stackedByDimInfo) { 29802 stackedByDimInfo.createInvertedIndices = true; 29803 } 29804 29805 var stackedDimCoordDim = stackedDimInfo.coordDim; 29806 var stackedDimType = stackedDimInfo.type; 29807 var stackedDimCoordIndex = 0; 29808 29809 each$1(dimensionInfoList, function (dimensionInfo) { 29810 if (dimensionInfo.coordDim === stackedDimCoordDim) { 29811 stackedDimCoordIndex++; 29812 } 29813 }); 29814 29815 dimensionInfoList.push({ 29816 name: stackResultDimension, 29817 coordDim: stackedDimCoordDim, 29818 coordDimIndex: stackedDimCoordIndex, 29819 type: stackedDimType, 29820 isExtraCoord: true, 29821 isCalculationCoord: true 29822 }); 29823 29824 stackedDimCoordIndex++; 29825 29826 dimensionInfoList.push({ 29827 name: stackedOverDimension, 29828 // This dimension contains stack base (generally, 0), so do not set it as 29829 // `stackedDimCoordDim` to avoid extent calculation, consider log scale. 29830 coordDim: stackedOverDimension, 29831 coordDimIndex: stackedDimCoordIndex, 29832 type: stackedDimType, 29833 isExtraCoord: true, 29834 isCalculationCoord: true 29835 }); 29836 } 29837 29838 return { 29839 stackedDimension: stackedDimInfo && stackedDimInfo.name, 29840 stackedByDimension: stackedByDimInfo && stackedByDimInfo.name, 29841 isStackedByIndex: byIndex, 29842 stackedOverDimension: stackedOverDimension, 29843 stackResultDimension: stackResultDimension 29844 }; 29845 } 29846 29847 /**
29848 * @param {module:echarts/data/List} data 29849 * @param {string} stackedDim 29850 */ 29851 function isDimensionStacked(data, stackedDim /*, stackedByDim*/) { 29852 // Each single series only maps to one pair of axis. So we do not need to 29853 // check stackByDim, whatever stacked by a dimension or stacked by index. 29854 return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension'); 29855 // && ( 29856 // stackedByDim != null 29857 // ? stackedByDim === data.getCalculationInfo('stackedByDimension') 29858 // : data.getCalculationInfo('isStackedByIndex') 29859 // ); 29860 } 29861 29862 /** 29863 * @param {module:echarts/data/List} data 29864 * @param {string} targetDim 29865 * @param {string} [stackedByDim] If not input this parameter, check whether 29866 * stacked by index. 29867 * @return {string} dimension 29868 */ 29869 function getStackedDimension(data, targetDim) { 29870 return isDimensionStacked(data, targetDim) 29871 ? data.getCalculationInfo('stackResultDimension') 29872 : targetDim; 29873 } 29874 29875 /* 29876* Licensed to the Apache Software Foundation (ASF) under one 29877* or more contributor license agreements. See the NOTICE file 29878* distributed with this work for additional information 29879* regarding copyright ownership. The ASF licenses this file 29880* to you under the Apache License, Version 2.0 (the 29881* "License"); you may not use this file except in compliance 29882* with the License. You may obtain a copy of the License at 29883* 29884* http://www.apache.org/licenses/LICENSE-2.0 29885* 29886* Unless required by applicable law or agreed to in writing, 29887* software distributed under the License is distributed on an 29888* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 29889* KIND, either express or implied. See the License for the 29890* specific language governing permissions and limitations 29891* under the License. 29892*/ 29893 29894 /** 29895 * @param {module:echarts/data/Source|Array} source Or raw data. 29896 * @param {module:echarts/model/Series} seriesModel 29897 * @param {Object} [opt] 29898 * @param {string} [opt.generateCoord] 29899 */ 29900 function createListFromArray(source, seriesModel, opt) { 29901 opt = opt || {}; 29902 29903 if (!Source.isInstance(source)) { 29904 source = Source.seriesDataToSource(source); 29905 } 29906 29907 var coordSysName = seriesModel.get('coordinateSystem'); 29908 var registeredCoordSys = CoordinateSystemManager.get(coordSysName); 29909 29910 var coordSysDefine = getCoordSysDefineBySeries(seriesModel); 29911 29912 var coordSysDimDefs; 29913 29914 if (coordSysDefine) { 29915 coordSysDimDefs = map(coordSysDefine.coordSysDims, function (dim) { 29916 var dimInfo = {name: dim}; 29917 var axisModel = coordSysDefine.axisMap.get(dim); 29918 if (axisModel) { 29919 var axisType = axisModel.get('type'); 29920 dimInfo.type = getDimensionTypeByAxis(axisType); 29921 // dimInfo.stackable = isStackable(axisType); 29922 } 29923 return dimInfo; 29924 }); 29925 } 29926 29927 if (!coordSysDimDefs) { 29928 // Get dimensions from registered coordinate system 29929 coordSysDimDefs = (registeredCoordSys && ( 29930 registeredCoordSys.getDimensionsInfo 29931 ? registeredCoordSys.getDimensionsInfo() 29932 : registeredCoordSys.dimensions.slice() 29933 )) || ['x', 'y']; 29934 } 29935 29936 var dimInfoList = createDimensions(source, { 29937 coordDimensions: coordSysDimDefs, 29938 generateCoord: opt.generateCoord 29939 }); 29940 29941 var firstCategoryDimIndex; 29942 var hasNameEncode; 29943 coordSysDefine && each$1(dimInfoList, function (dimInfo, dimIndex) { 29944 var coordDim = dimInfo.coordDim; 29945 var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim); 29946 if (categoryAxisModel) { 29947 if (firstCategoryDimIndex == null) { 29948 firstCategoryDimIndex = dimIndex; 29949 } 29950 dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta(); 29951 } 29952 if (dimInfo.otherDims.itemName != null) { 29953 hasNameEncode = true; 29954 } 29955 }); 29956 if (!hasNameEncode && firstCategoryDimIndex != null) { 29957 dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0; 29958 } 29959
29960 var stackCalculationInfo = enableDataStack(seriesModel, dimInfoList); 29961 29962 var list = new List(dimInfoList, seriesModel); 29963 29964 list.setCalculationInfo(stackCalculationInfo); 29965 29966 var dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source)) 29967 ? function (itemOpt, dimName, dataIndex, dimIndex) { 29968 // Use dataIndex as ordinal value in categoryAxis 29969 return dimIndex === firstCategoryDimIndex 29970 ? dataIndex 29971 : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex); 29972 } 29973 : null; 29974 29975 list.hasItemOption = false; 29976 list.initData(source, null, dimValueGetter); 29977 29978 return list; 29979 } 29980 29981 function isNeedCompleteOrdinalData(source) { 29982 if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) { 29983 var sampleItem = firstDataNotNull(source.data || []); 29984 return sampleItem != null 29985 && !isArray(getDataItemValue(sampleItem)); 29986 } 29987 } 29988 29989 function firstDataNotNull(data) { 29990 var i = 0; 29991 while (i < data.length && data[i] == null) { 29992 i++; 29993 } 29994 return data[i]; 29995 } 29996 29997 /* 29998* Licensed to the Apache Software Foundation (ASF) under one 29999* or more contributor license agreements. See the NOTICE file 30000* distributed with this work for additional information 30001* regarding copyright ownership. The ASF licenses this file 30002* to you under the Apache License, Version 2.0 (the 30003* "License"); you may not use this file except in compliance 30004* with the License. You may obtain a copy of the License at 30005* 30006* http://www.apache.org/licenses/LICENSE-2.0 30007* 30008* Unless required by applicable law or agreed to in writing, 30009* software distributed under the License is distributed on an 30010* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 30011* KIND, either express or implied. See the License for the 30012* specific language governing permissions and limitations 30013* under the License. 30014*/ 30015 30016 /** 30017 * // Scale class management 30018 * @module echarts/scale/Scale 30019 */ 30020 30021 /** 30022 * @param {Object} [setting] 30023 */ 30024 function Scale(setting) { 30025 this._setting = setting || {}; 30026 30027 /** 30028 * Extent 30029 * @type {Array.<number>} 30030 * @protected 30031 */ 30032 this._extent = [Infinity, -Infinity]; 30033 30034 /** 30035 * Step is calculated in adjustExtent 30036 * @type {Array.<number>} 30037 * @protected 30038 */ 30039 this._interval = 0; 30040 30041 this.init && this.init.apply(this, arguments); 30042 } 30043 30044 /** 30045 * Parse input val to valid inner number. 30046 * @param {*} val 30047 * @return {number} 30048 */ 30049 Scale.prototype.parse = function (val) { 30050 // Notice: This would be a trap here, If the implementation 30051 // of this method depends on extent, and this method is used 30052 // before extent set (like in dataZoom), it would be wrong. 30053 // Nevertheless, parse does not depend on extent generally. 30054 return val; 30055 }; 30056 30057 Scale.prototype.getSetting = function (name) { 30058 return this._setting[name]; 30059 }; 30060 30061 Scale.prototype.contain = function (val) { 30062 var extent = this._extent; 30063 return val >= extent[0] && val <= extent[1]; 30064 }; 30065 30066 /** 30067 * Normalize value to linear [0, 1], return 0.5 if extent span is 0 30068 * @param {number} val 30069 * @return {number} 30070 */ 30071 Scale.prototype.normalize = function (val) { 30072 var extent = this._extent; 30073 if (extent[1] === extent[0]) { 30074 return 0.5; 30075 } 30076 return (val - extent[0]) / (extent[1] - extent[0]); 30077 }; 30078 30079 /** 30080 * Scale normalized value 30081 * @param {number} val 30082 * @return {number} 30083 */ 30084 Scale.prototype.scale = function (val) { 30085 var extent = this._extent; 30086 return val * (extent[1] - extent[0]) + extent[0]; 30087 }; 30088 30089 /** 30090 * Set extent from data 30091 * @param {Array.<number>} other 30092 */ 30093 Scale.prototype.unionExtent = function (other) { 30094 var extent = this._extent; 30095 other[0] < extent[0] && (extent[0] = other[0]); 30096 other[1] > extent[1] && (extent[1] = other[1]); 30097 // not setExtent because in log axis it may transformed to power 30098 // this.setExtent(extent[0], extent[1]); 30099 }; 30100 30101 /** 30102 * Set extent from data
30103 * @param {module:echarts/data/List} data 30104 * @param {string} dim 30105 */ 30106 Scale.prototype.unionExtentFromData = function (data, dim) { 30107 this.unionExtent(data.getApproximateExtent(dim)); 30108 }; 30109 30110 /** 30111 * Get extent 30112 * @return {Array.<number>} 30113 */ 30114 Scale.prototype.getExtent = function () { 30115 return this._extent.slice(); 30116 }; 30117 30118 /** 30119 * Set extent 30120 * @param {number} start 30121 * @param {number} end 30122 */ 30123 Scale.prototype.setExtent = function (start, end) { 30124 var thisExtent = this._extent; 30125 if (!isNaN(start)) { 30126 thisExtent[0] = start; 30127 } 30128 if (!isNaN(end)) { 30129 thisExtent[1] = end; 30130 } 30131 }; 30132 30133 /** 30134 * When axis extent depends on data and no data exists, 30135 * axis ticks should not be drawn, which is named 'blank'. 30136 */ 30137 Scale.prototype.isBlank = function () { 30138 return this._isBlank; 30139 }, 30140 30141 /** 30142 * When axis extent depends on data and no data exists, 30143 * axis ticks should not be drawn, which is named 'blank'. 30144 */ 30145 Scale.prototype.setBlank = function (isBlank) { 30146 this._isBlank = isBlank; 30147 }; 30148 30149 /** 30150 * @abstract 30151 * @param {*} tick 30152 * @return {string} label of the tick. 30153 */ 30154 Scale.prototype.getLabel = null; 30155 30156 30157 enableClassExtend(Scale); 30158 enableClassManagement(Scale, { 30159 registerWhenExtend: true 30160 }); 30161 30162 /* 30163* Licensed to the Apache Software Foundation (ASF) under one 30164* or more contributor license agreements. See the NOTICE file 30165* distributed with this work for additional information 30166* regarding copyright ownership. The ASF licenses this file 30167* to you under the Apache License, Version 2.0 (the 30168* "License"); you may not use this file except in compliance 30169* with the License. You may obtain a copy of the License at 30170* 30171* http://www.apache.org/licenses/LICENSE-2.0 30172* 30173* Unless required by applicable law or agreed to in writing, 30174* software distributed under the License is distributed on an 30175* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 30176* KIND, either express or implied. See the License for the 30177* specific language governing permissions and limitations 30178* under the License. 30179*/ 30180 30181 /** 30182 * @constructor 30183 * @param {Object} [opt] 30184 * @param {Object} [opt.categories=[]] 30185 * @param {Object} [opt.needCollect=false] 30186 * @param {Object} [opt.deduplication=false] 30187 */ 30188 function OrdinalMeta(opt) { 30189 30190 /** 30191 * @readOnly 30192 * @type {Array.<string>} 30193 */ 30194 this.categories = opt.categories || []; 30195 30196 /** 30197 * @private 30198 * @type {boolean} 30199 */ 30200 this._needCollect = opt.needCollect; 30201 30202 /** 30203 * @private 30204 * @type {boolean} 30205 */ 30206 this._deduplication = opt.deduplication; 30207 30208 /** 30209 * @private 30210 * @type {boolean} 30211 */ 30212 this._map; 30213 } 30214 30215 /** 30216 * @param {module:echarts/model/Model} axisModel 30217 * @return {module:echarts/data/OrdinalMeta} 30218 */ 30219 OrdinalMeta.createByAxisModel = function (axisModel) { 30220 var option = axisModel.option; 30221 var data = option.data; 30222 var categories = data && map(data, getName); 30223 30224 return new OrdinalMeta({ 30225 categories: categories, 30226 needCollect: !categories, 30227 // deduplication is default in axis. 30228 deduplication: option.dedplication !== false 30229 }); 30230 }; 30231 30232 var proto$1 = OrdinalMeta.prototype; 30233 30234 /** 30235 * @param {string} category 30236 * @return {number} ordinal 30237 */ 30238 proto$1.getOrdinal = function (category) { 30239 return getOrCreateMap(this).get(category); 30240 }; 30241 30242 /** 30243 * @param {*} category 30244 * @return {number} The ordinal. If not found, return NaN. 30245 */ 30246 proto$1.parseAndCollect = function (category) { 30247 var index; 30248 var needCollect = this._needCollect; 30249 30250 // The value of category dim can be the index of the given category set. 30251 // This feature is only supported when !needCollect, because we should 30252 // consider a common case: a value is 2017, which is a number but is 30253 // expected to be tread as a category. This case usually happen in dataset, 30254 // where it happent to be no need of the index feature. 30255 if (typeof category !== 'string' && !needCollect) { 30256 return category; 30257 } 30258 30259 // Optimize for the scenario: 30260 // category is ['2012-01-01', '2012-01-02', ...], where the input 30261 // data has been ensured not duplicate and is large data. 30262 // Notice, if a dataset dimension provide categroies, usually echarts 30263 // should remove duplication except user tell echarts dont do that 30264 // (set axis.deduplication = false), because echarts do not know whether 30265 // the values in the category dimension has duplication (consider the 30266 // parallel-aqi example) 30267 if (needCollect && !this._deduplication) { 30268 index = this.categories.length; 30269 this.categories[index] = category; 30270 return index; 30271 } 30272 30273 var map$$1 = getOrCreateMap(this); 30274 index = map$$1.get(category); 30275 30276 if (index == null) { 30277 if (needCollect) { 30278 index = this.categories.length; 30279 this.categories[index] = category; 30280 map$$1.set(category, index); 30281 } 30282 else { 30283 index = NaN; 30284 } 30285 } 30286 30287 return index; 30288 }; 30289 30290// Consider big data, do not create map until needed. 30291 function getOrCreateMap(ordinalMeta) { 30292 return ordinalMeta._map || ( 30293 ordinalMeta._map = createHashMap(ordinalMeta.categories) 30294 ); 30295 } 30296 30297 function getName(obj) { 30298 if (isObject$1(obj) && obj.value != null) { 30299 return obj.value; 30300 } 30301 else { 30302 return obj + ''; 30303 } 30304 } 30305 30306 /* 30307* Licensed to the Apache Software Foundation (ASF) under one 30308* or more contributor license agreements. See the NOTICE file 30309* distributed with this work for additional information 30310* regarding copyright ownership. The ASF licenses this file 30311* to you under the Apache License, Version 2.0 (the 30312* "License");
30312 you may not use this file except in compliance 30313* with the License. You may obtain a copy of the License at 30314* 30315* http://www.apache.org/licenses/LICENSE-2.0 30316* 30317* Unless required by applicable law or agreed to in writing, 30318* software distributed under the License is distributed on an 30319* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 30320* KIND, either express or implied. See the License for the 30321* specific language governing permissions and limitations 30322* under the License. 30323*/ 30324 30325 /** 30326 * Linear continuous scale 30327 * @module echarts/coord/scale/Ordinal 30328 * 30329 * http://en.wikipedia.org/wiki/Level_of_measurement 30330 */ 30331 30332// FIXME only one data 30333 30334 var scaleProto = Scale.prototype; 30335 30336 var OrdinalScale = Scale.extend({ 30337 30338 type: 'ordinal', 30339 30340 /** 30341 * @param {module:echarts/data/OrdianlMeta|Array.<string>} ordinalMeta 30342 */ 30343 init: function (ordinalMeta, extent) { 30344 // Caution: Should not use instanceof, consider ec-extensions using 30345 // import approach to get OrdinalMeta class. 30346 if (!ordinalMeta || isArray(ordinalMeta)) { 30347 ordinalMeta = new OrdinalMeta({categories: ordinalMeta}); 30348 } 30349 this._ordinalMeta = ordinalMeta; 30350 this._extent = extent || [0, ordinalMeta.categories.length - 1]; 30351 }, 30352 30353 parse: function (val) { 30354 return typeof val === 'string' 30355 ? this._ordinalMeta.getOrdinal(val) 30356 // val might be float. 30357 : Math.round(val); 30358 }, 30359 30360 contain: function (rank) { 30361 rank = this.parse(rank); 30362 return scaleProto.contain.call(this, rank) 30363 && this._ordinalMeta.categories[rank] != null; 30364 }, 30365 30366 /** 30367 * Normalize given rank or name to linear [0, 1] 30368 * @param {number|string} [val] 30369 * @return {number} 30370 */ 30371 normalize: function (val) { 30372 return scaleProto.normalize.call(this, this.parse(val)); 30373 }, 30374 30375 scale: function (val) { 30376 return Math.round(scaleProto.scale.call(this, val)); 30377 }, 30378 30379 /** 30380 * @return {Array} 30381 */ 30382 getTicks: function () { 30383 var ticks = []; 30384 var extent = this._extent; 30385 var rank = extent[0]; 30386 30387 while (rank <= extent[1]) { 30388 ticks.push(rank); 30389 rank++; 30390 } 30391 30392 return ticks; 30393 }, 30394 30395 /** 30396 * Get item on rank n 30397 * @param {number} n 30398 * @return {string} 30399 */ 30400 getLabel: function (n) { 30401 if (!this.isBlank()) { 30402 // Note that if no data, ordinalMeta.categories is an empty array. 30403 return this._ordinalMeta.categories[n]; 30404 } 30405 }, 30406 30407 /** 30408 * @return {number} 30409 */ 30410 count: function () { 30411 return this._extent[1] - this._extent[0] + 1; 30412 }, 30413 30414 /** 30415 * @override 30416 */ 30417 unionExtentFromData: function (data, dim) { 30418 this.unionExtent(data.getApproximateExtent(dim)); 30419 }, 30420 30421 getOrdinalMeta: function () { 30422 return this._ordinalMeta; 30423 }, 30424 30425 niceTicks: noop, 30426 niceExtent: noop 30427 }); 30428 30429 /** 30430 * @return {module:echarts/scale/Time} 30431 */ 30432 OrdinalScale.create = function () { 30433 return new OrdinalScale(); 30434 }; 30435 30436 /* 30437* Licensed to the Apache Software Foundation (ASF) under one 30438* or more contributor license agreements. See the NOTICE file 30439* distributed with this work for additional information 30440* regarding copyright ownership. The ASF licenses this file 30441* to you under the Apache License, Version 2.0 (the 30442* "License"); you may not use this file except in compliance 30443* with the License. You may obtain a copy of the License at 30444* 30445* http://www.apache.org/licenses/LICENSE-2.0 30446* 30447* Unless required by applicable law or agreed to in writing, 30448* software distributed under the License is distributed on an 30449* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 30450* KIND, either express or implied. See the License for the 30451* specific language governing permissions and limitations 30452* under the License. 30453*/ 30454 30455 /** 30456 * For testable. 30457 */ 30458 30459 var roundNumber$1 = round$1; 30460 30461 /** 30462 * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number. 30463 * Should be extent[0] < extent[1]. 30464 * @param {number} splitNumber splitNumber should be >= 1. 30465 * @param {number} [minInterval] 30466 * @param {number} [maxInterval] 30467 * @return {Object} {interval, intervalPrecision, niceTickExtent} 30468 */ 30469 function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) { 30470 var result = {}; 30471 var span = extent[1] - extent[0]; 30472 30473 var interval = result.interval = nice(span / splitNumber, true); 30474 if (minInterval != null && interval < minInterval) { 30475 interval = result.interval = minInterval; 30476 } 30477 if (maxInterval != null && interval > maxInterval) { 30478 interval = result.interval = maxInterval; 30479 } 30480 // Tow more digital for tick. 30481 var precision = result.intervalPrecision = getIntervalPrecision(interval); 30482 // Niced extent inside original extent 30483 var niceTickExtent = result.niceTickExtent = [ 30484 roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision), 30485 roundNumber$1(Math.floor(extent[1] / interval) * interval, precision) 30486 ]; 30487 30488 fixExtent(niceTickExtent, extent); 30489 30490 return result; 30491 } 30492 30493 /** 30494 * @param {number} interval 30495 * @return {number} interval precision 30496 */ 30497 function getIntervalPrecision(interval) { 30498 // Tow more digital for tick. 30499 return getPrecisionSafe(interval) + 2; 30500 } 30501 30502 function clamp(niceTickExtent, idx, extent) { 30503 niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]); 30504 } 30505 30506// In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent. 30507 function fixExtent(niceTickExtent, extent) { 30508 !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]); 30509 !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]); 30510 clamp(niceTickExtent, 0, extent); 30511 clamp(niceTickExtent, 1, extent); 30512 if (niceTickExtent[0] > niceTickExtent[1]) { 30513 niceTickExtent[0] = niceTickExtent[1];
30514 } 30515 } 30516 30517 function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) { 30518 var ticks = []; 30519 30520 // If interval is 0, return []; 30521 if (!interval) { 30522 return ticks; 30523 } 30524 30525 // Consider this case: using dataZoom toolbox, zoom and zoom. 30526 var safeLimit = 10000; 30527 30528 if (extent[0] < niceTickExtent[0]) { 30529 ticks.push(extent[0]); 30530 } 30531 var tick = niceTickExtent[0]; 30532 30533 while (tick <= niceTickExtent[1]) { 30534 ticks.push(tick); 30535 // Avoid rounding error 30536 tick = roundNumber$1(tick + interval, intervalPrecision); 30537 if (tick === ticks[ticks.length - 1]) { 30538 // Consider out of safe float point, e.g., 30539 // -3711126.9907707 + 2e-10 === -3711126.9907707 30540 break; 30541 } 30542 if (ticks.length > safeLimit) { 30543 return []; 30544 } 30545 } 30546 // Consider this case: the last item of ticks is smaller 30547 // than niceTickExtent[1] and niceTickExtent[1] === extent[1]. 30548 if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) { 30549 ticks.push(extent[1]); 30550 } 30551 30552 return ticks; 30553 } 30554 30555 /* 30556* Licensed to the Apache Software Foundation (ASF) under one 30557* or more contributor license agreements. See the NOTICE file 30558* distributed with this work for additional information 30559* regarding copyright ownership. The ASF licenses this file 30560* to you under the Apache License, Version 2.0 (the 30561* "License"); you may not use this file except in compliance 30562* with the License. You may obtain a copy of the License at 30563* 30564* http://www.apache.org/licenses/LICENSE-2.0 30565* 30566* Unless required by applicable law or agreed to in writing, 30567* software distributed under the License is distributed on an 30568* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 30569* KIND, either express or implied. See the License for the 30570* specific language governing permissions and limitations 30571* under the License. 30572*/ 30573 30574 /** 30575 * Interval scale 30576 * @module echarts/scale/Interval 30577 */ 30578 30579 30580 var roundNumber = round$1; 30581 30582 /** 30583 * @alias module:echarts/coord/scale/Interval 30584 * @constructor 30585 */ 30586 var IntervalScale = Scale.extend({ 30587 30588 type: 'interval', 30589 30590 _interval: 0, 30591 30592 _intervalPrecision: 2, 30593 30594 setExtent: function (start, end) { 30595 var thisExtent = this._extent; 30596 //start,end may be a Number like '25',so... 30597 if (!isNaN(start)) { 30598 thisExtent[0] = parseFloat(start); 30599 } 30600 if (!isNaN(end)) { 30601 thisExtent[1] = parseFloat(end); 30602 } 30603 }, 30604 30605 unionExtent: function (other) { 30606 var extent = this._extent; 30607 other[0] < extent[0] && (extent[0] = other[0]); 30608 other[1] > extent[1] && (extent[1] = other[1]); 30609 30610 // unionExtent may called by it's sub classes 30611 IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]); 30612 }, 30613 /** 30614 * Get interval 30615 */ 30616 getInterval: function () { 30617 return this._interval; 30618 }, 30619 30620 /** 30621 * Set interval 30622 */ 30623 setInterval: function (interval) { 30624 this._interval = interval; 30625 // Dropped auto calculated niceExtent and use user setted extent 30626 // We assume user wan't to set both interval, min, max to get a better result 30627 this._niceExtent = this._extent.slice(); 30628 30629 this._intervalPrecision = getIntervalPrecision(interval); 30630 }, 30631 30632 /** 30633 * @return {Array.<number>} 30634 */ 30635 getTicks: function () { 30636 return intervalScaleGetTicks( 30637 this._interval, this._extent, this._niceExtent, this._intervalPrecision 30638 ); 30639 }, 30640 30641 /** 30642 * @param {number} data 30643 * @param {Object} [opt] 30644 * @param {number|string} [opt.precision] If 'auto', use nice presision. 30645 * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2. 30646 * @return {string} 30647 */ 30648 getLabel: function (data, opt) { 30649 if (data == null) { 30650 return ''; 30651 } 30652 30653 var precision = opt && opt.precision; 30654 30655 if (precision == null) { 30656 precision = getPrecisionSafe(data) || 0; 30657 } 30658 else if (precision === 'auto') { 30659 // Should be more precise then tick. 30660 precision = this._intervalPrecision; 30661 } 30662 30663 // (1) If `precision` is set, 12.005 should be display as '12.00500'. 30664 // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'. 30665 data = roundNumber(data, precision, true); 30666 30667 return addCommas(data); 30668 }, 30669 30670 /** 30671 * Update interval and extent of intervals for nice ticks 30672 * 30673 * @param {number}
30673 [splitNumber = 5] Desired number of ticks 30674 * @param {number} [minInterval] 30675 * @param {number} [maxInterval] 30676 */ 30677 niceTicks: function (splitNumber, minInterval, maxInterval) { 30678 splitNumber = splitNumber || 5; 30679 var extent = this._extent; 30680 var span = extent[1] - extent[0]; 30681 if (!isFinite(span)) { 30682 return; 30683 } 30684 // User may set axis min 0 and data are all negative 30685 // FIXME If it needs to reverse ? 30686 if (span < 0) { 30687 span = -span; 30688 extent.reverse(); 30689 } 30690 30691 var result = intervalScaleNiceTicks( 30692 extent, splitNumber, minInterval, maxInterval 30693 ); 30694 30695 this._intervalPrecision = result.intervalPrecision; 30696 this._interval = result.interval; 30697 this._niceExtent = result.niceTickExtent; 30698 }, 30699 30700 /** 30701 * Nice extent. 30702 * @param {Object} opt 30703 * @param {number} [opt.splitNumber = 5] Given approx tick number 30704 * @param {boolean} [opt.fixMin=false] 30705 * @param {boolean} [opt.fixMax=false] 30706 * @param {boolean} [opt.minInterval] 30707 * @param {boolean} [opt.maxInterval] 30708 */ 30709 niceExtent: function (opt) { 30710 var extent = this._extent; 30711 // If extent start and end are same, expand them 30712 if (extent[0] === extent[1]) { 30713 if (extent[0] !== 0) { 30714 // Expand extent 30715 var expandSize = extent[0]; 30716 // In the fowllowing case 30717 // Axis has been fixed max 100 30718 // Plus data are all 100 and axis extent are [100, 100]. 30719 // Extend to the both side will cause expanded max is larger than fixed max. 30720 // So only expand to the smaller side. 30721 if (!opt.fixMax) { 30722 extent[1] += expandSize / 2; 30723 extent[0] -= expandSize / 2; 30724 } 30725 else { 30726 extent[0] -= expandSize / 2; 30727 } 30728 } 30729 else { 30730 extent[1] = 1; 30731 } 30732 } 30733 var span = extent[1] - extent[0]; 30734 // If there are no data and extent are [Infinity, -Infinity] 30735 if (!isFinite(span)) { 30736 extent[0] = 0; 30737 extent[1] = 1; 30738 } 30739 30740 this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); 30741 30742 // var extent = this._extent; 30743 var interval = this._interval; 30744 30745 if (!opt.fixMin) { 30746 extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval); 30747 } 30748 if (!opt.fixMax) { 30749 extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval); 30750 } 30751 } 30752 }); 30753 30754 /** 30755 * @return {module:echarts/scale/Time} 30756 */ 30757 IntervalScale.create = function () { 30758 return new IntervalScale(); 30759 }; 30760 30761 /* 30762* Licensed to the Apache Software Foundation (ASF) under one 30763* or more contributor license agreements. See the NOTICE file 30764* distributed with this work for additional information 30765* regarding copyright ownership. The ASF licenses this file 30766* to you under the Apache License, Version 2.0 (the 30767* "License"); you may not use this file except in compliance 30768* with the License. You may obtain a copy of the License at 30769* 30770* http://www.apache.org/licenses/LICENSE-2.0 30771* 30772* Unless required by applicable law or agreed to in writing, 30773* software distributed under the License is distributed on an 30774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 30775* KIND, either express or implied. See the License for the 30776* specific language governing permissions and limitations 30777* under the License. 30778*/ 30779 30780 var STACK_PREFIX = '__ec_stack_'; 30781 var LARGE_BAR_MIN_WIDTH = 0.5; 30782 30783 var LargeArr = typeof Float32Array !== 'undefined' ? Float32Array : Array; 30784
vendor: 8,296 bytes, lines 30785-30999
30785 function getSeriesStackId(seriesModel) { 30786 return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex; 30787 } 30788 30789 function getAxisKey(axis) { 30790 return axis.dim + axis.index; 30791 } 30792 30793 /** 30794 * @param {Object} opt 30795 * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently. 30796 * @param {number} opt.count Positive interger. 30797 * @param {number} [opt.barWidth] 30798 * @param {number} [opt.barMaxWidth] 30799 * @param {number} [opt.barGap] 30800 * @param {number} [opt.barCategoryGap] 30801 * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined. 30802 */ 30803 function getLayoutOnAxis(opt) { 30804 var params = []; 30805 var baseAxis = opt.axis; 30806 var axisKey = 'axis0'; 30807 30808 if (baseAxis.type !== 'category') { 30809 return; 30810 } 30811 var bandWidth = baseAxis.getBandWidth(); 30812 30813 for (var i = 0; i < opt.count || 0; i++) { 30814 params.push(defaults({ 30815 bandWidth: bandWidth, 30816 axisKey: axisKey, 30817 stackId: STACK_PREFIX + i 30818 }, opt)); 30819 } 30820 var widthAndOffsets = doCalBarWidthAndOffset(params); 30821 30822 var result = []; 30823 for (var i = 0; i < opt.count; i++) { 30824 var item = widthAndOffsets[axisKey][STACK_PREFIX + i]; 30825 item.offsetCenter = item.offset + item.width / 2; 30826 result.push(item); 30827 } 30828 30829 return result; 30830 } 30831 30832 function prepareLayoutBarSeries(seriesType, ecModel) { 30833 var seriesModels = []; 30834 ecModel.eachSeriesByType(seriesType, function (seriesModel) { 30835 // Check series coordinate, do layout for cartesian2d only 30836 if (isOnCartesian(seriesModel) && !isInLargeMode(seriesModel)) { 30837 seriesModels.push(seriesModel); 30838 } 30839 }); 30840 return seriesModels; 30841 } 30842 30843 function makeColumnLayout(barSeries) { 30844 var seriesInfoList = []; 30845 each$1(barSeries, function (seriesModel) { 30846 var data = seriesModel.getData(); 30847 var cartesian = seriesModel.coordinateSystem; 30848 var baseAxis = cartesian.getBaseAxis(); 30849 var axisExtent = baseAxis.getExtent(); 30850 var bandWidth = baseAxis.type === 'category' 30851 ? baseAxis.getBandWidth() 30852 : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count()); 30853 30854 var barWidth = parsePercent$1( 30855 seriesModel.get('barWidth'), bandWidth 30856 ); 30857 var barMaxWidth = parsePercent$1( 30858 seriesModel.get('barMaxWidth'), bandWidth 30859 ); 30860 var barGap = seriesModel.get('barGap'); 30861 var barCategoryGap = seriesModel.get('barCategoryGap'); 30862 30863 seriesInfoList.push({ 30864 bandWidth: bandWidth, 30865 barWidth: barWidth, 30866 barMaxWidth: barMaxWidth, 30867 barGap: barGap, 30868 barCategoryGap: barCategoryGap, 30869 axisKey: getAxisKey(baseAxis), 30870 stackId: getSeriesStackId(seriesModel) 30871 }); 30872 }); 30873 30874 return doCalBarWidthAndOffset(seriesInfoList); 30875 } 30876 30877 function doCalBarWidthAndOffset(seriesInfoList) { 30878 // Columns info on each category axis. Key is cartesian name 30879 var columnsMap = {}; 30880 30881 each$1(seriesInfoList, function (seriesInfo, idx) { 30882 var axisKey = seriesInfo.axisKey; 30883 var bandWidth = seriesInfo.bandWidth; 30884 var columnsOnAxis = columnsMap[axisKey] || { 30885 bandWidth: bandWidth, 30886 remainedWidth: bandWidth, 30887 autoWidthCount: 0, 30888 categoryGap: '20%', 30889 gap: '30%', 30890 stacks: {} 30891 }; 30892 var stacks = columnsOnAxis.stacks; 30893 columnsMap[axisKey] = columnsOnAxis; 30894 30895 var stackId = seriesInfo.stackId; 30896 30897 if (!stacks[stackId]) { 30898 columnsOnAxis.autoWidthCount++; 30899 } 30900 stacks[stackId] = stacks[stackId] || { 30901 width: 0, 30902 maxWidth: 0 30903 }; 30904 30905 // Caution: In a single coordinate system, these barGrid attributes 30906 // will be shared by series. Consider that they have default values, 30907 // only the attributes set on the last series will work. 30908 // Do not change this fact unless there will be a break change. 30909 30910 // TODO 30911 var barWidth = seriesInfo.barWidth; 30912 if (barWidth && !stacks[stackId].width) { 30913 // See #6312, do not restrict width. 30914 stacks[stackId].width = barWidth; 30915 barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); 30916 columnsOnAxis.remainedWidth -= barWidth; 30917 } 30918 30919 var barMaxWidth = seriesInfo.barMaxWidth; 30920 barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); 30921 var barGap = seriesInfo.barGap; 30922 (barGap != null) && (columnsOnAxis.gap = barGap); 30923 var barCategoryGap = seriesInfo.barCategoryGap; 30924 (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap); 30925 }); 30926 30927 var result = {}; 30928 30929 each$1(columnsMap, function (columnsOnAxis, coordSysName) { 30930 30931 result[coordSysName] = {}; 30932 30933 var stacks = columnsOnAxis.stacks; 30934 var bandWidth = columnsOnAxis.bandWidth; 30935 var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth); 30936 var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1); 30937 30938 var remainedWidth = columnsOnAxis.remainedWidth; 30939 var autoWidthCount = columnsOnAxis.autoWidthCount; 30940 var autoWidth = (remainedWidth - categoryGap) 30941 / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); 30942 autoWidth = Math.max(autoWidth, 0); 30943 30944 // Find if any auto calculated bar exceeded maxBarWidth 30945 each$1(stacks, function (column, stack) { 30946 var maxWidth = column.maxWidth; 30947 if (maxWidth && maxWidth < autoWidth) { 30948 maxWidth = Math.min(maxWidth, remainedWidth); 30949 if (column.width) { 30950 maxWidth = Math.min(maxWidth, column.width); 30951 } 30952 remainedWidth -= maxWidth; 30953 column.width = maxWidth; 30954 autoWidthCount--; 30955 } 30956 }); 30957 30958 // Recalculate width again 30959 autoWidth = (remainedWidth - categoryGap) 30960 / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); 30961 autoWidth = Math.max(autoWidth, 0); 30962 30963 var widthSum = 0; 30964 var lastColumn; 30965 each$1(stacks, function (column, idx) { 30966 if (!column.width) { 30967 column.width = autoWidth; 30968 } 30969 lastColumn = column; 30970 widthSum += column.width * (1 + barGapPercent); 30971 }); 30972 if (lastColumn) { 30973 widthSum -= lastColumn.width * barGapPercent; 30974 } 30975 30976 var offset = -widthSum / 2; 30977 each$1(stacks, function (column, stackId) { 30978 result[coordSysName][stackId] = result[coordSysName][stackId] || { 30979 offset: offset, 30980 width: column.width 30981 }; 30982 30983 offset += column.width * (1 + barGapPercent); 30984 }); 30985 }); 30986 30987 return result; 30988 } 30989 30990 /** 30991 * @param {Object} barWidthAndOffset The result of makeColumnLayout 30992 * @param {module:echarts/coord/Axis} axis 30993 * @param {module:echarts/model/Series} [seriesModel] If not provided, return all. 30994 * @return {Object} {stackId: {offset, width}} or {offset, width} if seriesModel provided. 30995 */ 30996 function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) { 30997 if (barWidthAndOffset && axis) { 30998 var result = barWidthAndOffset[getAxisKey(axis)]; 30999 if (result != null && seriesModel != null) {
31000 result = result[getSeriesStackId(seriesModel)]; 31001 } 31002 return result; 31003 } 31004 } 31005 31006 /** 31007 * @param {string} seriesType 31008 * @param {module:echarts/model/Global} ecModel 31009 */ 31010 function layout(seriesType, ecModel) { 31011 31012 var seriesModels = prepareLayoutBarSeries(seriesType, ecModel); 31013 var barWidthAndOffset = makeColumnLayout(seriesModels); 31014 31015 var lastStackCoords = {}; 31016 each$1(seriesModels, function (seriesModel) { 31017 31018 var data = seriesModel.getData(); 31019 var cartesian = seriesModel.coordinateSystem; 31020 var baseAxis = cartesian.getBaseAxis(); 31021 31022 var stackId = getSeriesStackId(seriesModel); 31023 var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId]; 31024 var columnOffset = columnLayoutInfo.offset; 31025 var columnWidth = columnLayoutInfo.width; 31026 var valueAxis = cartesian.getOtherAxis(baseAxis); 31027 31028 var barMinHeight = seriesModel.get('barMinHeight') || 0; 31029 31030 lastStackCoords[stackId] = lastStackCoords[stackId] || []; 31031 data.setLayout({ 31032 offset: columnOffset, 31033 size: columnWidth 31034 }); 31035 31036 var valueDim = data.mapDimension(valueAxis.dim); 31037 var baseDim = data.mapDimension(baseAxis.dim); 31038 var stacked = isDimensionStacked(data, valueDim /*, baseDim*/); 31039 var isValueAxisH = valueAxis.isHorizontal(); 31040 31041 var valueAxisStart = getValueAxisStart(baseAxis, valueAxis, stacked); 31042 31043 for (var idx = 0, len = data.count(); idx < len; idx++) { 31044 var value = data.get(valueDim, idx); 31045 var baseValue = data.get(baseDim, idx); 31046 31047 if (isNaN(value)) { 31048 continue; 31049 } 31050 31051 var sign = value >= 0 ? 'p' : 'n'; 31052 var baseCoord = valueAxisStart; 31053 31054 // Because of the barMinHeight, we can not use the value in 31055 // stackResultDimension directly. 31056 if (stacked) { 31057 // Only ordinal axis can be stacked. 31058 if (!lastStackCoords[stackId][baseValue]) { 31059 lastStackCoords[stackId][baseValue] = { 31060 p: valueAxisStart, // Positive stack 31061 n: valueAxisStart // Negative stack 31062 }; 31063 } 31064 // Should also consider #4243 31065 baseCoord = lastStackCoords[stackId][baseValue][sign]; 31066 } 31067 31068 var x; 31069 var y; 31070 var width; 31071 var height; 31072 31073 if (isValueAxisH) { 31074 var coord = cartesian.dataToPoint([value, baseValue]); 31075 x = baseCoord; 31076 y = coord[1] + columnOffset; 31077 width = coord[0] - valueAxisStart; 31078 height = columnWidth; 31079 31080 if (Math.abs(width) < barMinHeight) { 31081 width = (width < 0 ? -1 : 1) * barMinHeight; 31082 } 31083 stacked && (lastStackCoords[stackId][baseValue][sign] += width); 31084 } 31085 else { 31086 var coord = cartesian.dataToPoint([baseValue, value]); 31087 x = coord[0] + columnOffset; 31088 y = baseCoord; 31089 width = columnWidth; 31090 height = coord[1] - valueAxisStart; 31091 31092 if (Math.abs(height) < barMinHeight) { 31093 // Include zero to has a positive bar 31094 height = (height <= 0 ? -1 : 1) * barMinHeight; 31095 } 31096 stacked && (lastStackCoords[stackId][baseValue][sign] += height); 31097 } 31098 31099 data.setItemLayout(idx, { 31100 x: x, 31101 y: y, 31102 width: width, 31103 height: height 31104 }); 31105 } 31106 31107 }, this); 31108 } 31109
31110// TODO: Do not support stack in large mode yet. 31111 var largeLayout = { 31112 31113 seriesType: 'bar', 31114 31115 plan: createRenderPlanner(), 31116 31117 reset: function (seriesModel) { 31118 if (!isOnCartesian(seriesModel) || !isInLargeMode(seriesModel)) { 31119 return; 31120 } 31121 31122 var data = seriesModel.getData(); 31123 var cartesian = seriesModel.coordinateSystem; 31124 var baseAxis = cartesian.getBaseAxis(); 31125 var valueAxis = cartesian.getOtherAxis(baseAxis); 31126 var valueDim = data.mapDimension(valueAxis.dim); 31127 var baseDim = data.mapDimension(baseAxis.dim); 31128 var valueAxisHorizontal = valueAxis.isHorizontal(); 31129 var valueDimIdx = valueAxisHorizontal ? 0 : 1; 31130 31131 var barWidth = retrieveColumnLayout( 31132 makeColumnLayout([seriesModel]), baseAxis, seriesModel 31133 ).width; 31134 if (!(barWidth > LARGE_BAR_MIN_WIDTH)) { // jshint ignore:line 31135 barWidth = LARGE_BAR_MIN_WIDTH; 31136 } 31137 31138 return {progress: progress}; 31139 31140 function progress(params, data) { 31141 var largePoints = new LargeArr(params.count * 2); 31142 var dataIndex; 31143 var coord = []; 31144 var valuePair = []; 31145 var offset = 0; 31146 31147 while ((dataIndex = params.next()) != null) { 31148 valuePair[valueDimIdx] = data.get(valueDim, dataIndex); 31149 valuePair[1 - valueDimIdx] = data.get(baseDim, dataIndex); 31150 31151 coord = cartesian.dataToPoint(valuePair, null, coord); 31152 largePoints[offset++] = coord[0]; 31153 largePoints[offset++] = coord[1]; 31154 } 31155 31156 data.setLayout({ 31157 largePoints: largePoints, 31158 barWidth: barWidth, 31159 valueAxisStart: getValueAxisStart(baseAxis, valueAxis, false), 31160 valueAxisHorizontal: valueAxisHorizontal 31161 }); 31162 } 31163 } 31164 }; 31165 31166 function isOnCartesian(seriesModel) { 31167 return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d'; 31168 } 31169 31170 function isInLargeMode(seriesModel) { 31171 return seriesModel.pipelineContext && seriesModel.pipelineContext.large; 31172 } 31173 31174 function getValueAxisStart(baseAxis, valueAxis, stacked) { 31175 return ( 31176 indexOf(baseAxis.getAxesOnZeroOf(), valueAxis) >= 0 31177 || stacked 31178 ) 31179 ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)) 31180 : valueAxis.getGlobalExtent()[0]; 31181 } 31182 31183 /* 31184* Licensed to the Apache Software Foundation (ASF) under one 31185* or more contributor license agreements. See the NOTICE file 31186* distributed with this work for additional information 31187* regarding copyright ownership. The ASF licenses this file 31188* to you under the Apache License, Version 2.0 (the 31189* "License"); you may not use this file except in compliance 31190* with the License. You may obtain a copy of the License at 31191* 31192* http://www.apache.org/licenses/LICENSE-2.0 31193* 31194* Unless required by applicable law or agreed to in writing, 31195* software distributed under the License is distributed on an 31196* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 31197* KIND, either express or implied. See the License for the 31198* specific language governing permissions and limitations 31199* under the License. 31200*/ 31201 31202 /* 31203* The `scaleLevels` references to d3.js. The use of the source 31204* code of this file is also subject to the terms and consitions 31205* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>). 31206*/ 31207 31208// [About UTC and local time zone]: 31209// In most cases, `number.parseDate` will treat input data string as local time 31210// (except time zone is specified in time string). And `format.formateTime` returns 31211// local time by default. option.useUTC is false by default. This design have 31212// concidered these common case: 31213// (1) Time that is persistent in server is in UTC, but it is needed to be diplayed 31214// in local time by default. 31215// (2) By default, the input data string (e.g., '2011-01-02') should be displayed 31216// as its original time, without any time difference. 31217 31218 var intervalScaleProto = IntervalScale.prototype; 31219
31220 var mathCeil = Math.ceil; 31221 var mathFloor = Math.floor; 31222 var ONE_SECOND = 1000; 31223 var ONE_MINUTE = ONE_SECOND * 60; 31224 var ONE_HOUR = ONE_MINUTE * 60; 31225 var ONE_DAY = ONE_HOUR * 24; 31226 31227// FIXME Ã¥â¦Â¬Ã§â¨ï¼Ÿ 31228 var bisect = function (a, x, lo, hi) { 31229 while (lo < hi) { 31230 var mid = lo + hi >>> 1; 31231 if (a[mid][1] < x) { 31232 lo = mid + 1; 31233 } 31234 else { 31235 hi = mid; 31236 } 31237 } 31238 return lo; 31239 }; 31240 31241 /** 31242 * @alias module:echarts/coord/scale/Time 31243 * @constructor 31244 */ 31245 var TimeScale = IntervalScale.extend({ 31246 type: 'time', 31247 31248 /** 31249 * @override 31250 */ 31251 getLabel: function (val) { 31252 var stepLvl = this._stepLvl; 31253 31254 var date = new Date(val); 31255 31256 return formatTime(stepLvl[0], date, this.getSetting('useUTC')); 31257 }, 31258 31259 /** 31260 * @override 31261 */ 31262 niceExtent: function (opt) { 31263 var extent = this._extent; 31264 // If extent start and end are same, expand them 31265 if (extent[0] === extent[1]) { 31266 // Expand extent 31267 extent[0] -= ONE_DAY; 31268 extent[1] += ONE_DAY; 31269 } 31270 // If there are no data and extent are [Infinity, -Infinity] 31271 if (extent[1] === -Infinity && extent[0] === Infinity) { 31272 var d = new Date(); 31273 extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate()); 31274 extent[0] = extent[1] - ONE_DAY; 31275 } 31276 31277 this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); 31278 31279 // var extent = this._extent; 31280 var interval = this._interval; 31281 31282 if (!opt.fixMin) { 31283 extent[0] = round$1(mathFloor(extent[0] / interval) * interval); 31284 } 31285 if (!opt.fixMax) { 31286 extent[1] = round$1(mathCeil(extent[1] / interval) * interval); 31287 } 31288 }, 31289 31290 /** 31291 * @override 31292 */ 31293 niceTicks: function (approxTickNum, minInterval, maxInterval) { 31294 approxTickNum = approxTickNum || 10; 31295 31296 var extent = this._extent; 31297 var span = extent[1] - extent[0]; 31298 var approxInterval = span / approxTickNum; 31299 31300 if (minInterval != null && approxInterval < minInterval) { 31301 approxInterval = minInterval; 31302 } 31303 if (maxInterval != null && approxInterval > maxInterval) { 31304 approxInterval = maxInterval; 31305 } 31306 31307 var scaleLevelsLen = scaleLevels.length; 31308 var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen); 31309 31310 var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)]; 31311 var interval = level[1]; 31312 // Same with interval scale if span is much larger than 1 year 31313 if (level[0] === 'year') { 31314 var yearSpan = span / interval; 31315 31316 // From "Nice Numbers for Graph Labels" of Graphic Gems 31317 // var niceYearSpan = numberUtil.nice(yearSpan, false); 31318 var yearStep = nice(yearSpan / approxTickNum, true); 31319 31320 interval *= yearStep; 31321 } 31322 31323 var timezoneOffset = this.getSetting('useUTC') 31324 ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000; 31325 var niceExtent = [ 31326 Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset), 31327 Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset) 31328 ]; 31329 31330 fixExtent(niceExtent, extent); 31331 31332 this._stepLvl = level; 31333 // Interval will be used in getTicks 31334 this._interval = interval; 31335 this._niceExtent = niceExtent; 31336 }, 31337 31338 parse: function (val) { 31339 // val might be float. 31340 return +parseDate(val); 31341 } 31342 });
31343 31344 each$1(['contain', 'normalize'], function (methodName) { 31345 TimeScale.prototype[methodName] = function (val) { 31346 return intervalScaleProto[methodName].call(this, this.parse(val)); 31347 }; 31348 }); 31349 31350// Steps from d3, see the license statement at the top of this file. 31351 var scaleLevels = [ 31352 // Format interval 31353 ['hh:mm:ss', ONE_SECOND], // 1s 31354 ['hh:mm:ss', ONE_SECOND * 5], // 5s 31355 ['hh:mm:ss', ONE_SECOND * 10], // 10s 31356 ['hh:mm:ss', ONE_SECOND * 15], // 15s 31357 ['hh:mm:ss', ONE_SECOND * 30], // 30s 31358 ['hh:mm\nMM-dd', ONE_MINUTE], // 1m 31359 ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m 31360 ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m 31361 ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m 31362 ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m 31363 ['hh:mm\nMM-dd', ONE_HOUR], // 1h 31364 ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h 31365 ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h 31366 ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h 31367 ['MM-dd\nyyyy', ONE_DAY], // 1d 31368 ['MM-dd\nyyyy', ONE_DAY * 2], // 2d 31369 ['MM-dd\nyyyy', ONE_DAY * 3], // 3d 31370 ['MM-dd\nyyyy', ONE_DAY * 4], // 4d 31371 ['MM-dd\nyyyy', ONE_DAY * 5], // 5d 31372 ['MM-dd\nyyyy', ONE_DAY * 6], // 6d 31373 ['week', ONE_DAY * 7], // 7d 31374 ['MM-dd\nyyyy', ONE_DAY * 10], // 10d 31375 ['week', ONE_DAY * 14], // 2w 31376 ['week', ONE_DAY * 21], // 3w 31377 ['month', ONE_DAY * 31], // 1M 31378 ['week', ONE_DAY * 42], // 6w 31379 ['month', ONE_DAY * 62], // 2M 31380 ['week', ONE_DAY * 42], // 10w 31381 ['quarter', ONE_DAY * 380 / 4], // 3M 31382 ['month', ONE_DAY * 31 * 4], // 4M 31383 ['month', ONE_DAY * 31 * 5], // 5M 31384 ['half-year', ONE_DAY * 380 / 2], // 6M 31385 ['month', ONE_DAY * 31 * 8], // 8M 31386 ['month', ONE_DAY * 31 * 10], // 10M 31387 ['year', ONE_DAY * 380] // 1Y 31388 ]; 31389 31390 /** 31391 * @param {module:echarts/model/Model} 31392 * @return {module:echarts/scale/Time} 31393 */ 31394 TimeScale.create = function (model) { 31395 return new TimeScale({useUTC: model.ecModel.get('useUTC')}); 31396 }; 31397 31398 /* 31399* Licensed to the Apache Software Foundation (ASF) under one 31400* or more contributor license agreements. See the NOTICE file 31401* distributed with this work for additional information 31402* regarding copyright ownership. The ASF licenses this file 31403* to you under the Apache License, Version 2.0 (the 31404* "License"); you may not use this file except in compliance 31405* with the License. You may obtain a copy of the License at 31406* 31407* http://www.apache.org/licenses/LICENSE-2.0 31408* 31409* Unless required by applicable law or agreed to in writing, 31410* software distributed under the License is distributed on an 31411* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 31412* KIND, either express or implied. See the License for the 31413* specific language governing permissions and limitations 31414* under the License. 31415*/ 31416 31417 /** 31418 * Log scale 31419 * @module echarts/scale/Log 31420 */ 31421 31422// Use some method of IntervalScale 31423 var scaleProto$1 = Scale.prototype; 31424 var intervalScaleProto$1 = IntervalScale.prototype; 31425 31426 var getPrecisionSafe$1 = getPrecisionSafe; 31427 var roundingErrorFix = round$1; 31428 31429 var mathFloor$1 = Math.floor; 31430 var mathCeil$1 = Math.ceil; 31431 var mathPow$1 = Math.pow; 31432 31433 var mathLog = Math.log; 31434 31435 var LogScale = Scale.extend({ 31436 31437 type: 'log', 31438 31439 base: 10, 31440 31441 $constructor: function () { 31442 Scale.apply(this, arguments); 31443 this._originalScale = new IntervalScale(); 31444 }, 31445 31446 /** 31447 * @return {Array.<number>} 31448 */ 31449 getTicks: function () { 31450 var originalScale = this._originalScale; 31451 var extent = this._extent; 31452 var originalExtent = originalScale.getExtent(); 31453 31454 return map(intervalScaleProto$1.getTicks.call(this), function (val) { 31455 var powVal = round$1(mathPow$1(this.base, val)); 31456 31457 // Fix #4158 31458 powVal = (val === extent[0] && originalScale.__fixMin) 31459 ? fixRoundingError(powVal, originalExtent[0]) 31460 : powVal; 31461 powVal = (val === extent[1] && originalScale.__fixMax) 31462 ? fixRoundingError(powVal, originalExtent[1]) 31463 : powVal; 31464 31465 return powVal; 31466 }, this); 31467 }, 31468 31469 /** 31470 * @param {number} val 31471 * @return {string} 31472 */ 31473 getLabel: intervalScaleProto$1.getLabel, 31474 31475 /** 31476 * @param {number} val 31477 * @return {number} 31478 */ 31479 scale: function (val) { 31480 val = scaleProto$1.scale.call(this, val); 31481 return mathPow$1(this.base, val); 31482 }, 31483 31484 /** 31485 * @param {number} start 31486 * @param {number} end 31487 */ 31488 setExtent: function (start, end) { 31489 var base = this.base; 31490 start = mathLog(start) / mathLog(base); 31491 end = mathLog(end) / mathLog(base); 31492 intervalScaleProto$1.setExtent.call(this, start, end); 31493 }, 31494 31495 /** 31496 * @return {number} end 31497 */ 31498 getExtent: function () { 31499 var base = this.base; 31500 var extent = scaleProto$1.getExtent.call(this); 31501 extent[0] = mathPow$1(base, extent[0]); 31502 extent[1] = mathPow$1(base, extent[1]); 31503 31504 // Fix #4158 31505 var originalScale = this._originalScale; 31506 var originalExtent = originalScale.getExtent(); 31507 originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0])); 31508 originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1])); 31509 31510 return extent; 31511 }, 31512 31513 /** 31514 * @param {Array.<number>} extent 31515 */ 31516 unionExtent: function (extent) { 31517 this._originalScale.unionExtent(extent); 31518 31519 var base = this.base; 31520 extent[0] = mathLog(extent[0]) / mathLog(base); 31521 extent[1] = mathLog(extent[1]) / mathLog(base); 31522 scaleProto$1.unionExtent.call(this, extent); 31523 }, 31524 31525 /** 31526 * @override 31527 */ 31528 unionExtentFromData: function (data, dim) { 31529 // TODO 31530 // filter value that <= 0 31531 this.unionExtent(data.getApproximateExtent(dim)); 31532 }, 31533 31534 /** 31535 * Update interval and extent of intervals for nice ticks 31536 * @param {number} [approxTickNum = 10] Given approx tick number 31537 */ 31538 niceTicks: function (approxTickNum) { 31539 approxTickNum = approxTickNum || 10; 31540 var extent = this._extent; 31541 var span = extent[1] - extent[0]; 31542 if (span === Infinity || span <= 0) { 31543 return; 31544 } 31545 31546 var interval = quantity(span); 31547 var err = approxTickNum / span * interval; 31548 31549 // Filter ticks to get closer to the desired count. 31550 if (err <= 0.5) { 31551 interval *= 10; 31552 } 31553 31554 // Interval should be integer 31555 while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) { 31556 interval *= 10; 31557 } 31558 31559 var niceExtent = [ 31560 round$1(mathCeil$1(extent[0] / interval) * interval), 31561 round$1(mathFloor$1(extent[1] / interval) * interval) 31562 ]; 31563 31564 this._interval = interval; 31565 this._niceExtent = niceExtent; 31566 }, 31567 31568 /** 31569 * Nice extent. 31570 * @override 31571 */ 31572 niceExtent: function (opt) { 31573 intervalScaleProto$1.niceExtent.call(this, opt); 31574 31575 var originalScale = this._originalScale; 31576 originalScale.__fixMin = opt.fixMin; 31577 originalScale.__fixMax = opt.fixMax; 31578 } 31579 31580 });
31581 31582 each$1(['contain', 'normalize'], function (methodName) { 31583 LogScale.prototype[methodName] = function (val) { 31584 val = mathLog(val) / mathLog(this.base); 31585 return scaleProto$1[methodName].call(this, val); 31586 }; 31587 }); 31588 31589 LogScale.create = function () { 31590 return new LogScale(); 31591 }; 31592 31593 function fixRoundingError(val, originalVal) { 31594 return roundingErrorFix(val, getPrecisionSafe$1(originalVal)); 31595 } 31596 31597 /* 31598* Licensed to the Apache Software Foundation (ASF) under one 31599* or more contributor license agreements. See the NOTICE file 31600* distributed with this work for additional information 31601* regarding copyright ownership. The ASF licenses this file 31602* to you under the Apache License, Version 2.0 (the 31603* "License"); you may not use this file except in compliance 31604* with the License. You may obtain a copy of the License at 31605* 31606* http://www.apache.org/licenses/LICENSE-2.0 31607* 31608* Unless required by applicable law or agreed to in writing, 31609* software distributed under the License is distributed on an 31610* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 31611* KIND, either express or implied. See the License for the 31612* specific language governing permissions and limitations 31613* under the License. 31614*/ 31615 31616 /** 31617 * Get axis scale extent before niced. 31618 * Item of returned array can only be number (including Infinity and NaN). 31619 */ 31620 function getScaleExtent(scale, model) { 31621 var scaleType = scale.type; 31622 31623 var min = model.getMin(); 31624 var max = model.getMax(); 31625 var fixMin = min != null; 31626 var fixMax = max != null; 31627 var originalExtent = scale.getExtent(); 31628 31629 var axisDataLen; 31630 var boundaryGap; 31631 var span; 31632 if (scaleType === 'ordinal') { 31633 axisDataLen = model.getCategories().length; 31634 } 31635 else { 31636 boundaryGap = model.get('boundaryGap'); 31637 if (!isArray(boundaryGap)) { 31638 boundaryGap = [boundaryGap || 0, boundaryGap || 0]; 31639 } 31640 if (typeof boundaryGap[0] === 'boolean') { 31641 if (__DEV__) { 31642 console.warn('Boolean type for boundaryGap is only ' 31643 + 'allowed for ordinal axis. Please use string in ' 31644 + 'percentage instead, e.g., "20%". Currently, ' 31645 + 'boundaryGap is set to be 0.'); 31646 } 31647 boundaryGap = [0, 0]; 31648 } 31649 boundaryGap[0] = parsePercent$1(boundaryGap[0], 1); 31650 boundaryGap[1] = parsePercent$1(boundaryGap[1], 1); 31651 span = (originalExtent[1] - originalExtent[0]) 31652 || Math.abs(originalExtent[0]); 31653 } 31654 31655 // Notice: When min/max is not set (that is, when there are null/undefined, 31656 // which is the most common case), these cases should be ensured: 31657 // (1) For 'ordinal', show all axis.data. 31658 // (2) For others: 31659 // + `boundaryGap` is applied (if min/max set, boundaryGap is 31660 // disabled). 31661 // + If `needCrossZero`, min/max should be zero, otherwise, min/max should 31662 // be the result that originalExtent enlarged by boundaryGap. 31663 // (3) If no data, it should be ensured that `scale.setBlank` is set. 31664 31665 // FIXME 31666 // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used? 31667 // (2) When `needCrossZero` and all data is positive/negative, should it be ensured 31668 // that the results processed by boundaryGap are positive/negative? 31669 31670 if (min == null) { 31671 min = scaleType === 'ordinal' 31672 ? (axisDataLen ? 0 : NaN) 31673 : originalExtent[0] - boundaryGap[0] * span; 31674 } 31675 if (max == null) { 31676 max = scaleType === 'ordinal' 31677 ? (axisDataLen ? axisDataLen - 1 : NaN) 31678 : originalExtent[1] + boundaryGap[1] * span; 31679 } 31680 31681 if (min === 'dataMin') { 31682 min = originalExtent[0]; 31683 } 31684 else if (typeof min === 'function') { 31685 min = min({ 31686 min: originalExtent[0], 31687 max: originalExtent[1] 31688 }); 31689 } 31690 31691 if (max === 'dataMax') { 31692 max = originalExtent[1];
31693 } 31694 else if (typeof max === 'function') { 31695 max = max({ 31696 min: originalExtent[0], 31697 max: originalExtent[1] 31698 }); 31699 } 31700 31701 (min == null || !isFinite(min)) && (min = NaN); 31702 (max == null || !isFinite(max)) && (max = NaN); 31703 31704 scale.setBlank( 31705 eqNaN(min) 31706 || eqNaN(max) 31707 || (scaleType === 'ordinal' && !scale.getOrdinalMeta().categories.length) 31708 ); 31709 31710 // Evaluate if axis needs cross zero 31711 if (model.getNeedCrossZero()) { 31712 // Axis is over zero and min is not set 31713 if (min > 0 && max > 0 && !fixMin) { 31714 min = 0; 31715 } 31716 // Axis is under zero and max is not set 31717 if (min < 0 && max < 0 && !fixMax) { 31718 max = 0; 31719 } 31720 } 31721 31722 // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis 31723 // is base axis 31724 // FIXME 31725 // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly. 31726 // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent? 31727 // Should not depend on series type `bar`? 31728 // (3) Fix that might overlap when using dataZoom. 31729 // (4) Consider other chart types using `barGrid`? 31730 // See #6728, #4862, `test/bar-overflow-time-plot.html` 31731 var ecModel = model.ecModel; 31732 if (ecModel && (scaleType === 'time' /*|| scaleType === 'interval' */)) { 31733 var barSeriesModels = prepareLayoutBarSeries('bar', ecModel); 31734 var isBaseAxisAndHasBarSeries; 31735 31736 each$1(barSeriesModels, function (seriesModel) { 31737 isBaseAxisAndHasBarSeries |= seriesModel.getBaseAxis() === model.axis; 31738 }); 31739 31740 if (isBaseAxisAndHasBarSeries) { 31741 // Calculate placement of bars on axis 31742 var barWidthAndOffset = makeColumnLayout(barSeriesModels); 31743 31744 // Adjust axis min and max to account for overflow 31745 var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset); 31746 min = adjustedScale.min; 31747 max = adjustedScale.max; 31748 } 31749 } 31750 31751 return [min, max]; 31752 } 31753 31754 function adjustScaleForOverflow(min, max, model, barWidthAndOffset) { 31755 31756 // Get Axis Length 31757 var axisExtent = model.axis.getExtent(); 31758 var axisLength = axisExtent[1] - axisExtent[0]; 31759 31760 // Get bars on current base axis and calculate min and max overflow 31761 var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis); 31762 if (barsOnCurrentAxis === undefined) { 31763 return {min: min, max: max}; 31764 } 31765 31766 var minOverflow = Infinity; 31767 each$1(barsOnCurrentAxis, function (item) { 31768 minOverflow = Math.min(item.offset, minOverflow); 31769 }); 31770 var maxOverflow = -Infinity; 31771 each$1(barsOnCurrentAxis, function (item) { 31772 maxOverflow = Math.max(item.offset + item.width, maxOverflow); 31773 }); 31774 minOverflow = Math.abs(minOverflow); 31775 maxOverflow = Math.abs(maxOverflow); 31776 var totalOverFlow = minOverflow + maxOverflow; 31777 31778 // Calulate required buffer based on old range and overflow 31779 var oldRange = max - min; 31780 var oldRangePercentOfNew = (1 - (minOverflow + maxOverflow) / axisLength); 31781 var overflowBuffer = ((oldRange / oldRangePercentOfNew) - oldRange); 31782 31783 max += overflowBuffer * (maxOverflow / totalOverFlow); 31784 min -= overflowBuffer * (minOverflow / totalOverFlow); 31785 31786 return {min: min, max: max}; 31787 } 31788 31789 function niceScaleExtent(scale, model) { 31790 var extent = getScaleExtent(scale, model); 31791 var fixMin = model.getMin() != null; 31792 var fixMax = model.getMax() != null; 31793 var splitNumber = model.get('splitNumber'); 31794 31795 if (scale.type === 'log') { 31796 scale.base = model.get('logBase'); 31797 } 31798 31799 var scaleType = scale.type; 31800 scale.setExtent(extent[0], extent[1]); 31801 scale.niceExtent({ 31802 splitNumber: splitNumber, 31803 fixMin: fixMin, 31804 fixMax: fixMax, 31805 minInterval: (scaleType === 'interval' || scaleType === 'time') 31806 ? model.get('minInterval') : null, 31807 maxInterval: (scaleType === 'interval' || scaleType === 'time') 31808 ? model.get('maxInterval') : null 31809 }); 31810 31811 // If some one specified the min, max. And the default calculated interval 31812 // is not good enough. He can specify the interval. It is often appeared 31813 // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard 31814 // to be 60. 31815 // FIXME 31816 var interval = model.get('interval'); 31817 if (interval != null) { 31818 scale.setInterval && scale.setInterval(interval); 31819 } 31820 } 31821 31822 /** 31823 * @param {module:echarts/model/Model} model 31824 * @param {string} [axisType] Default retrieve from model.type 31825 * @return {module:echarts/scale/*} 31826 */ 31827 function createScaleByModel(model, axisType) { 31828 axisType = axisType || model.get('type'); 31829 if (axisType) { 31830 switch (axisType) { 31831 // Buildin scale 31832 case 'category': 31833 return new OrdinalScale( 31834 model.getOrdinalMeta 31835 ? model.getOrdinalMeta() 31836 : model.getCategories(), 31837 [Infinity, -Infinity] 31838 ); 31839 case 'value': 31840 return new IntervalScale(); 31841 // Extended scale, like time and log 31842 default: 31843 return (Scale.getClass(axisType) || IntervalScale).create(model); 31844 } 31845 } 31846 } 31847 31848 /** 31849 * Check if the axis corss 0 31850 */ 31851 function ifAxisCrossZero(axis) { 31852 var dataExtent = axis.scale.getExtent(); 31853 var min = dataExtent[0]; 31854 var max = dataExtent[1];
31855 return !((min > 0 && max > 0) || (min < 0 && max < 0)); 31856 } 31857 31858 /** 31859 * @param {module:echarts/coord/Axis} axis 31860 * @return {Function} Label formatter function. 31861 * param: {number} tickValue, 31862 * param: {number} idx, the index in all ticks. 31863 * If category axis, this param is not requied. 31864 * return: {string} label string. 31865 */ 31866 function makeLabelFormatter(axis) { 31867 var labelFormatter = axis.getLabelModel().get('formatter'); 31868 var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null; 31869 31870 if (typeof labelFormatter === 'string') { 31871 labelFormatter = (function (tpl) { 31872 return function (val) { 31873 return tpl.replace('{value}', val != null ? val : ''); 31874 }; 31875 })(labelFormatter); 31876 // Consider empty array 31877 return labelFormatter; 31878 } 31879 else if (typeof labelFormatter === 'function') { 31880 return function (tickValue, idx) { 31881 // The original intention of `idx` is "the index of the tick in all ticks". 31882 // But the previous implementation of category axis do not consider the 31883 // `axisLabel.interval`, which cause that, for example, the `interval` is 31884 // `1`, then the ticks "name5", "name7", "name9" are displayed, where the 31885 // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep 31886 // the definition here for back compatibility. 31887 if (categoryTickStart != null) { 31888 idx = tickValue - categoryTickStart; 31889 } 31890 return labelFormatter(getAxisRawValue(axis, tickValue), idx); 31891 }; 31892 } 31893 else { 31894 return function (tick) { 31895 return axis.scale.getLabel(tick); 31896 }; 31897 } 31898 } 31899 31900 function getAxisRawValue(axis, value) { 31901 // In category axis with data zoom, tick is not the original 31902 // index of axis.data. So tick should not be exposed to user 31903 // in category axis. 31904 return axis.type === 'category' ? axis.scale.getLabel(value) : value; 31905 } 31906 31907 /** 31908 * @param {module:echarts/coord/Axis} axis 31909 * @return {module:zrender/core/BoundingRect} Be null/undefined if no labels. 31910 */ 31911 function estimateLabelUnionRect(axis) { 31912 var axisModel = axis.model; 31913 var scale = axis.scale; 31914 31915 if (!axisModel.get('axisLabel.show') || scale.isBlank()) { 31916 return; 31917 } 31918 31919 var isCategory = axis.type === 'category'; 31920 31921 var realNumberScaleTicks; 31922 var tickCount; 31923 var categoryScaleExtent = scale.getExtent(); 31924 31925 // Optimize for large category data, avoid call `getTicks()`. 31926 if (isCategory) { 31927 tickCount = scale.count(); 31928 } 31929 else { 31930 realNumberScaleTicks = scale.getTicks(); 31931 tickCount = realNumberScaleTicks.length; 31932 } 31933 31934 var axisLabelModel = axis.getLabelModel(); 31935 var labelFormatter = makeLabelFormatter(axis); 31936 31937 var rect; 31938 var step = 1; 31939 // Simple optimization for large amount of labels 31940 if (tickCount > 40) { 31941 step = Math.ceil(tickCount / 40); 31942 } 31943 for (var i = 0; i < tickCount; i += step) { 31944 var tickValue = realNumberScaleTicks ? realNumberScaleTicks[i] : categoryScaleExtent[0] + i; 31945 var label = labelFormatter(tickValue); 31946 var unrotatedSingleRect = axisLabelModel.getTextRect(label); 31947 var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0); 31948 31949 rect ? rect.union(singleRect) : (rect = singleRect); 31950 } 31951 31952 return rect; 31953 } 31954 31955 function rotateTextRect(textRect, rotate) { 31956 var rotateRadians = rotate * Math.PI / 180; 31957 var boundingBox = textRect.plain(); 31958 var beforeWidth = boundingBox.width; 31959 var beforeHeight = boundingBox.height; 31960 var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
31961 var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians); 31962 var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight); 31963 31964 return rotatedRect; 31965 } 31966 31967 /* 31968* Licensed to the Apache Software Foundation (ASF) under one 31969* or more contributor license agreements. See the NOTICE file 31970* distributed with this work for additional information 31971* regarding copyright ownership. The ASF licenses this file 31972* to you under the Apache License, Version 2.0 (the 31973* "License"); you may not use this file except in compliance 31974* with the License. You may obtain a copy of the License at 31975* 31976* http://www.apache.org/licenses/LICENSE-2.0 31977* 31978* Unless required by applicable law or agreed to in writing, 31979* software distributed under the License is distributed on an 31980* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 31981* KIND, either express or implied. See the License for the 31982* specific language governing permissions and limitations 31983* under the License. 31984*/ 31985 31986 var axisModelCommonMixin = { 31987 31988 /** 31989 * @param {boolean} origin 31990 * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN 31991 */ 31992 getMin: function (origin) { 31993 var option = this.option; 31994 var min = (!origin && option.rangeStart != null) 31995 ? option.rangeStart : option.min; 31996 31997 if (this.axis 31998 && min != null 31999 && min !== 'dataMin' 32000 && typeof min !== 'function' 32001 && !eqNaN(min) 32002 ) { 32003 min = this.axis.scale.parse(min); 32004 } 32005 return min; 32006 }, 32007 32008 /** 32009 * @param {boolean} origin 32010 * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN 32011 */ 32012 getMax: function (origin) { 32013 var option = this.option; 32014 var max = (!origin && option.rangeEnd != null) 32015 ? option.rangeEnd : option.max; 32016 32017 if (this.axis 32018 && max != null 32019 && max !== 'dataMax' 32020 && typeof max !== 'function' 32021 && !eqNaN(max) 32022 ) { 32023 max = this.axis.scale.parse(max); 32024 } 32025 return max; 32026 }, 32027 32028 /** 32029 * @return {boolean} 32030 */ 32031 getNeedCrossZero: function () { 32032 var option = this.option; 32033 return (option.rangeStart != null || option.rangeEnd != null) 32034 ? false : !option.scale; 32035 }, 32036 32037 /** 32038 * Should be implemented by each axis model if necessary. 32039 * @return {module:echarts/model/Component} coordinate system model 32040 */ 32041 getCoordSysModel: noop, 32042 32043 /** 32044 * @param {number} rangeStart Can only be finite number or null/undefined or NaN. 32045 * @param {number} rangeEnd Can only be finite number or null/undefined or NaN. 32046 */ 32047 setRange: function (rangeStart, rangeEnd) { 32048 this.option.rangeStart = rangeStart; 32049 this.option.rangeEnd = rangeEnd; 32050 }, 32051 32052 /** 32053 * Reset range 32054 */ 32055 resetRange: function () { 32056 // rangeStart and rangeEnd is readonly. 32057 this.option.rangeStart = this.option.rangeEnd = null; 32058 } 32059 }; 32060 32061 /* 32062* Licensed to the Apache Software Foundation (ASF) under one 32063* or more contributor license agreements. See the NOTICE file 32064* distributed with this work for additional information 32065* regarding copyright ownership. The ASF licenses this file 32066* to you under the Apache License, Version 2.0 (the 32067* "License"); you may not use this file except in compliance 32068* with the License. You may obtain a copy of the License at 32069* 32070* http://www.apache.org/licenses/LICENSE-2.0 32071* 32072* Unless required by applicable law or agreed to in writing, 32073* software distributed under the License is distributed on an 32074* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 32075* KIND, either express or implied. See the License for the 32076* specific language governing permissions and limitations 32077* under the License. 32078*/ 32079 32080// Symbol factory 32081 32082 /** 32083 * Triangle shape 32084 * @inner 32085 */ 32086 var Triangle = extendShape({ 32087 type: 'triangle', 32088 shape: { 32089 cx: 0, 32090 cy: 0, 32091 width: 0, 32092 height: 0 32093 }, 32094 buildPath: function (path, shape) { 32095 var cx = shape.cx; 32096 var cy = shape.cy; 32097 var width = shape.width / 2; 32098 var height = shape.height / 2; 32099 path.moveTo(cx, cy - height); 32100 path.lineTo(cx + width, cy + height); 32101 path.lineTo(cx - width, cy + height);
vendor: 5,327 bytes, lines 32102-32314
32102 path.closePath(); 32103 } 32104 }); 32105 32106 /** 32107 * Diamond shape 32108 * @inner 32109 */ 32110 var Diamond = extendShape({ 32111 type: 'diamond', 32112 shape: { 32113 cx: 0, 32114 cy: 0, 32115 width: 0, 32116 height: 0 32117 }, 32118 buildPath: function (path, shape) { 32119 var cx = shape.cx; 32120 var cy = shape.cy; 32121 var width = shape.width / 2; 32122 var height = shape.height / 2; 32123 path.moveTo(cx, cy - height); 32124 path.lineTo(cx + width, cy); 32125 path.lineTo(cx, cy + height); 32126 path.lineTo(cx - width, cy); 32127 path.closePath(); 32128 } 32129 }); 32130 32131 /** 32132 * Pin shape 32133 * @inner 32134 */ 32135 var Pin = extendShape({ 32136 type: 'pin', 32137 shape: { 32138 // x, y on the cusp 32139 x: 0, 32140 y: 0, 32141 width: 0, 32142 height: 0 32143 }, 32144 32145 buildPath: function (path, shape) { 32146 var x = shape.x; 32147 var y = shape.y; 32148 var w = shape.width / 5 * 3; 32149 // Height must be larger than width 32150 var h = Math.max(w, shape.height); 32151 var r = w / 2; 32152 32153 // Dist on y with tangent point and circle center 32154 var dy = r * r / (h - r); 32155 var cy = y - h + r + dy; 32156 var angle = Math.asin(dy / r); 32157 // Dist on x with tangent point and circle center 32158 var dx = Math.cos(angle) * r; 32159 32160 var tanX = Math.sin(angle); 32161 var tanY = Math.cos(angle); 32162 32163 var cpLen = r * 0.6; 32164 var cpLen2 = r * 0.7; 32165 32166 path.moveTo(x - dx, cy + dy); 32167 32168 path.arc( 32169 x, cy, r, 32170 Math.PI - angle, 32171 Math.PI * 2 + angle 32172 ); 32173 path.bezierCurveTo( 32174 x + dx - tanX * cpLen, cy + dy + tanY * cpLen, 32175 x, y - cpLen2, 32176 x, y 32177 ); 32178 path.bezierCurveTo( 32179 x, y - cpLen2, 32180 x - dx + tanX * cpLen, cy + dy + tanY * cpLen, 32181 x - dx, cy + dy 32182 ); 32183 path.closePath(); 32184 } 32185 }); 32186 32187 /** 32188 * Arrow shape 32189 * @inner 32190 */ 32191 var Arrow = extendShape({ 32192 32193 type: 'arrow', 32194 32195 shape: { 32196 x: 0, 32197 y: 0, 32198 width: 0, 32199 height: 0 32200 }, 32201 32202 buildPath: function (ctx, shape) { 32203 var height = shape.height; 32204 var width = shape.width; 32205 var x = shape.x; 32206 var y = shape.y; 32207 var dx = width / 3 * 2; 32208 ctx.moveTo(x, y); 32209 ctx.lineTo(x + dx, y + height); 32210 ctx.lineTo(x, y + height / 4 * 3); 32211 ctx.lineTo(x - dx, y + height); 32212 ctx.lineTo(x, y); 32213 ctx.closePath(); 32214 } 32215 }); 32216 32217 /** 32218 * Map of path contructors 32219 * @type {Object.<string, module:zrender/graphic/Path>} 32220 */ 32221 var symbolCtors = { 32222 32223 line: Line, 32224 32225 rect: Rect, 32226 32227 roundRect: Rect, 32228 32229 square: Rect, 32230 32231 circle: Circle, 32232 32233 diamond: Diamond, 32234 32235 pin: Pin, 32236 32237 arrow: Arrow, 32238 32239 triangle: Triangle 32240 }; 32241 32242 var symbolShapeMakers = { 32243 32244 line: function (x, y, w, h, shape) { 32245 // FIXME 32246 shape.x1 = x; 32247 shape.y1 = y + h / 2; 32248 shape.x2 = x + w; 32249 shape.y2 = y + h / 2; 32250 }, 32251 32252 rect: function (x, y, w, h, shape) { 32253 shape.x = x; 32254 shape.y = y; 32255 shape.width = w; 32256 shape.height = h; 32257 }, 32258 32259 roundRect: function (x, y, w, h, shape) { 32260 shape.x = x; 32261 shape.y = y; 32262 shape.width = w; 32263 shape.height = h; 32264 shape.r = Math.min(w, h) / 4; 32265 }, 32266 32267 square: function (x, y, w, h, shape) { 32268 var size = Math.min(w, h); 32269 shape.x = x; 32270 shape.y = y; 32271 shape.width = size; 32272 shape.height = size; 32273 }, 32274 32275 circle: function (x, y, w, h, shape) { 32276 // Put circle in the center of square 32277 shape.cx = x + w / 2; 32278 shape.cy = y + h / 2; 32279 shape.r = Math.min(w, h) / 2; 32280 }, 32281 32282 diamond: function (x, y, w, h, shape) { 32283 shape.cx = x + w / 2; 32284 shape.cy = y + h / 2; 32285 shape.width = w; 32286 shape.height = h; 32287 }, 32288 32289 pin: function (x, y, w, h, shape) { 32290 shape.x = x + w / 2; 32291 shape.y = y + h / 2; 32292 shape.width = w; 32293 shape.height = h; 32294 }, 32295 32296 arrow: function (x, y, w, h, shape) { 32297 shape.x = x + w / 2; 32298 shape.y = y + h / 2; 32299 shape.width = w; 32300 shape.height = h; 32301 }, 32302 32303 triangle: function (x, y, w, h, shape) { 32304 shape.cx = x + w / 2; 32305 shape.cy = y + h / 2; 32306 shape.width = w; 32307 shape.height = h; 32308 } 32309 }; 32310 32311 var symbolBuildProxies = {}; 32312 each$1(symbolCtors, function (Ctor, name) { 32313 symbolBuildProxies[name] = new Ctor(); 32314 });
32315 32316 var SymbolClz = extendShape({ 32317 32318 type: 'symbol', 32319 32320 shape: { 32321 symbolType: '', 32322 x: 0, 32323 y: 0, 32324 width: 0, 32325 height: 0 32326 }, 32327 32328 beforeBrush: function () { 32329 var style = this.style; 32330 var shape = this.shape; 32331 // FIXME 32332 if (shape.symbolType === 'pin' && style.textPosition === 'inside') { 32333 style.textPosition = ['50%', '40%']; 32334 style.textAlign = 'center'; 32335 style.textVerticalAlign = 'middle'; 32336 } 32337 }, 32338 32339 buildPath: function (ctx, shape, inBundle) { 32340 var symbolType = shape.symbolType; 32341 var proxySymbol = symbolBuildProxies[symbolType]; 32342 if (shape.symbolType !== 'none') { 32343 if (!proxySymbol) { 32344 // Default rect 32345 symbolType = 'rect'; 32346 proxySymbol = symbolBuildProxies[symbolType]; 32347 } 32348 symbolShapeMakers[symbolType]( 32349 shape.x, shape.y, shape.width, shape.height, proxySymbol.shape 32350 ); 32351 proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle); 32352 } 32353 } 32354 }); 32355 32356// Provide setColor helper method to avoid determine if set the fill or stroke outside 32357 function symbolPathSetColor(color, innerColor) { 32358 if (this.type !== 'image') { 32359 var symbolStyle = this.style; 32360 var symbolShape = this.shape; 32361 if (symbolShape && symbolShape.symbolType === 'line') { 32362 symbolStyle.stroke = color; 32363 } 32364 else if (this.__isEmptyBrush) { 32365 symbolStyle.stroke = color; 32366 symbolStyle.fill = innerColor || '#fff'; 32367 } 32368 else { 32369 // FIXME åˤæâÂÃ¥âºÂ¾Ã¥Â½Â¢Ã©Â»Ë认æË¯å¡«åâ¦â¦Ã¨Â¿ËæË¯æÂÂè¾¹ï¼Å使ç⨠onlyStroke ? 32370 symbolStyle.fill && (symbolStyle.fill = color); 32371 symbolStyle.stroke && (symbolStyle.stroke = color); 32372 } 32373 this.dirty(false); 32374 } 32375 } 32376 32377 /** 32378 * Create a symbol element with given symbol configuration: shape, x, y, width, height, color 32379 * @param {string} symbolType 32380 * @param {number} x 32381 * @param {number} y 32382 * @param {number} w 32383 * @param {number} h 32384 * @param {string} color 32385 * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h, 32386 * for path and image only. 32387 */ 32388 function createSymbol(symbolType, x, y, w, h, color, keepAspect) { 32389 // TODO Support image object, DynamicImage. 32390 32391 var isEmpty = symbolType.indexOf('empty') === 0; 32392 if (isEmpty) { 32393 symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6); 32394 } 32395 var symbolPath; 32396 32397 if (symbolType.indexOf('image://') === 0) { 32398 symbolPath = makeImage( 32399 symbolType.slice(8), 32400 new BoundingRect(x, y, w, h), 32401 keepAspect ? 'center' : 'cover' 32402 ); 32403 } 32404 else if (symbolType.indexOf('path://') === 0) { 32405 symbolPath = makePath( 32406 symbolType.slice(7), 32407 {}, 32408 new BoundingRect(x, y, w, h), 32409 keepAspect ? 'center' : 'cover' 32410 ); 32411 } 32412 else { 32413 symbolPath = new SymbolClz({ 32414 shape: { 32415 symbolType: symbolType, 32416 x: x, 32417 y: y, 32418 width: w, 32419 height: h 32420 } 32421 }); 32422 } 32423 32424 symbolPath.__isEmptyBrush = isEmpty; 32425 32426 symbolPath.setColor = symbolPathSetColor; 32427 32428 symbolPath.setColor(color); 32429 32430 return symbolPath; 32431 } 32432 32433 /* 32434* Licensed to the Apache Software Foundation (ASF) under one 32435* or more contributor license agreements. See the NOTICE file 32436* distributed with this work for additional information 32437* regarding copyright ownership. The ASF licenses this file 32438* to you under the Apache License, Version 2.0 (the 32439* "License");
32439 you may not use this file except in compliance 32440* with the License. You may obtain a copy of the License at 32441* 32442* http://www.apache.org/licenses/LICENSE-2.0 32443* 32444* Unless required by applicable law or agreed to in writing, 32445* software distributed under the License is distributed on an 32446* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 32447* KIND, either express or implied. See the License for the 32448* specific language governing permissions and limitations 32449* under the License. 32450*/ 32451 32452// import createGraphFromNodeEdge from './chart/helper/createGraphFromNodeEdge'; 32453 /** 32454 * Create a muti dimension List structure from seriesModel. 32455 * @param {module:echarts/model/Model} seriesModel 32456 * @return {module:echarts/data/List} list 32457 */ 32458 function createList(seriesModel) { 32459 return createListFromArray(seriesModel.getSource(), seriesModel); 32460 } 32461 32462 var dataStack$1 = { 32463 isDimensionStacked: isDimensionStacked, 32464 enableDataStack: enableDataStack, 32465 getStackedDimension: getStackedDimension 32466 }; 32467 32468 /** 32469 * Create scale 32470 * @param {Array.<number>} dataExtent 32471 * @param {Object|module:echarts/Model} option 32472 */ 32473 function createScale(dataExtent, option) { 32474 var axisModel = option; 32475 if (!Model.isInstance(option)) { 32476 axisModel = new Model(option); 32477 mixin(axisModel, axisModelCommonMixin); 32478 } 32479 32480 var scale = createScaleByModel(axisModel); 32481 scale.setExtent(dataExtent[0], dataExtent[1]); 32482 32483 niceScaleExtent(scale, axisModel); 32484 return scale; 32485 } 32486 32487 /** 32488 * Mixin common methods to axis model, 32489 * 32490 * Inlcude methods 32491 * `getFormattedLabels() => Array.<string>` 32492 * `getCategories() => Array.<string>` 32493 * `getMin(origin: boolean) => number` 32494 * `getMax(origin: boolean) => number` 32495 * `getNeedCrossZero() => boolean` 32496 * `setRange(start: number, end: number)` 32497 * `resetRange()` 32498 */ 32499 function mixinAxisModelCommonMethods(Model$$1) { 32500 mixin(Model$$1, axisModelCommonMixin); 32501 } 32502 32503 var helper = (Object.freeze || Object)({ 32504 createList: createList, 32505 getLayoutRect: getLayoutRect, 32506 dataStack: dataStack$1, 32507 createScale: createScale, 32508 mixinAxisModelCommonMethods: mixinAxisModelCommonMethods, 32509 completeDimensions: completeDimensions, 32510 createDimensions: createDimensions, 32511 createSymbol: createSymbol 32512 }); 32513 32514 var EPSILON$3 = 1e-8; 32515 32516 function isAroundEqual$1(a, b) { 32517 return Math.abs(a - b) < EPSILON$3; 32518 } 32519 32520 function contain$1(points, x, y) { 32521 var w = 0; 32522 var p = points[0]; 32523 32524 if (!p) { 32525 return false; 32526 } 32527 32528 for (var i = 1; i < points.length; i++) { 32529 var p2 = points[i]; 32530 w += windingLine(p[0], p[1], p2[0], p2[1], x, y); 32531 p = p2; 32532 } 32533 32534 // Close polygon 32535 var p0 = points[0]; 32536 if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) { 32537 w += windingLine(p[0], p[1], p0[0], p0[1], x, y); 32538 } 32539 32540 return w !== 0; 32541 } 32542 32543 /* 32544* Licensed to the Apache Software Foundation (ASF) under one 32545* or more contributor license agreements. See the NOTICE file 32546* distributed with this work for additional information 32547* regarding copyright ownership. The ASF licenses this file 32548* to you under the Apache License, Version 2.0 (the 32549* "License"); you may not use this file except in compliance 32550* with the License. You may obtain a copy of the License at 32551* 32552* http://www.apache.org/licenses/LICENSE-2.0 32553* 32554* Unless required by applicable law or agreed to in writing, 32555* software distributed under the License is distributed on an 32556* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 32557* KIND, either express or implied. See the License for the 32558* specific language governing permissions and limitations 32559* under the License. 32560*/ 32561 32562 /** 32563 * @module echarts/coord/geo/Region 32564 */ 32565 32566 /** 32567 * @param {string} name 32568 * @param {Array} geometries 32569 * @param {Array.<number>} cp 32570 */ 32571 function Region(name, geometries, cp) { 32572 32573 /** 32574 * @type {string} 32575 * @readOnly 32576 */ 32577 this.name = name; 32578 32579 /** 32580 * @type {Array.<Array>} 32581 * @readOnly 32582 */ 32583 this.geometries = geometries; 32584 32585 if (!cp) {
32586 var rect = this.getBoundingRect(); 32587 cp = [ 32588 rect.x + rect.width / 2, 32589 rect.y + rect.height / 2 32590 ]; 32591 } 32592 else { 32593 cp = [cp[0], cp[1]]; 32594 } 32595 /** 32596 * @type {Array.<number>} 32597 */ 32598 this.center = cp; 32599 } 32600 32601 Region.prototype = { 32602 32603 constructor: Region, 32604 32605 properties: null, 32606 32607 /** 32608 * @return {module:zrender/core/BoundingRect} 32609 */ 32610 getBoundingRect: function () { 32611 var rect = this._rect; 32612 if (rect) { 32613 return rect; 32614 } 32615 32616 var MAX_NUMBER = Number.MAX_VALUE; 32617 var min$$1 = [MAX_NUMBER, MAX_NUMBER]; 32618 var max$$1 = [-MAX_NUMBER, -MAX_NUMBER]; 32619 var min2 = []; 32620 var max2 = []; 32621 var geometries = this.geometries; 32622 for (var i = 0; i < geometries.length; i++) { 32623 // Only support polygon 32624 if (geometries[i].type !== 'polygon') { 32625 continue; 32626 } 32627 // Doesn't consider hole 32628 var exterior = geometries[i].exterior; 32629 fromPoints(exterior, min2, max2); 32630 min(min$$1, min$$1, min2); 32631 max(max$$1, max$$1, max2); 32632 } 32633 // No data 32634 if (i === 0) { 32635 min$$1[0] = min$$1[1] = max$$1[0] = max$$1[1] = 0; 32636 } 32637 32638 return (this._rect = new BoundingRect( 32639 min$$1[0], min$$1[1], max$$1[0] - min$$1[0], max$$1[1] - min$$1[1] 32640 )); 32641 }, 32642 32643 /** 32644 * @param {<Array.<number>} coord 32645 * @return {boolean} 32646 */ 32647 contain: function (coord) { 32648 var rect = this.getBoundingRect(); 32649 var geometries = this.geometries; 32650 if (!rect.contain(coord[0], coord[1])) { 32651 return false; 32652 } 32653 loopGeo: for (var i = 0, len$$1 = geometries.length; i < len$$1; i++) { 32654 // Only support polygon. 32655 if (geometries[i].type !== 'polygon') { 32656 continue; 32657 } 32658 var exterior = geometries[i].exterior; 32659 var interiors = geometries[i].interiors; 32660 if (contain$1(exterior, coord[0], coord[1])) { 32661 // Not in the region if point is in the hole. 32662 for (var k = 0; k < (interiors ? interiors.length : 0); k++) { 32663 if (contain$1(interiors[k])) { 32664 continue loopGeo; 32665 } 32666 } 32667 return true; 32668 } 32669 } 32670 return false; 32671 }, 32672 32673 transformTo: function (x, y, width, height) { 32674 var rect = this.getBoundingRect(); 32675 var aspect = rect.width / rect.height; 32676 if (!width) { 32677 width = aspect * height; 32678 } 32679 else if (!height) { 32680 height = width / aspect ; 32681 } 32682 var target = new BoundingRect(x, y, width, height); 32683 var transform = rect.calculateTransform(target); 32684 var geometries = this.geometries; 32685 for (var i = 0; i < geometries.length; i++) { 32686 // Only support polygon. 32687 if (geometries[i].type !== 'polygon') { 32688 continue; 32689 } 32690 var exterior = geometries[i].exterior; 32691 var interiors = geometries[i].interiors; 32692 for (var p = 0; p < exterior.length; p++) { 32693 applyTransform(exterior[p], exterior[p], transform); 32694 } 32695 for (var h = 0; h < (interiors ? interiors.length : 0); h++) { 32696 for (var p = 0; p < interiors[h].length; p++) { 32697 applyTransform(interiors[h][p], interiors[h][p], transform); 32698 } 32699 } 32700 } 32701 rect = this._rect; 32702 rect.copy(target); 32703 // Update center 32704 this.center = [ 32705 rect.x + rect.width / 2, 32706 rect.y + rect.height / 2 32707 ]; 32708 } 32709 }; 32710 32711 /* 32712* Licensed to the Apache Software Foundation (ASF) under one 32713* or more contributor license agreements. See the NOTICE file 32714* distributed with this work for additional information 32715* regarding copyright ownership. The ASF licenses this file 32716* to you under the Apache License, Version 2.0 (the 32717* "License");
32717 you may not use this file except in compliance 32718* with the License. You may obtain a copy of the License at 32719* 32720* http://www.apache.org/licenses/LICENSE-2.0 32721* 32722* Unless required by applicable law or agreed to in writing, 32723* software distributed under the License is distributed on an 32724* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 32725* KIND, either express or implied. See the License for the 32726* specific language governing permissions and limitations 32727* under the License. 32728*/ 32729 32730 /** 32731 * Parse and decode geo json 32732 * @module echarts/coord/geo/parseGeoJson 32733 */ 32734 32735 function decode(json) { 32736 if (!json.UTF8Encoding) { 32737 return json; 32738 } 32739 var encodeScale = json.UTF8Scale; 32740 if (encodeScale == null) { 32741 encodeScale = 1024; 32742 } 32743 32744 var features = json.features; 32745 32746 for (var f = 0; f < features.length; f++) { 32747 var feature = features[f]; 32748 var geometry = feature.geometry; 32749 var coordinates = geometry.coordinates; 32750 var encodeOffsets = geometry.encodeOffsets; 32751 32752 for (var c = 0; c < coordinates.length; c++) { 32753 var coordinate = coordinates[c]; 32754 32755 if (geometry.type === 'Polygon') { 32756 coordinates[c] = decodePolygon( 32757 coordinate, 32758 encodeOffsets[c], 32759 encodeScale 32760 ); 32761 } 32762 else if (geometry.type === 'MultiPolygon') { 32763 for (var c2 = 0; c2 < coordinate.length; c2++) { 32764 var polygon = coordinate[c2]; 32765 coordinate[c2] = decodePolygon( 32766 polygon, 32767 encodeOffsets[c][c2], 32768 encodeScale 32769 ); 32770 } 32771 } 32772 } 32773 } 32774 // Has been decoded 32775 json.UTF8Encoding = false; 32776 return json; 32777 } 32778 32779 function decodePolygon(coordinate, encodeOffsets, encodeScale) { 32780 var result = []; 32781 var prevX = encodeOffsets[0]; 32782 var prevY = encodeOffsets[1]; 32783 32784 for (var i = 0; i < coordinate.length; i += 2) { 32785 var x = coordinate.charCodeAt(i) - 64; 32786 var y = coordinate.charCodeAt(i + 1) - 64; 32787 // ZigZag decoding 32788 x = (x >> 1) ^ (-(x & 1)); 32789 y = (y >> 1) ^ (-(y & 1)); 32790 // Delta deocding 32791 x += prevX; 32792 y += prevY; 32793 32794 prevX = x; 32795 prevY = y; 32796 // Dequantize 32797 result.push([x / encodeScale, y / encodeScale]); 32798 } 32799 32800 return result; 32801 } 32802 32803 /** 32804 * @alias module:echarts/coord/geo/parseGeoJson 32805 * @param {Object} geoJson 32806 * @return {module:zrender/container/Group} 32807 */ 32808 var parseGeoJson$1 = function (geoJson) { 32809 32810 decode(geoJson); 32811 32812 return map(filter(geoJson.features, function (featureObj) { 32813 // Output of mapshaper may have geometry null 32814 return featureObj.geometry 32815 && featureObj.properties 32816 && featureObj.geometry.coordinates.length > 0; 32817 }), function (featureObj) { 32818 var properties = featureObj.properties; 32819 var geo = featureObj.geometry; 32820 32821 var coordinates = geo.coordinates; 32822 32823 var geometries = []; 32824 if (geo.type === 'Polygon') { 32825 geometries.push({ 32826 type: 'polygon', 32827 // According to the GeoJSON specification. 32828 // First must be exterior, and the rest are all interior(holes). 32829 exterior: coordinates[0], 32830 interiors: coordinates.slice(1) 32831 }); 32832 } 32833 if (geo.type === 'MultiPolygon') { 32834 each$1(coordinates, function (item) { 32835 if (item[0]) { 32836 geometries.push({ 32837 type: 'polygon', 32838 exterior: item[0], 32839 interiors: item.slice(1) 32840 }); 32841 } 32842 }); 32843 } 32844 32845 var region = new Region( 32846 properties.name, 32847 geometries, 32848 properties.cp 32849 ); 32850 region.properties = properties; 32851 return region; 32852 }); 32853 }; 32854 32855 /* 32856* Licensed to the Apache Software Foundation (ASF) under one 32857* or more contributor license agreements. See the NOTICE file 32858* distributed with this work for additional information 32859* regarding copyright ownership. The ASF licenses this file 32860* to you under the Apache License, Version 2.0 (the 32861* "License");
32861 you may not use this file except in compliance 32862* with the License. You may obtain a copy of the License at 32863* 32864* http://www.apache.org/licenses/LICENSE-2.0 32865* 32866* Unless required by applicable law or agreed to in writing, 32867* software distributed under the License is distributed on an 32868* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 32869* KIND, either express or implied. See the License for the 32870* specific language governing permissions and limitations 32871* under the License. 32872*/ 32873 32874 var inner$6 = makeInner(); 32875 32876 /** 32877 * @param {module:echats/coord/Axis} axis 32878 * @return {Object} { 32879 * labels: [{ 32880 * formattedLabel: string, 32881 * rawLabel: string, 32882 * tickValue: number 32883 * }, ...], 32884 * labelCategoryInterval: number 32885 * } 32886 */ 32887 function createAxisLabels(axis) { 32888 // Only ordinal scale support tick interval 32889 return axis.type === 'category' 32890 ? makeCategoryLabels(axis) 32891 : makeRealNumberLabels(axis); 32892 } 32893 32894 /** 32895 * @param {module:echats/coord/Axis} axis 32896 * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea. 32897 * @return {Object} { 32898 * ticks: Array.<number> 32899 * tickCategoryInterval: number 32900 * } 32901 */ 32902 function createAxisTicks(axis, tickModel) { 32903 // Only ordinal scale support tick interval 32904 return axis.type === 'category' 32905 ? makeCategoryTicks(axis, tickModel) 32906 : {ticks: axis.scale.getTicks()}; 32907 } 32908 32909 function makeCategoryLabels(axis) { 32910 var labelModel = axis.getLabelModel(); 32911 var result = makeCategoryLabelsActually(axis, labelModel); 32912 32913 return (!labelModel.get('show') || axis.scale.isBlank()) 32914 ? {labels: [], labelCategoryInterval: result.labelCategoryInterval} 32915 : result; 32916 } 32917 32918 function makeCategoryLabelsActually(axis, labelModel) { 32919 var labelsCache = getListCache(axis, 'labels'); 32920 var optionLabelInterval = getOptionCategoryInterval(labelModel); 32921 var result = listCacheGet(labelsCache, optionLabelInterval); 32922 32923 if (result) { 32924 return result; 32925 } 32926 32927 var labels; 32928 var numericLabelInterval; 32929 32930 if (isFunction$1(optionLabelInterval)) { 32931 labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval); 32932 } 32933 else { 32934 numericLabelInterval = optionLabelInterval === 'auto' 32935 ? makeAutoCategoryInterval(axis) : optionLabelInterval; 32936 labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval); 32937 } 32938 32939 // Cache to avoid calling interval function repeatly. 32940 return listCacheSet(labelsCache, optionLabelInterval, { 32941 labels: labels, labelCategoryInterval: numericLabelInterval 32942 }); 32943 } 32944 32945 function makeCategoryTicks(axis, tickModel) { 32946 var ticksCache = getListCache(axis, 'ticks'); 32947 var optionTickInterval = getOptionCategoryInterval(tickModel); 32948 var result = listCacheGet(ticksCache, optionTickInterval); 32949 32950 if (result) { 32951 return result; 32952 } 32953 32954 var ticks; 32955 var tickCategoryInterval; 32956 32957 // Optimize for the case that large category data and no label displayed, 32958 // we should not return all ticks. 32959 if (!tickModel.get('show') || axis.scale.isBlank()) { 32960 ticks = []; 32961 } 32962 32963 if (isFunction$1(optionTickInterval)) { 32964 ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true); 32965 } 32966 // Always use label interval by default despite label show. Consider this 32967 // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows 32968 // labels. `splitLine` and `axisTick` should be consistent in this case. 32969 else if (optionTickInterval === 'auto') { 32970 var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel()); 32971 tickCategoryInterval = labelsResult.labelCategoryInterval; 32972 ticks = map(labelsResult.labels, function (labelItem) { 32973 return labelItem.tickValue; 32974 }); 32975 } 32976 else { 32977 tickCategoryInterval = optionTickInterval; 32978 ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true); 32979 } 32980 32981 // Cache to avoid calling interval function repeatly. 32982 return listCacheSet(ticksCache, optionTickInterval, { 32983 ticks: ticks, tickCategoryInterval: tickCategoryInterval 32984 }); 32985 } 32986 32987 function makeRealNumberLabels(axis) { 32988 var ticks = axis.scale.getTicks(); 32989 var labelFormatter = makeLabelFormatter(axis); 32990 return { 32991 labels: map(ticks, function (tickValue, idx) { 32992 return { 32993 formattedLabel: labelFormatter(tickValue, idx), 32994 rawLabel: axis.scale.getLabel(tickValue), 32995 tickValue: tickValue 32996 }; 32997 }) 32998 }; 32999 } 33000 33001// Large category data calculation is performence sensitive, and ticks and label 33002// probably be fetched by multiple times. So we cache the result. 33003// axis is created each time during a ec process, so we do not need to clear cache. 33004 function getListCache(axis, prop) { 33005 // Because key can be funciton, and cache size always be small, we use array cache. 33006 return inner$6(axis)[prop] || (inner$6(axis)[prop] = []); 33007 } 33008 33009 function listCacheGet(cache, key) { 33010 for (var i = 0; i < cache.length; i++) { 33011 if (cache[i].key === key) { 33012 return cache[i].value; 33013 } 33014 } 33015 } 33016 33017 function listCacheSet(cache, key, value) { 33018 cache.push({key: key, value: value}); 33019 return value; 33020 } 33021 33022 function makeAutoCategoryInterval(axis) { 33023 var result = inner$6(axis).autoInterval; 33024 return result != null 33025 ? result 33026 : (inner$6(axis).autoInterval = axis.calculateCategoryInterval()); 33027 } 33028 33029 /** 33030 * Calculate interval for category axis ticks and labels. 33031 * To get precise result, at least one of `getRotate` and `isHorizontal` 33032 * should be implemented in axis. 33033 */ 33034 function calculateCategoryInterval(axis) { 33035 var params = fetchAutoCategoryIntervalCalculationParams(axis); 33036 var labelFormatter = makeLabelFormatter(axis); 33037 var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI; 33038 33039 var ordinalScale = axis.scale; 33040 var ordinalExtent = ordinalScale.getExtent(); 33041 // Providing this method is for optimization: 33042 // avoid generating a long array by `getTicks` 33043 // in large category data case. 33044 var tickCount = ordinalScale.count(); 33045 33046 if (ordinalExtent[1] - ordinalExtent[0] < 1) { 33047 return 0; 33048 } 33049 33050 var step = 1; 33051 // Simple optimization. Empirical value: tick count should less than 40. 33052 if (tickCount > 40) { 33053 step = Math.max(1, Math.floor(tickCount / 40)); 33054 } 33055 var tickValue = ordinalExtent[0]; 33056 var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue); 33057 var unitW = Math.abs(unitSpan * Math.cos(rotation)); 33058 var unitH = Math.abs(unitSpan * Math.sin(rotation)); 33059 33060 var maxW = 0; 33061 var maxH = 0; 33062 33063 // Caution: Performance sensitive for large category data. 33064 // Consider dataZoom, we should make appropriate step to avoid O(n) loop. 33065 for (; tickValue <= ordinalExtent[1]; tickValue += step) { 33066 var width = 0; 33067 var height = 0; 33068 33069 // Polar is also calculated in assumptive linear layout here. 33070 // Not precise, do not consider align and vertical align 33071 // and each distance from axis line yet.
vendor: 1,261 bytes, lines 33072-33102
33072 var rect = getBoundingRect( 33073 labelFormatter(tickValue), params.font, 'center', 'top' 33074 ); 33075 // Magic number 33076 width = rect.width * 1.3; 33077 height = rect.height * 1.3; 33078 33079 // Min size, void long loop. 33080 maxW = Math.max(maxW, width, 7); 33081 maxH = Math.max(maxH, height, 7); 33082 } 33083 33084 var dw = maxW / unitW; 33085 var dh = maxH / unitH; 33086 // 0/0 is NaN, 1/0 is Infinity. 33087 isNaN(dw) && (dw = Infinity); 33088 isNaN(dh) && (dh = Infinity); 33089 var interval = Math.max(0, Math.floor(Math.min(dw, dh))); 33090 33091 var cache = inner$6(axis.model); 33092 var lastAutoInterval = cache.lastAutoInterval; 33093 var lastTickCount = cache.lastTickCount; 33094 33095 // Use cache to keep interval stable while moving zoom window, 33096 // otherwise the calculated interval might jitter when the zoom 33097 // window size is close to the interval-changing size. 33098 if (lastAutoInterval != null 33099 && lastTickCount != null 33100 && Math.abs(lastAutoInterval - interval) <= 1 33101 && Math.abs(lastTickCount - tickCount) <= 1 33102 // Always choose the bigger one, otherwise the critical
33103 // point is not the same when zooming in or zooming out. 33104 && lastAutoInterval > interval 33105 ) { 33106 interval = lastAutoInterval; 33107 } 33108 // Only update cache if cache not used, otherwise the 33109 // changing of interval is too insensitive. 33110 else { 33111 cache.lastTickCount = tickCount; 33112 cache.lastAutoInterval = interval; 33113 } 33114 33115 return interval; 33116 } 33117 33118 function fetchAutoCategoryIntervalCalculationParams(axis) { 33119 var labelModel = axis.getLabelModel(); 33120 return { 33121 axisRotate: axis.getRotate 33122 ? axis.getRotate() 33123 : (axis.isHorizontal && !axis.isHorizontal()) 33124 ? 90 33125 : 0, 33126 labelRotate: labelModel.get('rotate') || 0, 33127 font: labelModel.getFont() 33128 }; 33129 } 33130 33131 function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) { 33132 var labelFormatter = makeLabelFormatter(axis); 33133 var ordinalScale = axis.scale; 33134 var ordinalExtent = ordinalScale.getExtent(); 33135 var labelModel = axis.getLabelModel(); 33136 var result = []; 33137 33138 // TODO: axisType: ordinalTime, pick the tick from each month/day/year/... 33139 33140 var step = Math.max((categoryInterval || 0) + 1, 1); 33141 var startTick = ordinalExtent[0]; 33142 var tickCount = ordinalScale.count(); 33143 33144 // Calculate start tick based on zero if possible to keep label consistent 33145 // while zooming and moving while interval > 0. Otherwise the selection 33146 // of displayable ticks and symbols probably keep changing. 33147 // 3 is empirical value. 33148 if (startTick !== 0 && step > 1 && tickCount / step > 2) { 33149 startTick = Math.round(Math.ceil(startTick / step) * step); 33150 } 33151 33152 // (1) Only add min max label here but leave overlap checking 33153 // to render stage, which also ensure the returned list 33154 // suitable for splitLine and splitArea rendering. 33155 // (2) Scales except category always contain min max label so 33156 // do not need to perform this process. 33157 var showMinMax = { 33158 min: labelModel.get('showMinLabel'), 33159 max: labelModel.get('showMaxLabel') 33160 }; 33161 33162 if (showMinMax.min && startTick !== ordinalExtent[0]) { 33163 addItem(ordinalExtent[0]); 33164 } 33165 33166 // Optimize: avoid generating large array by `ordinalScale.getTicks()`. 33167 var tickValue = startTick; 33168 for (; tickValue <= ordinalExtent[1]; tickValue += step) { 33169 addItem(tickValue); 33170 } 33171 33172 if (showMinMax.max && tickValue !== ordinalExtent[1]) { 33173 addItem(ordinalExtent[1]); 33174 } 33175 33176 function addItem(tVal) { 33177 result.push(onlyTick 33178 ? tVal 33179 : { 33180 formattedLabel: labelFormatter(tVal), 33181 rawLabel: ordinalScale.getLabel(tVal), 33182 tickValue: tVal 33183 } 33184 ); 33185 } 33186 33187 return result; 33188 } 33189 33190// When interval is function, the result `false` means ignore the tick. 33191// It is time consuming for large category data. 33192 function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) { 33193 var ordinalScale = axis.scale; 33194 var labelFormatter = makeLabelFormatter(axis); 33195 var result = []; 33196 33197 each$1(ordinalScale.getTicks(), function (tickValue) { 33198 var rawLabel = ordinalScale.getLabel(tickValue); 33199 if (categoryInterval(tickValue, rawLabel)) { 33200 result.push(onlyTick 33201 ? tickValue 33202 : { 33203 formattedLabel: labelFormatter(tickValue), 33204 rawLabel: rawLabel, 33205 tickValue: tickValue 33206 } 33207 ); 33208 } 33209 }); 33210 33211 return result; 33212 } 33213 33214// Can be null|'auto'|number|function 33215 function getOptionCategoryInterval(model) { 33216 var interval = model.get('interval'); 33217 return interval == null ? 'auto' : interval; 33218 } 33219 33220 /* 33221* Licensed to the Apache Software Foundation (ASF) under one 33222* or more contributor license agreements. See the NOTICE file 33223* distributed with this work for additional information 33224* regarding copyright ownership. The ASF licenses this file 33225* to you under the Apache License, Version 2.0 (the 33226* "License");
33226 you may not use this file except in compliance 33227* with the License. You may obtain a copy of the License at 33228* 33229* http://www.apache.org/licenses/LICENSE-2.0 33230* 33231* Unless required by applicable law or agreed to in writing, 33232* software distributed under the License is distributed on an 33233* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 33234* KIND, either express or implied. See the License for the 33235* specific language governing permissions and limitations 33236* under the License. 33237*/ 33238 33239 var NORMALIZED_EXTENT = [0, 1]; 33240 33241 /** 33242 * Base class of Axis. 33243 * @constructor 33244 */ 33245 var Axis = function (dim, scale, extent) { 33246 33247 /** 33248 * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'. 33249 * @type {string} 33250 */ 33251 this.dim = dim; 33252 33253 /** 33254 * Axis scale 33255 * @type {module:echarts/coord/scale/*} 33256 */ 33257 this.scale = scale; 33258 33259 /** 33260 * @type {Array.<number>} 33261 * @private 33262 */ 33263 this._extent = extent || [0, 0]; 33264 33265 /** 33266 * @type {boolean} 33267 */ 33268 this.inverse = false; 33269 33270 /** 33271 * Usually true when axis has a ordinal scale 33272 * @type {boolean} 33273 */ 33274 this.onBand = false; 33275 }; 33276 33277 Axis.prototype = { 33278 33279 constructor: Axis, 33280 33281 /** 33282 * If axis extent contain given coord 33283 * @param {number} coord 33284 * @return {boolean} 33285 */ 33286 contain: function (coord) { 33287 var extent = this._extent; 33288 var min = Math.min(extent[0], extent[1]); 33289 var max = Math.max(extent[0], extent[1]); 33290 return coord >= min && coord <= max; 33291 }, 33292 33293 /** 33294 * If axis extent contain given data 33295 * @param {number} data 33296 * @return {boolean} 33297 */ 33298 containData: function (data) { 33299 return this.contain(this.dataToCoord(data)); 33300 }, 33301 33302 /** 33303 * Get coord extent. 33304 * @return {Array.<number>} 33305 */ 33306 getExtent: function () { 33307 return this._extent.slice(); 33308 }, 33309 33310 /** 33311 * Get precision used for formatting 33312 * @param {Array.<number>} [dataExtent] 33313 * @return {number} 33314 */ 33315 getPixelPrecision: function (dataExtent) { 33316 return getPixelPrecision( 33317 dataExtent || this.scale.getExtent(), 33318 this._extent 33319 ); 33320 }, 33321 33322 /** 33323 * Set coord extent 33324 * @param {number} start 33325 * @param {number} end 33326 */ 33327 setExtent: function (start, end) { 33328 var extent = this._extent; 33329 extent[0] = start; 33330 extent[1] = end; 33331 }, 33332 33333 /** 33334 * Convert data to coord. Data is the rank if it has an ordinal scale 33335 * @param {number} data 33336 * @param {boolean} clamp 33337 * @return {number} 33338 */ 33339 dataToCoord: function (data, clamp) { 33340 var extent = this._extent; 33341 var scale = this.scale; 33342 data = scale.normalize(data); 33343 33344 if (this.onBand && scale.type === 'ordinal') { 33345 extent = extent.slice(); 33346 fixExtentWithBands(extent, scale.count()); 33347 } 33348 33349 return linearMap(data, NORMALIZED_EXTENT, extent, clamp); 33350 }, 33351 33352 /** 33353 * Convert coord to data. Data is the rank if it has an ordinal scale 33354 * @param {number} coord 33355 * @param {boolean} clamp 33356 * @return {number} 33357 */ 33358 coordToData: function (coord, clamp) { 33359 var extent = this._extent; 33360 var scale = this.scale; 33361 33362 if (this.onBand && scale.type === 'ordinal') { 33363 extent = extent.slice(); 33364 fixExtentWithBands(extent, scale.count()); 33365 } 33366 33367 var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp); 33368 33369 return this.scale.scale(t); 33370 }, 33371 33372 /** 33373 * Convert pixel point to data in axis 33374 * @param {Array.<number>} point 33375 * @param {boolean} clamp 33376 * @return {number} data 33377 */ 33378 pointToData: function (point, clamp) { 33379 // Should be implemented in derived class if necessary. 33380 }, 33381 33382 /** 33383 * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`, 33384 * `axis.getTicksCoords` considers `onBand`, which is used by 33385 * `boundaryGap:true` of category axis and splitLine and splitArea. 33386 * @param {Object} [opt] 33387 * @param {number} [opt.tickModel=axis.model.getModel('axisTick')] 33388 * @param {boolean} [opt.clamp] If `true`, the first and the last 33389 * tick must be at the axis end points. Otherwise, clip ticks 33390 * that outside the axis extent. 33391 * @return {Array.<Object>} [{ 33392 * coord: ..., 33393 * tickValue: ... 33394 * }, ...] 33395 */ 33396 getTicksCoords: function (opt) { 33397 opt = opt || {}; 33398 33399 var tickModel = opt.tickModel || this.getTickModel(); 33400 33401 var result = createAxisTicks(this, tickModel); 33402 var ticks = result.ticks; 33403 33404 var ticksCoords = map(ticks, function (tickValue) { 33405 return { 33406 coord: this.dataToCoord(tickValue), 33407 tickValue: tickValue 33408 }; 33409 }, this); 33410 33411 var alignWithLabel = tickModel.get('alignWithLabel'); 33412 fixOnBandTicksCoords( 33413 this, ticksCoords, result.tickCategoryInterval, alignWithLabel, opt.clamp 33414 ); 33415 33416 return ticksCoords; 33417 }, 33418 33419 /** 33420 * @return {Array.<Object>} [{ 33421 * formattedLabel: string, 33422 * rawLabel: axis.scale.getLabel(tickValue) 33423 * tickValue: number 33424 * }, ...] 33425 */ 33426 getViewLabels: function () { 33427 return createAxisLabels(this).labels; 33428 }, 33429 33430 /** 33431 * @return {module:echarts/coord/model/Model} 33432 */ 33433 getLabelModel: function () { 33434 return this.model.getModel('axisLabel'); 33435 }, 33436 33437 /** 33438 * Notice here we only get the default tick model. For splitLine 33439 * or splitArea, we should pass the splitLineModel or splitAreaModel 33440 * manually when calling `getTicksCoords`.
33441 * In GL, this method may be overrided to: 33442 * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));` 33443 * @return {module:echarts/coord/model/Model} 33444 */ 33445 getTickModel: function () { 33446 return this.model.getModel('axisTick'); 33447 }, 33448 33449 /** 33450 * Get width of band 33451 * @return {number} 33452 */ 33453 getBandWidth: function () { 33454 var axisExtent = this._extent; 33455 var dataExtent = this.scale.getExtent(); 33456 33457 var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); 33458 // Fix #2728, avoid NaN when only one data. 33459 len === 0 && (len = 1); 33460 33461 var size = Math.abs(axisExtent[1] - axisExtent[0]); 33462 33463 return Math.abs(size) / len; 33464 }, 33465 33466 /** 33467 * @abstract 33468 * @return {boolean} Is horizontal 33469 */ 33470 isHorizontal: null, 33471 33472 /** 33473 * @abstract 33474 * @return {number} Get axis rotate, b
33474y degree. 33475 */ 33476 getRotate: null, 33477 33478 /** 33479 * Only be called in category axis. 33480 * Can be overrided, consider other axes like in 3D. 33481 * @return {number} Auto interval for cateogry axis tick and label 33482 */ 33483 calculateCategoryInterval: function () { 33484 return calculateCategoryInterval(this); 33485 } 33486 33487 }; 33488 33489 function fixExtentWithBands(extent, nTick) { 33490 var size = extent[1] - extent[0]; 33491 var len = nTick; 33492 var margin = size / len / 2; 33493 extent[0] += margin; 33494 extent[1] -= margin; 33495 } 33496 33497// If axis has labels [1, 2, 3, 4]. Bands on the axis are 33498// |---1---|---2---|---3---|---4---|. 33499// So the displayed ticks and splitLine/splitArea should between 33500// each data item, otherwise cause misleading (e.g., split tow bars 33501// of a single data item when there are two bar series). 33502// Also consider if tickCategoryInterval > 0 and onBand, ticks and 33503// splitLine/spliteArea should layout appropriately corresponding 33504// to displayed labels. (So we should not use `getBandWidth` in this 33505// case). 33506 function fixOnBandTicksCoords(axis, ticksCoords, tickCategoryInterval, alignWithLabel, clamp) { 33507 var ticksLen = ticksCoords.length; 33508 33509 if (!axis.onBand || alignWithLabel || !ticksLen) { 33510 return; 33511 } 33512 33513 var axisExtent = axis.getExtent(); 33514 var last; 33515 if (ticksLen === 1) { 33516 ticksCoords[0].coord = axisExtent[0]; 33517 last = ticksCoords[1] = {coord: axisExtent[0]}; 33518 } 33519 else { 33520 var shift = (ticksCoords[1].coord - ticksCoords[0].coord); 33521 each$1(ticksCoords, function (ticksItem) { 33522 ticksItem.coord -= shift / 2; 33523 var tickCategoryInterval = tickCategoryInterval || 0; 33524 // Avoid split a single data item when odd interval. 33525 if (tickCategoryInterval % 2 > 0) { 33526 ticksItem.coord -= shift / ((tickCategoryInterval + 1) * 2); 33527 } 33528 }); 33529 last = {coord: ticksCoords[ticksLen - 1].coord + shift}; 33530 ticksCoords.push(last); 33531 } 33532 33533 var inverse = axisExtent[0] > axisExtent[1]; 33534 33535 if (littleThan(ticksCoords[0].coord, axisExtent[0])) { 33536 clamp ? (ticksCoords[0].coord = axisExtent[0]) : ticksCoords.shift(); 33537 } 33538 if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) { 33539 ticksCoords.unshift({coord: axisExtent[0]}); 33540 } 33541 if (littleThan(axisExtent[1], last.coord)) { 33542 clamp ? (last.coord = axisExtent[1]) : ticksCoords.pop(); 33543 } 33544 if (clamp && littleThan(last.coord, axisExtent[1])) { 33545 ticksCoords.push({coord: axisExtent[1]}); 33546 } 33547 33548 function littleThan(a, b) { 33549 return inverse ? a > b : a < b; 33550 } 33551 } 33552 33553 /* 33554* Licensed to the Apache Software Foundation (ASF) under one 33555* or more contributor license agreements. See the NOTICE file 33556* distributed with this work for additional information 33557* regarding copyright ownership. The ASF licenses this file 33558* to you under the Apache License, Version 2.0 (the 33559* "License"); you may not use this file except in compliance 33560* with the License. You may obtain a copy of the License at 33561* 33562* http://www.apache.org/licenses/LICENSE-2.0 33563* 33564* Unless required by applicable law or agreed to in writing, 33565* software distributed under the License is distributed on an 33566* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 33567* KIND, either express or implied. See the License for the 33568* specific language governing permissions and limitations 33569* under the License. 33570*/ 33571 33572 /** 33573 * Do not mount those modules on 'src/echarts' for better tree shaking. 33574 */ 33575 33576 var parseGeoJson = parseGeoJson$1; 33577 33578 var ecUtil = {}; 33579 each$1([ 33580 'map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter', 33581 'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction', 33582 'extend', 'defaults', 'clone', 'merge' 33583 ], 33584 function (name) { 33585 ecUtil[name] = zrUtil[name]; 33586 } 33587 ); 33588 33589 /* 33590* Licensed to the Apache Software Foundation (ASF) under one 33591* or more contributor license agreements. See the NOTICE file 33592* distributed with this work for additional information 33593* regarding copyright ownership. The ASF licenses this file 33594* to you under the Apache License, Version 2.0 (the 33595* "License");
33595 you may not use this file except in compliance 33596* with the License. You may obtain a copy of the License at 33597* 33598* http://www.apache.org/licenses/LICENSE-2.0 33599* 33600* Unless required by applicable law or agreed to in writing, 33601* software distributed under the License is distributed on an 33602* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 33603* KIND, either express or implied. See the License for the 33604* specific language governing permissions and limitations 33605* under the License. 33606*/ 33607 33608 SeriesModel.extend({ 33609 33610 type: 'series.line', 33611 33612 dependencies: ['grid', 'polar'], 33613 33614 getInitialData: function (option, ecModel) { 33615 if (__DEV__) { 33616 var coordSys = option.coordinateSystem; 33617 if (coordSys !== 'polar' && coordSys !== 'cartesian2d') { 33618 throw new Error('Line not support coordinateSystem besides cartesian and polar'); 33619 } 33620 } 33621 return createListFromArray(this.getSource(), this); 33622 }, 33623 33624 defaultOption: { 33625 zlevel: 0, 33626 z: 2, 33627 coordinateSystem: 'cartesian2d', 33628 legendHoverLink: true, 33629 33630 hoverAnimation: true, 33631 // stack: null 33632 // xAxisIndex: 0, 33633 // yAxisIndex: 0, 33634 33635 // polarIndex: 0, 33636 33637 // If clip the overflow value 33638 clipOverflow: true, 33639 // cursor: null, 33640 33641 label: { 33642 position: 'top' 33643 }, 33644 // itemStyle: { 33645 // }, 33646 33647 lineStyle: { 33648 width: 2, 33649 type: 'solid' 33650 }, 33651 // areaStyle: { 33652 // origin of areaStyle. Valid values: 33653 // `'auto'/null/undefined`: from axisLine to data 33654 // `'start'`: from min to data 33655 // `'end'`: from data to max 33656 // origin: 'auto' 33657 // }, 33658 // false, 'start', 'end', 'middle' 33659 step: false, 33660 33661 // Disabled if step is true 33662 smooth: false, 33663 smoothMonotone: null, 33664 symbol: 'emptyCircle', 33665 symbolSize: 4, 33666 symbolRotate: null, 33667 33668 showSymbol: true, 33669 // `false`: follow the label interval strategy. 33670 // `true`: show all symbols. 33671 // `'auto'`: If possible, show all symbols, otherwise 33672 // follow the label interval strategy. 33673 showAllSymbol: 'auto', 33674 33675 // Whether to connect break point. 33676 connectNulls: false, 33677 33678 // Sampling for large data. Can be: 'average', 'max', 'min', 'sum'. 33679 sampling: 'none', 33680 33681 animationEasing: 'linear', 33682 33683 // Disable progressive 33684 progressive: 0, 33685 hoverLayerThreshold: Infinity 33686 } 33687 }); 33688 33689 /* 33690* Licensed to the Apache Software Foundation (ASF) under one 33691* or more contributor license agreements. See the NOTICE file 33692* distributed with this work for additional information 33693* regarding copyright ownership. The ASF licenses this file 33694* to you under the Apache License, Version 2.0 (the 33695* "License"); you may not use this file except in compliance 33696* with the License. You may obtain a copy of the License at 33697* 33698* http://www.apache.org/licenses/LICENSE-2.0 33699* 33700* Unless required by applicable law or agreed to in writing, 33701* software distributed under the License is distributed on an 33702* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 33703* KIND, either express or implied. See the License for the 33704* specific language governing permissions and limitations 33705* under the License. 33706*/ 33707 33708 /** 33709 * @param {module:echarts/data/List} data 33710 * @param {number} dataIndex 33711 * @return {string} label string. Not null/undefined 33712 */ 33713 function getDefaultLabel(data, dataIndex) { 33714 var labelDims = data.mapDimension('defaultedLabel', true); 33715 var len = labelDims.length; 33716 33717 // Simple optimization (in lots of cases, label dims length is 1) 33718 if (len === 1) { 33719 return retrieveRawValue(data, dataIndex, labelDims[0]); 33720 } 33721 else if (len) { 33722 var vals = []; 33723 for (var i = 0; i < labelDims.length; i++) { 33724 var val = retrieveRawValue(data, dataIndex, labelDims[i]); 33725 vals.push(val); 33726 } 33727 return vals.join(' '); 33728 } 33729 } 33730 33731 /* 33732* Licensed to the Apache Software Foundation (ASF) under one 33733* or more contributor license agreements. See the NOTICE file 33734* distributed with this work for additional information 33735* regarding copyright ownership. The ASF licenses this file 33736* to you under the Apache License, Version 2.0 (the 33737* "License");
33737 you may not use this file except in compliance 33738* with the License. You may obtain a copy of the License at 33739* 33740* http://www.apache.org/licenses/LICENSE-2.0 33741* 33742* Unless required by applicable law or agreed to in writing, 33743* software distributed under the License is distributed on an 33744* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 33745* KIND, either express or implied. See the License for the 33746* specific language governing permissions and limitations 33747* under the License. 33748*/ 33749 33750 /** 33751 * @module echarts/chart/helper/Symbol 33752 */ 33753 33754 /** 33755 * @constructor 33756 * @alias {module:echarts/chart/helper/Symbol} 33757 * @param {module:echarts/data/List} data 33758 * @param {number} idx 33759 * @extends {module:zrender/graphic/Group} 33760 */ 33761 function SymbolClz$1(data, idx, seriesScope) { 33762 Group.call(this); 33763 this.updateData(data, idx, seriesScope); 33764 } 33765 33766 var symbolProto = SymbolClz$1.prototype; 33767 33768 /** 33769 * @public 33770 * @static 33771 * @param {module:echarts/data/List} data 33772 * @param {number} dataIndex 33773 * @return {Array.<number>} [width, height] 33774 */ 33775 var getSymbolSize = SymbolClz$1.getSymbolSize = function (data, idx) { 33776 var symbolSize = data.getItemVisual(idx, 'symbolSize'); 33777 return symbolSize instanceof Array 33778 ? symbolSize.slice() 33779 : [+symbolSize, +symbolSize]; 33780 }; 33781 33782 function getScale(symbolSize) { 33783 return [symbolSize[0] / 2, symbolSize[1] / 2]; 33784 } 33785 33786 function driftSymbol(dx, dy) { 33787 this.parent.drift(dx, dy); 33788 } 33789 33790 symbolProto._createSymbol = function ( 33791 symbolType, 33792 data, 33793 idx, 33794 symbolSize, 33795 keepAspect 33796 ) { 33797 // Remove paths created before 33798 this.removeAll(); 33799 33800 var color = data.getItemVisual(idx, 'color'); 33801 33802 // var symbolPath = createSymbol( 33803 // symbolType, -0.5, -0.5, 1, 1, color 33804 // ); 33805 // If width/height are set too small (e.g., set to 1) on ios10 33806 // and macOS Sierra, a circle stroke become a rect, no matter what 33807 // the scale is set. So we set width/height as 2. See #4150. 33808 var symbolPath = createSymbol( 33809 symbolType, -1, -1, 2, 2, color, keepAspect 33810 ); 33811 33812 symbolPath.attr({ 33813 z2: 100, 33814 culling: true, 33815 scale: getScale(symbolSize) 33816 }); 33817 // Rewrite drift method 33818 symbolPath.drift = driftSymbol; 33819 33820 this._symbolType = symbolType; 33821 33822 this.add(symbolPath); 33823 }; 33824 33825 /** 33826 * Stop animation 33827 * @param {boolean} toLastFrame 33828 */ 33829 symbolProto.stopSymbolAnimation = function (toLastFrame) { 33830 this.childAt(0).stopAnimation(toLastFrame); 33831 }; 33832 33833 /** 33834 * FIXME: 33835 * Caution: This method breaks the encapsulation of this module, 33836 * but it indeed brings convenience. So do not use the method 33837 * unless you detailedly know all the implements of `Symbol`, 33838 * especially animation. 33839 * 33840 * Get symbol path element. 33841 */ 33842 symbolProto.getSymbolPath = function () { 33843 return this.childAt(0); 33844 }; 33845 33846 /** 33847 * Get scale(aka, current symbol size). 33848 * Including the change caused by animation 33849 */ 33850 symbolProto.getScale = function () { 33851 return this.childAt(0).scale; 33852 }; 33853 33854 /** 33855 * Highlight symbol 33856 */ 33857 symbolProto.highlight = function () { 33858 this.childAt(0).trigger('emphasis'); 33859 }; 33860 33861 /** 33862 * Downplay symbol 33863 */ 33864 symbolProto.downplay = function () { 33865 this.childAt(0).trigger('normal'); 33866 }; 33867 33868 /** 33869 * @param {number} zlevel 33870 * @param {number} z 33871 */ 33872 symbolProto.setZ = function (zlevel, z) { 33873 var symbolPath = this.childAt(0); 33874 symbolPath.zlevel = zlevel; 33875 symbolPath.z = z; 33876 }; 33877 33878 symbolProto.setDraggable = function (draggable) { 33879 var symbolPath = this.childAt(0); 33880 symbolPath.draggable = draggable; 33881 symbolPath.cursor = draggable ? 'move' : 'pointer'; 33882 }; 33883 33884 /** 33885 * Update symbol properties
33886 * @param {module:echarts/data/List} data 33887 * @param {number} idx 33888 * @param {Object} [seriesScope] 33889 * @param {Object} [seriesScope.itemStyle] 33890 * @param {Object} [seriesScope.hoverItemStyle] 33891 * @param {Object} [seriesScope.symbolRotate] 33892 * @param {Object} [seriesScope.symbolOffset] 33893 * @param {module:echarts/model/Model} [seriesScope.labelModel] 33894 * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel] 33895 * @param {boolean} [seriesScope.hoverAnimation] 33896 * @param {Object} [seriesScope.cursorStyle] 33897 * @param {module:echarts/model/Model} [seriesScope.itemModel] 33898 * @param {string} [seriesScope.symbolInnerColor] 33899 * @param {Object} [seriesScope.fadeIn=false] 33900 */ 33901 symbolProto.updateData = function (data, idx, seriesScope) { 33902 this.silent = false; 33903 33904 var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; 33905 var seriesModel = data.hostModel; 33906 var symbolSize = getSymbolSize(data, idx); 33907 var isInit = symbolType !== this._symbolType; 33908 33909 if (isInit) { 33910 var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect'); 33911 this._createSymbol(symbolType, data, idx, symbolSize, keepAspect); 33912 } 33913 else { 33914 var symbolPath = this.childAt(0); 33915 symbolPath.silent = false; 33916 updateProps(symbolPath, { 33917 scale: getScale(symbolSize) 33918 }, seriesModel, idx); 33919 } 33920 33921 this._updateCommon(data, idx, symbolSize, seriesScope); 33922 33923 if (isInit) { 33924 var symbolPath = this.childAt(0); 33925 var fadeIn = seriesScope && seriesScope.fadeIn; 33926 33927 var target = {scale: symbolPath.scale.slice()}; 33928 fadeIn && (target.style = {opacity: symbolPath.style.opacity}); 33929 33930 symbolPath.scale = [0, 0]; 33931 fadeIn && (symbolPath.style.opacity = 0); 33932 33933 initProps(symbolPath, target, seriesModel, idx); 33934 } 33935 33936 this._seriesModel = seriesModel; 33937 }; 33938 33939// Update common properties 33940 var normalStyleAccessPath = ['itemStyle']; 33941 var emphasisStyleAccessPath = ['emphasis', 'itemStyle']; 33942 var normalLabelAccessPath = ['label']; 33943 var emphasisLabelAccessPath = ['emphasis', 'label']; 33944 33945 /** 33946 * @param {module:echarts/data/List} data 33947 * @param {number} idx 33948 * @param {Array.<number>} symbolSize 33949 * @param {Object} [seriesScope] 33950 */ 33951 symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) { 33952 var symbolPath = this.childAt(0); 33953 var seriesModel = data.hostModel; 33954 var color = data.getItemVisual(idx, 'color'); 33955 33956 // Reset style 33957 if (symbolPath.type !== 'image') { 33958 symbolPath.useStyle({ 33959 strokeNoScale: true 33960 }); 33961 } 33962 33963 var itemStyle = seriesScope && seriesScope.itemStyle; 33964 var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle; 33965 var symbolRotate = seriesScope && seriesScope.symbolRotate; 33966 var symbolOffset = seriesScope && seriesScope.symbolOffset; 33967 var labelModel = seriesScope && seriesScope.labelModel; 33968 var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; 33969 var hoverAnimation = seriesScope && seriesScope.hoverAnimation; 33970 var cursorStyle = seriesScope && seriesScope.cursorStyle; 33971 33972 if (!seriesScope || data.hasItemOption) { 33973 var itemModel = (seriesScope && seriesScope.itemModel) 33974 ? seriesScope.itemModel : data.getItemModel(idx); 33975 33976 // Color must be excluded. 33977 // Because symbol provide setColor individually to set fill and stroke 33978 itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']); 33979 hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); 33980 33981 symbolRotate = itemModel.getShallow('symbolRotate'); 33982 symbolOffset = itemModel.getShallow('symbolOffset'); 33983 33984 labelModel = itemModel.getModel(normalLabelAccessPath); 33985 hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath); 33986 hoverAnimation = itemModel.getShallow('hoverAnimation'); 33987 cursorStyle = itemModel.getShallow('cursor'); 33988 } 33989 else { 33990 hoverItemStyle = extend({}, hoverItemStyle); 33991 } 33992 33993 var elStyle = symbolPath.style; 33994 33995 symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0); 33996 33997 if (symbolOffset) { 33998 symbolPath.attr('position', [ 33999 parsePercent$1(symbolOffset[0], symbolSize[0]), 34000 parsePercent$1(symbolOffset[1], symbolSize[1]) 34001 ]); 34002 } 34003 34004 cursorStyle && symbolPath.attr('cursor', cursorStyle); 34005 34006 // PENDING setColor before setStyle!!! 34007 symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor); 34008 34009 symbolPath.setStyle(itemStyle); 34010 34011 var opacity = data.getItemVisual(idx, 'opacity'); 34012 if (opacity != null) { 34013 elStyle.opacity = opacity; 34014 } 34015 34016 var liftZ = data.getItemVisual(idx, 'liftZ'); 34017 var z2Origin = symbolPath.__z2Origin; 34018 if (liftZ != null) { 34019 if (z2Origin == null) { 34020 symbolPath.__z2Origin = symbolPath.z2; 34021 symbolPath.z2 += liftZ; 34022 } 34023 } 34024 else if (z2Origin != null) { 34025 symbolPath.z2 = z2Origin; 34026 symbolPath.__z2Origin = null; 34027 } 34028 34029 var useNameLabel = seriesScope && seriesScope.useNameLabel; 34030 34031 setLabelStyle( 34032 elStyle, hoverItemStyle, labelModel, hoverLabelModel, 34033 { 34034 labelFetcher: seriesModel,
34035 labelDataIndex: idx, 34036 defaultText: getLabelDefaultText, 34037 isRectText: true, 34038 autoColor: color 34039 } 34040 ); 34041 34042 // Do not execute util needed. 34043 function getLabelDefaultText(idx, opt) { 34044 return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx); 34045 } 34046 34047 symbolPath.off('mouseover') 34048 .off('mouseout') 34049 .off('emphasis') 34050 .off('normal'); 34051 34052 symbolPath.hoverStyle = hoverItemStyle; 34053 34054 // FIXME 34055 // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead. 34056 setHoverStyle(symbolPath); 34057 34058 var scale = getScale(symbolSize); 34059 34060 if (hoverAnimation && seriesModel.isAnimationEnabled()) { 34061 var onEmphasis = function() { 34062 // Do not support this hover animation util some scenario required. 34063 // Animation can only be supported in hover layer when using `el.incremetal`. 34064 if (this.incremental) { 34065 return; 34066 } 34067 var ratio = scale[1] / scale[0]; 34068 this.animateTo({ 34069 scale: [ 34070 Math.max(scale[0] * 1.1, scale[0] + 3), 34071 Math.max(scale[1] * 1.1, scale[1] + 3 * ratio) 34072 ] 34073 }, 400, 'elasticOut'); 34074 }; 34075 var onNormal = function() { 34076 if (this.incremental) { 34077 return; 34078 } 34079 this.animateTo({ 34080 scale: scale 34081 }, 400, 'elasticOut'); 34082 }; 34083 symbolPath.on('mouseover', onEmphasis) 34084 .on('mouseout', onNormal) 34085 .on('emphasis', onEmphasis) 34086 .on('normal', onNormal); 34087 } 34088 }; 34089 34090 /** 34091 * @param {Function} cb 34092 * @param {Object} [opt] 34093 * @param {Object} [opt.keepLabel=true] 34094 */ 34095 symbolProto.fadeOut = function (cb, opt) { 34096 var symbolPath = this.childAt(0); 34097 // Avoid mistaken hover when fading out 34098 this.silent = symbolPath.silent = true; 34099 // Not show text when animating 34100 !(opt && opt.keepLabel) && (symbolPath.style.text = null); 34101 34102 updateProps( 34103 symbolPath, 34104 { 34105 style: {opacity: 0}, 34106 scale: [0, 0] 34107 }, 34108 this._seriesModel, 34109 this.dataIndex, 34110 cb 34111 ); 34112 }; 34113 34114 inherits(SymbolClz$1, Group); 34115 34116 /* 34117* Licensed to the Apache Software Foundation (ASF) under one 34118* or more contributor license agreements. See the NOTICE file 34119* distributed with this work for additional information 34120* regarding copyright ownership. The ASF licenses this file 34121* to you under the Apache License, Version 2.0 (the 34122* "License"); you may not use this file except in compliance 34123* with the License. You may obtain a copy of the License at 34124* 34125* http://www.apache.org/licenses/LICENSE-2.0 34126* 34127* Unless required by applicable law or agreed to in writing, 34128* software distributed under the License is distributed on an 34129* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 34130* KIND, either express or implied. See the License for the 34131* specific language governing permissions and limitations 34132* under the License. 34133*/ 34134 34135 /** 34136 * @module echarts/chart/helper/SymbolDraw 34137 */ 34138 34139 /** 34140 * @constructor 34141 * @alias module:echarts/chart/helper/SymbolDraw 34142 * @param {module:zrender/graphic/Group} [symbolCtor] 34143 */ 34144 function SymbolDraw(symbolCtor) { 34145 this.group = new Group(); 34146 34147 this._symbolCtor = symbolCtor || SymbolClz$1; 34148 } 34149 34150 var symbolDrawProto = SymbolDraw.prototype; 34151 34152 function symbolNeedsDraw(data, point, idx, opt) { 34153 return point && !isNaN(point[0]) && !isNaN(point[1]) 34154 && !(opt.isIgnore && opt.isIgnore(idx)) 34155 // We do not set clipShape on group, because it will cut part of 34156 // the symbol element shape. We use the same clip shape here as 34157 // the line clip. 34158 && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) 34159 && data.getItemVisual(idx, 'symbol') !== 'none'; 34160 } 34161 34162 /** 34163 * Update symbols draw by new data
34164 * @param {module:echarts/data/List} data 34165 * @param {Object} [opt] Or isIgnore 34166 * @param {Function} [opt.isIgnore] 34167 * @param {Object} [opt.clipShape] 34168 */ 34169 symbolDrawProto.updateData = function (data, opt) { 34170 opt = normalizeUpdateOpt(opt); 34171 34172 var group = this.group; 34173 var seriesModel = data.hostModel; 34174 var oldData = this._data; 34175 var SymbolCtor = this._symbolCtor; 34176 34177 var seriesScope = makeSeriesScope(data); 34178 34179 // There is no oldLineData only when first rendering or switching from 34180 // stream mode to normal mode, where previous elements should be removed. 34181 if (!oldData) { 34182 group.removeAll(); 34183 } 34184 34185 data.diff(oldData) 34186 .add(function (newIdx) { 34187 var point = data.getItemLayout(newIdx); 34188 if (symbolNeedsDraw(data, point, newIdx, opt)) { 34189 var symbolEl = new SymbolCtor(data, newIdx, seriesScope); 34190 symbolEl.attr('position', point); 34191 data.setItemGraphicEl(newIdx, symbolEl); 34192 group.add(symbolEl); 34193 } 34194 }) 34195 .update(function (newIdx, oldIdx) { 34196 var symbolEl = oldData.getItemGraphicEl(oldIdx); 34197 var point = data.getItemLayout(newIdx); 34198 if (!symbolNeedsDraw(data, point, newIdx, opt)) { 34199 group.remove(symbolEl); 34200 return; 34201 } 34202 if (!symbolEl) { 34203 symbolEl = new SymbolCtor(data, newIdx); 34204 symbolEl.attr('position', point); 34205 } 34206 else { 34207 symbolEl.updateData(data, newIdx, seriesScope); 34208 updateProps(symbolEl, { 34209 position: point 34210 }, seriesModel); 34211 } 34212 34213 // Add back 34214 group.add(symbolEl); 34215 34216 data.setItemGraphicEl(newIdx, symbolEl); 34217 }) 34218 .remove(function (oldIdx) { 34219 var el = oldData.getItemGraphicEl(oldIdx); 34220 el && el.fadeOut(function () { 34221 group.remove(el); 34222 }); 34223 }) 34224 .execute(); 34225 34226 this._data = data; 34227 }; 34228 34229 symbolDrawProto.isPersistent = function () { 34230 return true; 34231 }; 34232 34233 symbolDrawProto.updateLayout = function () { 34234 var data = this._data; 34235 if (data) { 34236 // Not use animation 34237 data.eachItemGraphicEl(function (el, idx) { 34238 var point = data.getItemLayout(idx); 34239 el.attr('position', point); 34240 }); 34241 } 34242 }; 34243 34244 symbolDrawProto.incrementalPrepareUpdate = function (data) { 34245 this._seriesScope = makeSeriesScope(data); 34246 this._data = null; 34247 this.group.removeAll(); 34248 }; 34249 34250 /** 34251 * Update symbols draw by new data 34252 * @param {module:echarts/data/List} data 34253 * @param {Object} [opt] Or isIgnore 34254 * @param {Function} [opt.isIgnore] 34255 * @param {Object} [opt.clipShape] 34256 */ 34257 symbolDrawProto.incrementalUpdate = function (taskParams, data, opt) { 34258 opt = normalizeUpdateOpt(opt); 34259 34260 function updateIncrementalAndHover(el) { 34261 if (!el.isGroup) { 34262 el.incremental = el.useHoverLayer = true; 34263 } 34264 } 34265 for (var idx = taskParams.start; idx < taskParams.end; idx++) { 34266 var point = data.getItemLayout(idx); 34267 if (symbolNeedsDraw(data, point, idx, opt)) { 34268 var el = new this._symbolCtor(data, idx, this._seriesScope); 34269 el.traverse(updateIncrementalAndHover); 34270 el.attr('position', point); 34271 this.group.add(el); 34272 data.setItemGraphicEl(idx, el); 34273 } 34274 } 34275 }; 34276 34277 function normalizeUpdateOpt(opt) { 34278 if (opt != null && !isObject$1(opt)) { 34279 opt = {isIgnore: opt}; 34280 } 34281 return opt || {}; 34282 } 34283 34284 symbolDrawProto.remove = function (enableAnimation) { 34285 var group = this.group; 34286 var data = this._data; 34287 // Incremental model do not have this._data. 34288 if (data && enableAnimation) { 34289 data.eachItemGraphicEl(function (el) { 34290 el.fadeOut(function () { 34291 group.remove(el); 34292 }); 34293 }); 34294 } 34295 else { 34296 group.removeAll(); 34297 } 34298 }; 34299 34300 function makeSeriesScope(data) { 34301 var seriesModel = data.hostModel; 34302 return { 34303 itemStyle: seriesModel.getModel('itemStyle').getItemStyle(['color']), 34304 hoverItemStyle: seriesModel.getModel('emphasis.itemStyle').getItemStyle(), 34305 symbolRotate: seriesModel.get('symbolRotate'), 34306 symbolOffset: seriesModel.get('symbolOffset'), 34307 hoverAnimation: seriesModel.get('hoverAnimation'), 34308 labelModel: seriesModel.getModel('label'), 34309 hoverLabelModel: seriesModel.getModel('emphasis.label'), 34310 cursorStyle: seriesModel.get('cursor') 34311 }; 34312 } 34313 34314 /* 34315* Licensed to the Apache Software Foundation (ASF) under one 34316* or more contributor license agreements. See the NOTICE file 34317* distributed with this work for additional information 34318* regarding copyright ownership. The ASF licenses this file 34319* to you under the Apache License, Version 2.0 (the 34320* "License");
34320 you may not use this file except in compliance 34321* with the License. You may obtain a copy of the License at 34322* 34323* http://www.apache.org/licenses/LICENSE-2.0 34324* 34325* Unless required by applicable law or agreed to in writing, 34326* software distributed under the License is distributed on an 34327* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 34328* KIND, either express or implied. See the License for the 34329* specific language governing permissions and limitations 34330* under the License. 34331*/ 34332 34333 /** 34334 * @param {Object} coordSys 34335 * @param {module:echarts/data/List} data 34336 * @param {string} valueOrigin lineSeries.option.areaStyle.origin 34337 */ 34338 function prepareDataCoordInfo(coordSys, data, valueOrigin) { 34339 var baseAxis = coordSys.getBaseAxis(); 34340 var valueAxis = coordSys.getOtherAxis(baseAxis); 34341 var valueStart = getValueStart(valueAxis, valueOrigin); 34342 34343 var baseAxisDim = baseAxis.dim; 34344 var valueAxisDim = valueAxis.dim; 34345 var valueDim = data.mapDimension(valueAxisDim); 34346 var baseDim = data.mapDimension(baseAxisDim); 34347 var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0; 34348 34349 var dims = map(coordSys.dimensions, function (coordDim) { 34350 return data.mapDimension(coordDim); 34351 }); 34352 34353 var stacked; 34354 var stackResultDim = data.getCalculationInfo('stackResultDimension'); 34355 if (stacked |= isDimensionStacked(data, dims[0] /*, dims[1]*/)) { // jshint ignore:line 34356 dims[0] = stackResultDim; 34357 } 34358 if (stacked |= isDimensionStacked(data, dims[1] /*, dims[0]*/)) { // jshint ignore:line 34359 dims[1] = stackResultDim; 34360 } 34361 34362 return { 34363 dataDimsForPoint: dims, 34364 valueStart: valueStart, 34365 valueAxisDim: valueAxisDim, 34366 baseAxisDim: baseAxisDim, 34367 stacked: !!stacked, 34368 valueDim: valueDim, 34369 baseDim: baseDim, 34370 baseDataOffset: baseDataOffset, 34371 stackedOverDimension: data.getCalculationInfo('stackedOverDimension') 34372 }; 34373 } 34374 34375 function getValueStart(valueAxis, valueOrigin) { 34376 var valueStart = 0; 34377 var extent = valueAxis.scale.getExtent(); 34378 34379 if (valueOrigin === 'start') { 34380 valueStart = extent[0]; 34381 } 34382 else if (valueOrigin === 'end') { 34383 valueStart = extent[1]; 34384 } 34385 // auto 34386 else { 34387 // Both positive 34388 if (extent[0] > 0) { 34389 valueStart = extent[0]; 34390 } 34391 // Both negative 34392 else if (extent[1] < 0) { 34393 valueStart = extent[1]; 34394 } 34395 // If is one positive, and one negative, onZero shall be true 34396 } 34397 34398 return valueStart; 34399 } 34400 34401 function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) { 34402 var value = NaN; 34403 if (dataCoordInfo.stacked) { 34404 value = data.get(data.getCalculationInfo('stackedOverDimension'), idx); 34405 } 34406 if (isNaN(value)) { 34407 value = dataCoordInfo.valueStart; 34408 } 34409 34410 var baseDataOffset = dataCoordInfo.baseDataOffset; 34411 var stackedData = []; 34412 stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx); 34413 stackedData[1 - baseDataOffset] = value; 34414 34415 return coordSys.dataToPoint(stackedData); 34416 } 34417 34418 /* 34419* Licensed to the Apache Software Foundation (ASF) under one 34420* or more contributor license agreements. See the NOTICE file 34421* distributed with this work for additional information 34422* regarding copyright ownership. The ASF licenses this file 34423* to you under the Apache License, Version 2.0 (the 34424* "License"); you may not use this file except in compliance 34425* with the License. You may obtain a copy of the License at 34426* 34427* http://www.apache.org/licenses/LICENSE-2.0 34428* 34429* Unless required by applicable law or agreed to in writing, 34430* software distributed under the License is distributed on an 34431* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 34432* KIND, either express or implied. See the License for the 34433* specific language governing permissions and limitations 34434* under the License. 34435*/ 34436 34437// var arrayDiff = require('zrender/src/core/arrayDiff'); 34438// 'zrender/src/core/arrayDiff' has been used before, but it did 34439// not do well in performance when roam with fixed dataZoom window. 34440 34441// function convertToIntId(newIdList, oldIdList) { 34442// // Generate int id instead of string id.
34443// // Compare string maybe slow in score function of arrDiff 34444 34445// // Assume id in idList are all unique 34446// var idIndicesMap = {}; 34447// var idx = 0; 34448// for (var i = 0; i < newIdList.length; i++) { 34449// idIndicesMap[newIdList[i]] = idx; 34450// newIdList[i] = idx++; 34451// } 34452// for (var i = 0; i < oldIdList.length; i++) { 34453// var oldId = oldIdList[i]; 34454// // Same with newIdList 34455// if (idIndicesMap[oldId]) { 34456// oldIdList[i] = idIndicesMap[oldId]; 34457// } 34458// else { 34459// oldIdList[i] = idx++; 34460// } 34461// } 34462// } 34463 34464 function diffData(oldData, newData) { 34465 var diffResult = []; 34466 34467 newData.diff(oldData) 34468 .add(function (idx) { 34469 diffResult.push({cmd: '+', idx: idx}); 34470 }) 34471 .update(function (newIdx, oldIdx) { 34472 diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx}); 34473 }) 34474 .remove(function (idx) { 34475 diffResult.push({cmd: '-', idx: idx}); 34476 }) 34477 .execute(); 34478 34479 return diffResult; 34480 } 34481 34482 var lineAnimationDiff = function ( 34483 oldData, newData, 34484 oldStackedOnPoints, newStackedOnPoints, 34485 oldCoordSys, newCoordSys, 34486 oldValueOrigin, newValueOrigin 34487 ) { 34488 var diff = diffData(oldData, newData); 34489 34490 // var newIdList = newData.mapArray(newData.getId); 34491 // var oldIdList = oldData.mapArray(oldData.getId); 34492 34493 // convertToIntId(newIdList, oldIdList); 34494 34495 // // FIXME One data ? 34496 // diff = arrayDiff(oldIdList, newIdList); 34497 34498 var currPoints = []; 34499 var nextPoints = []; 34500 // Points for stacking base line 34501 var currStackedPoints = []; 34502 var nextStackedPoints = []; 34503 34504 var status = []; 34505 var sortedIndices = []; 34506 var rawIndices = []; 34507 34508 var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); 34509 var oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin); 34510 34511 for (var i = 0; i < diff.length; i++) { 34512 var diffItem = diff[i]; 34513 var pointAdded = true; 34514 34515 // FIXME, animation is not so perfect when dataZoom window moves fast 34516 // Which is in case remvoing or add more than one data in the tail or head 34517 switch (diffItem.cmd) { 34518 case '=': 34519 var currentPt = oldData.getItemLayout(diffItem.idx); 34520 var nextPt = newData.getItemLayout(diffItem.idx1); 34521 // If previous data is NaN, use next point directly 34522 if (isNaN(currentPt[0]) || isNaN(currentPt[1])) { 34523 currentPt = nextPt.slice(); 34524 } 34525 currPoints.push(currentPt); 34526 nextPoints.push(nextPt); 34527 34528 currStackedPoints.push(oldStackedOnPoints[diffItem.idx]); 34529 nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]); 34530 34531 rawIndices.push(newData.getRawIndex(diffItem.idx1)); 34532 break; 34533 case '+': 34534 var idx = diffItem.idx; 34535 currPoints.push( 34536 oldCoordSys.dataToPoint([ 34537 newData.get(newDataOldCoordInfo.dataDimsForPoint[0], idx), 34538 newData.get(newDataOldCoordInfo.dataDimsForPoint[1], idx) 34539 ]) 34540 ); 34541 34542 nextPoints.push(newData.getItemLayout(idx).slice()); 34543 34544 currStackedPoints.push( 34545 getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, idx) 34546 ); 34547 nextStackedPoints.push(newStackedOnPoints[idx]); 34548 34549 rawIndices.push(newData.getRawIndex(idx)); 34550 break; 34551 case '-': 34552 var idx = diffItem.idx; 34553 var rawIndex = oldData.getRawIndex(idx); 34554 // Data is replaced. In the case of dynamic data queue 34555 // FIXME FIXME FIXME 34556 if (rawIndex !== idx) { 34557 currPoints.push(oldData.getItemLayout(idx)); 34558 nextPoints.push(newCoordSys.dataToPoint([ 34559 oldData.get(oldDataNewCoordInfo.dataDimsForPoint[0], idx), 34560 oldData.get(oldDataNewCoordInfo.dataDimsForPoint[1], idx) 34561 ])); 34562 34563 currStackedPoints.push(oldStackedOnPoints[idx]); 34564 nextStackedPoints.push( 34565 getStackedOnPoint(oldDataNewCoordInfo, newCoordSys, oldData, idx) 34566 ); 34567 34568 rawIndices.push(rawIndex); 34569 } 34570 else { 34571 pointAdded = false; 34572 } 34573 } 34574 34575 // Original indices 34576 if (pointAdded) { 34577 status.push(diffItem); 34578 sortedIndices.push(sortedIndices.length); 34579 } 34580 } 34581 34582 // Diff result may be crossed if all items are changed 34583 // Sort by data index 34584 sortedIndices.sort(function (a, b) { 34585 return rawIndices[a] - rawIndices[b]; 34586 }); 34587 34588 var sortedCurrPoints = []; 34589 var sortedNextPoints = []; 34590 34591 var sortedCurrStackedPoints = []; 34592 var sortedNextStackedPoints = []; 34593 34594 var sortedStatus = []; 34595 for (var i = 0; i < sortedIndices.length; i++) { 34596 var idx = sortedIndices[i]; 34597 sortedCurrPoints[i] = currPoints[idx]; 34598 sortedNextPoints[i] = nextPoints[idx]; 34599 34600 sortedCurrStackedPoints[i] = currStackedPoints[idx]; 34601 sortedNextStackedPoints[i] = nextStackedPoints[idx]; 34602 34603 sortedStatus[i] = status[idx]; 34604 } 34605 34606 return { 34607 current: sortedCurrPoints, 34608 next: sortedNextPoints, 34609 34610 stackedOnCurrent: sortedCurrStackedPoints, 34611 stackedOnNext: sortedNextStackedPoints, 34612 34613 status: sortedStatus 34614 }; 34615 }; 34616 34617 /* 34618* Licensed to the Apache Software Foundation (ASF) under one 34619* or more contributor license agreements. See the NOTICE file 34620* distributed with this work for additional information 34621* regarding copyright ownership. The ASF licenses this file 34622* to you under the Apache License, Version 2.0 (the 34623* "License");
34623 you may not use this file except in compliance 34624* with the License. You may obtain a copy of the License at 34625* 34626* http://www.apache.org/licenses/LICENSE-2.0 34627* 34628* Unless required by applicable law or agreed to in writing, 34629* software distributed under the License is distributed on an 34630* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 34631* KIND, either express or implied. See the License for the 34632* specific language governing permissions and limitations 34633* under the License. 34634*/ 34635 34636// Poly path support NaN point 34637 34638 var vec2Min = min; 34639 var vec2Max = max; 34640 34641 var scaleAndAdd$1 = scaleAndAdd; 34642 var v2Copy = copy; 34643 34644// Temporary variable 34645 var v = []; 34646 var cp0 = []; 34647 var cp1 = []; 34648 34649 function isPointNull(p) { 34650 return isNaN(p[0]) || isNaN(p[1]); 34651 } 34652 34653 function drawSegment( 34654 ctx, points, start, segLen, allLen, 34655 dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls 34656 ) { 34657 // if (smoothMonotone == null) { 34658 // if (isMono(points, 'x')) { 34659 // return drawMono(ctx, points, start, segLen, allLen, 34660 // dir, smoothMin, smoothMax, smooth, 'x', connectNulls); 34661 // } 34662 // else if (isMono(points, 'y')) { 34663 // return drawMono(ctx, points, start, segLen, allLen, 34664 // dir, smoothMin, smoothMax, smooth, 'y', connectNulls); 34665 // } 34666 // else { 34667 // return drawNonMono.apply(this, arguments); 34668 // } 34669 // } 34670 // else if (smoothMonotone !== 'none' && isMono(points, smoothMonotone)) { 34671 // return drawMono.apply(this, arguments); 34672 // } 34673 // else { 34674 // return drawNonMono.apply(this, arguments); 34675 // } 34676 if (smoothMonotone === 'none' || !smoothMonotone) { 34677 return drawNonMono.apply(this, arguments); 34678 } 34679 else { 34680 return drawMono.apply(this, arguments); 34681 } 34682 } 34683 34684 /** 34685 * Check if points is in monotone. 34686 * 34687 * @param {number[][]} points Array of points which is in [x, y] form 34688 * @param {string} smoothMonotone 'x', 'y', or 'none', stating for which 34689 * dimension that is checking. 34690 * If is 'none', `drawNonMono` should be 34691 * called. 34692 * If is undefined, either being monotone 34693 * in 'x' or 'y' will call `drawMono`. 34694 */ 34695// function isMono(points, smoothMonotone) { 34696// if (points.length <= 1) { 34697// return true; 34698// } 34699 34700// var dim = smoothMonotone === 'x' ? 0 : 1; 34701// var last = points[0][dim]; 34702// var lastDiff = 0; 34703// for (var i = 1; i < points.length; ++i) { 34704// var diff = points[i][dim] - last; 34705// if (!isNaN(diff) && !isNaN(lastDiff) 34706// && diff !== 0 && lastDiff !== 0 34707// && ((diff >= 0) !== (lastDiff >= 0)) 34708// ) { 34709// return false; 34710// } 34711// if (!isNaN(diff) && diff !== 0) { 34712// lastDiff = diff; 34713// last = points[i][dim]; 34714// } 34715// } 34716// return true; 34717// } 34718 34719 /** 34720 * Draw smoothed line in monotone, in which only vertical or horizontal bezier 34721 * control points will be used. This should be used when points are monotone 34722 * either in x or y dimension. 34723 */ 34724 function drawMono( 34725 ctx, points, start, segLen, allLen, 34726 dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls 34727 ) { 34728 var prevIdx = 0; 34729 var idx = start; 34730 for (var k = 0; k < segLen; k++) { 34731 var p = points[idx]; 34732 if (idx >= allLen || idx < 0) { 34733 break; 34734 } 34735 if (isPointNull(p)) { 34736 if (connectNulls) { 34737 idx += dir; 34738 continue; 34739 } 34740 break; 34741 } 34742 34743 if (idx === start) { 34744 ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); 34745 } 34746 else { 34747 if (smooth > 0) { 34748 var prevP = points[prevIdx]; 34749 var dim = smoothMonotone === 'y' ? 1 : 0; 34750 34751 // Length of control point to p, either in x or y, but not both 34752 var ctrlLen = (p[dim] - prevP[dim]) * smooth; 34753 34754 v2Copy(cp0, prevP); 34755 cp0[dim] = prevP[dim] + ctrlLen; 34756 34757 v2Copy(cp1, p); 34758 cp1[dim] = p[dim] - ctrlLen; 34759 34760 ctx.bezierCurveTo( 34761 cp0[0], cp0[1], 34762 cp1[0], cp1[1], 34763 p[0], p[1] 34764 ); 34765 } 34766 else { 34767 ctx.lineTo(p[0], p[1]); 34768 } 34769 } 34770 34771 prevIdx = idx; 34772 idx += dir; 34773 } 34774 34775 return k; 34776 } 34777 34778 /** 34779 * Draw smoothed line in non-monotone, in may cause undesired curve in extreme 34780 * situations. This should be used when points are non-monotone neither in x or 34781 * y dimension. 34782 */ 34783 function drawNonMono( 34784 ctx, points, start, segLen, allLen, 34785 dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls 34786 ) { 34787 var prevIdx = 0; 34788 var idx = start; 34789 for (var k = 0; k < segLen; k++) { 34790 var p = points[idx]; 34791 if (idx >= allLen || idx < 0) { 34792 break; 34793 } 34794 if (isPointNull(p)) { 34795 if (connectNulls) { 34796 idx += dir; 34797 continue; 34798 } 34799 break; 34800 } 34801 34802 if (idx === start) { 34803 ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); 34804 v2Copy(cp0, p); 34805 } 34806 else { 34807 if (smooth > 0) { 34808 var nextIdx = idx + dir; 34809 var nextP = points[nextIdx]; 34810 if (connectNulls) { 34811 // Find next point not null 34812 while (nextP && isPointNull(points[nextIdx])) { 34813 nextIdx += dir; 34814 nextP = points[nextIdx]; 34815 } 34816 } 34817 34818 var ratioNextSeg = 0.5; 34819 var prevP = points[prevIdx]; 34820 var nextP = points[nextIdx]; 34821 // Last point 34822 if (!nextP || isPointNull(nextP)) { 34823 v2Copy(cp1, p); 34824 } 34825 else { 34826 // If next data is null in not connect case 34827 if (isPointNull(nextP) && !connectNulls) { 34828 nextP = p; 34829 } 34830 34831 sub(v, nextP, prevP); 34832 34833 var lenPrevSeg; 34834 var lenNextSeg; 34835 if (smoothMonotone === 'x' || smoothMonotone === 'y') { 34836 var dim = smoothMonotone === 'x' ? 0 : 1; 34837 lenPrevSeg = Math.abs(p[dim] - prevP[dim]); 34838 lenNextSeg = Math.abs(p[dim] - nextP[dim]); 34839 } 34840 else { 34841 lenPrevSeg = dist(p, prevP); 34842 lenNextSeg = dist(p, nextP); 34843 } 34844 34845 // Use ratio of seg length 34846 ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); 34847 34848 scaleAndAdd$1(cp1, p, v, -smooth * (1 - ratioNextSeg)); 34849 } 34850 // Smooth constraint 34851 vec2Min(cp0, cp0, smoothMax); 34852 vec2Max(cp0, cp0, smoothMin); 34853 vec2Min(cp1, cp1, smoothMax); 34854 vec2Max(cp1, cp1, smoothMin); 34855 34856 ctx.bezierCurveTo( 34857 cp0[0], cp0[1], 34858 cp1[0], cp1[1], 34859 p[0], p[1] 34860 ); 34861 // cp0 of next segment 34862 scaleAndAdd$1(cp0, p, v, smooth * ratioNextSeg); 34863 } 34864 else { 34865 ctx.lineTo(p[0], p[1]); 34866 } 34867 } 34868 34869 prevIdx = idx; 34870 idx += dir; 34871 } 34872 34873 return k; 34874 } 34875 34876 function getBoundingBox(points, smoothConstraint) {
34877 var ptMin = [Infinity, Infinity]; 34878 var ptMax = [-Infinity, -Infinity]; 34879 if (smoothConstraint) { 34880 for (var i = 0; i < points.length; i++) { 34881 var pt = points[i]; 34882 if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; } 34883 if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; } 34884 if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; } 34885 if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; } 34886 } 34887 } 34888 return { 34889 min: smoothConstraint ? ptMin : ptMax, 34890 max: smoothConstraint ? ptMax : ptMin 34891 }; 34892 } 34893 34894 var Polyline$1 = Path.extend({ 34895 34896 type: 'ec-polyline', 34897 34898 shape: { 34899 points: [], 34900 34901 smooth: 0, 34902 34903 smoothConstraint: true, 34904 34905 smoothMonotone: null, 34906 34907 connectNulls: false 34908 }, 34909 34910 style: { 34911 fill: null, 34912 34913 stroke: '#000' 34914 }, 34915 34916 brush: fixClipWithShadow(Path.prototype.brush), 34917 34918 buildPath: function (ctx, shape) { 34919 var points = shape.points; 34920 34921 var i = 0; 34922 var len$$1 = points.length; 34923 34924 var result = getBoundingBox(points, shape.smoothConstraint); 34925 34926 if (shape.connectNulls) { 34927 // Must remove first and last null values avoid draw error in polygon 34928 for (; len$$1 > 0; len$$1--) { 34929 if (!isPointNull(points[len$$1 - 1])) { 34930 break; 34931 } 34932 } 34933 for (; i < len$$1; i++) { 34934 if (!isPointNull(points[i])) { 34935 break; 34936 } 34937 } 34938 } 34939 while (i < len$$1) { 34940 i += drawSegment( 34941 ctx, points, i, len$$1, len$$1, 34942 1, result.min, result.max, shape.smooth, 34943 shape.smoothMonotone, shape.connectNulls 34944 ) + 1; 34945 } 34946 } 34947 }); 34948 34949 var Polygon$1 = Path.extend({ 34950 34951 type: 'ec-polygon', 34952 34953 shape: { 34954 points: [], 34955 34956 // Offset between stacked base points and points 34957 stackedOnPoints: [], 34958 34959 smooth: 0, 34960 34961 stackedOnSmooth: 0, 34962 34963 smoothConstraint: true, 34964 34965 smoothMonotone: null, 34966 34967 connectNulls: false 34968 }, 34969 34970 brush: fixClipWithShadow(Path.prototype.brush), 34971 34972 buildPath: function (ctx, shape) { 34973 var points = shape.points; 34974 var stackedOnPoints = shape.stackedOnPoints; 34975 34976 var i = 0; 34977 var len$$1 = points.length; 34978 var smoothMonotone = shape.smoothMonotone; 34979 var bbox = getBoundingBox(points, shape.smoothConstraint); 34980 var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint); 34981 34982 if (shape.connectNulls) { 34983 // Must remove first and last null values avoid draw error in polygon 34984 for (; len$$1 > 0; len$$1--) { 34985 if (!isPointNull(points[len$$1 - 1])) { 34986 break; 34987 } 34988 } 34989 for (; i < len$$1; i++) { 34990 if (!isPointNull(points[i])) { 34991 break; 34992 } 34993 } 34994 } 34995 while (i < len$$1) { 34996 var k = drawSegment( 34997 ctx, points, i, len$$1, len$$1, 34998 1, bbox.min, bbox.max, shape.smooth, 34999 smoothMonotone, shape.connectNulls 35000 ); 35001 drawSegment( 35002 ctx, stackedOnPoints, i + k - 1, k, len$$1, 35003 -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, 35004 smoothMonotone, shape.connectNulls 35005 ); 35006 i += k + 1; 35007 35008 ctx.closePath(); 35009 } 35010 } 35011 }); 35012 35013 /* 35014* Licensed to the Apache Software Foundation (ASF) under one 35015* or more contributor license agreements. See the NOTICE file 35016* distributed with this work for additional information 35017* regarding copyright ownership. The ASF licenses this file 35018* to you under the Apache License, Version 2.0 (the 35019* "License");
35019 you may not use this file except in compliance 35020* with the License. You may obtain a copy of the License at 35021* 35022* http://www.apache.org/licenses/LICENSE-2.0 35023* 35024* Unless required by applicable law or agreed to in writing, 35025* software distributed under the License is distributed on an 35026* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 35027* KIND, either express or implied. See the License for the 35028* specific language governing permissions and limitations 35029* under the License. 35030*/ 35031 35032// FIXME step not support polar 35033 35034 function isPointsSame(points1, points2) { 35035 if (points1.length !== points2.length) { 35036 return; 35037 } 35038 for (var i = 0; i < points1.length; i++) { 35039 var p1 = points1[i]; 35040 var p2 = points2[i]; 35041 if (p1[0] !== p2[0] || p1[1] !== p2[1]) { 35042 return; 35043 } 35044 } 35045 return true; 35046 } 35047 35048 function getSmooth(smooth) { 35049 return typeof (smooth) === 'number' ? smooth : (smooth ? 0.5 : 0); 35050 } 35051 35052 function getAxisExtentWithGap(axis) { 35053 var extent = axis.getGlobalExtent(); 35054 if (axis.onBand) { 35055 // Remove extra 1px to avoid line miter in clipped edge 35056 var halfBandWidth = axis.getBandWidth() / 2 - 1; 35057 var dir = extent[1] > extent[0] ? 1 : -1; 35058 extent[0] += dir * halfBandWidth; 35059 extent[1] -= dir * halfBandWidth; 35060 } 35061 return extent; 35062 } 35063 35064 /** 35065 * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys 35066 * @param {module:echarts/data/List} data 35067 * @param {Object} dataCoordInfo 35068 * @param {Array.<Array.<number>>} points 35069 */ 35070 function getStackedOnPoints(coordSys, data, dataCoordInfo) { 35071 if (!dataCoordInfo.valueDim) { 35072 return []; 35073 } 35074 35075 var points = []; 35076 for (var idx = 0, len = data.count(); idx < len; idx++) { 35077 points.push(getStackedOnPoint(dataCoordInfo, coordSys, data, idx)); 35078 } 35079 35080 return points; 35081 } 35082 35083 function createGridClipShape(cartesian, hasAnimation, forSymbol, seriesModel) { 35084 var xExtent = getAxisExtentWithGap(cartesian.getAxis('x')); 35085 var yExtent = getAxisExtentWithGap(cartesian.getAxis('y')); 35086 var isHorizontal = cartesian.getBaseAxis().isHorizontal(); 35087 35088 var x = Math.min(xExtent[0], xExtent[1]); 35089 var y = Math.min(yExtent[0], yExtent[1]); 35090 var width = Math.max(xExtent[0], xExtent[1]) - x; 35091 var height = Math.max(yExtent[0], yExtent[1]) - y; 35092 35093 // Avoid float number rounding error for symbol on the edge of axis extent. 35094 // See #7913 and `test/dataZoom-clip.html`. 35095 if (forSymbol) { 35096 x -= 0.5; 35097 width += 0.5; 35098 y -= 0.5; 35099 height += 0.5; 35100 } 35101 else { 35102 var lineWidth = seriesModel.get('lineStyle.width') || 2; 35103 // Expand clip shape to avoid clipping when line value exceeds axis 35104 var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height); 35105 if (isHorizontal) { 35106 y -= expandSize; 35107 height += expandSize * 2; 35108 } 35109 else { 35110 x -= expandSize; 35111 width += expandSize * 2; 35112 } 35113 } 35114 35115 var clipPath = new Rect({ 35116 shape: { 35117 x: x, 35118 y: y, 35119 width: width, 35120 height: height 35121 } 35122 }); 35123 35124 if (hasAnimation) { 35125 clipPath.shape[isHorizontal ? 'width' : 'height'] = 0; 35126 initProps(clipPath, { 35127 shape: { 35128 width: width, 35129 height: height 35130 } 35131 }, seriesModel); 35132 } 35133 35134 return clipPath; 35135 } 35136 35137 function createPolarClipShape(polar, hasAnimation, forSymbol, seriesModel) { 35138 var angleAxis = polar.getAngleAxis(); 35139 var radiusAxis = polar.getRadiusAxis(); 35140 35141 var radiusExtent = radiusAxis.getExtent().slice(); 35142 radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse(); 35143 var angleExtent = angleAxis.getExtent(); 35144 35145 var RADIAN = Math.PI / 180; 35146 35147 // Avoid float number rounding error for symbol on the edge of axis extent. 35148 if (forSymbol) { 35149 radiusExtent[0] -= 0.5; 35150 radiusExtent[1] += 0.5; 35151 } 35152 35153 var clipPath = new Sector({ 35154 shape: { 35155 cx: round$1(polar.cx, 1), 35156 cy: round$1(polar.cy, 1), 35157 r0: round$1(radiusExtent[0], 1), 35158 r: round$1(radiusExtent[1], 1), 35159 startAngle: -angleExtent[0] * RADIAN, 35160 endAngle: -angleExtent[1] * RADIAN, 35161 clockwise: angleAxis.inverse 35162 } 35163 }); 35164 35165 if (hasAnimation) { 35166 clipPath.shape.endAngle = -angleExtent[0] * RADIAN; 35167 initProps(clipPath, { 35168 shape: { 35169 endAngle: -angleExtent[1] * RADIAN 35170 } 35171 }, seriesModel); 35172 } 35173 35174 return clipPath; 35175 } 35176 35177 function createClipShape(coordSys, hasAnimation, forSymbol, seriesModel) { 35178 return coordSys.type === 'polar'
35179 ? createPolarClipShape(coordSys, hasAnimation, forSymbol, seriesModel) 35180 : createGridClipShape(coordSys, hasAnimation, forSymbol, seriesModel); 35181 } 35182 35183 function turnPointsIntoStep(points, coordSys, stepTurnAt) { 35184 var baseAxis = coordSys.getBaseAxis(); 35185 var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; 35186 35187 var stepPoints = []; 35188 for (var i = 0; i < points.length - 1; i++) { 35189 var nextPt = points[i + 1]; 35190 var pt = points[i]; 35191 stepPoints.push(pt); 35192 35193 var stepPt = []; 35194 switch (stepTurnAt) { 35195 case 'end': 35196 stepPt[baseIndex] = nextPt[baseIndex]; 35197 stepPt[1 - baseIndex] = pt[1 - baseIndex]; 35198 // default is start 35199 stepPoints.push(stepPt); 35200 break; 35201 case 'middle': 35202 // default is start 35203 var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2; 35204 var stepPt2 = []; 35205 stepPt[baseIndex] = stepPt2[baseIndex] = middle; 35206 stepPt[1 - baseIndex] = pt[1 - baseIndex]; 35207 stepPt2[1 - baseIndex] = nextPt[1 - baseIndex]; 35208 stepPoints.push(stepPt); 35209 stepPoints.push(stepPt2); 35210 break; 35211 default: 35212 stepPt[baseIndex] = pt[baseIndex]; 35213 stepPt[1 - baseIndex] = nextPt[1 - baseIndex]; 35214 // default is start 35215 stepPoints.push(stepPt); 35216 } 35217 } 35218 // Last points 35219 points[i] && stepPoints.push(points[i]); 35220 return stepPoints; 35221 } 35222 35223 function getVisualGradient(data, coordSys) { 35224 var visualMetaList = data.getVisual('visualMeta'); 35225 if (!visualMetaList || !visualMetaList.length || !data.count()) { 35226 // When data.count() is 0, gradient range can not be calculated. 35227 return; 35228 } 35229 35230 if (coordSys.type !== 'cartesian2d') { 35231 if (__DEV__) { 35232 console.warn('Visual map on line style is only supported on cartesian2d.'); 35233 } 35234 return; 35235 } 35236 35237 var coordDim; 35238 var visualMeta; 35239 35240 for (var i = visualMetaList.length - 1; i >= 0; i--) { 35241 var dimIndex = visualMetaList[i].dimension; 35242 var dimName = data.dimensions[dimIndex]; 35243 var dimInfo = data.getDimensionInfo(dimName); 35244 coordDim = dimInfo && dimInfo.coordDim; 35245 // Can only be x or y 35246 if (coordDim === 'x' || coordDim === 'y') { 35247 visualMeta = visualMetaList[i]; 35248 break; 35249 } 35250 } 35251 35252 if (!visualMeta) { 35253 if (__DEV__) { 35254 console.warn('Visual map on line style only support x or y dimension.'); 35255 } 35256 return; 35257 } 35258 35259 // If the area to be rendered is bigger than area defined by LinearGradient, 35260 // the canvas spec prescribes that the color of the first stop and the last 35261 // stop should be used. But if two stops are added at offset 0, in effect 35262 // browsers use the color of the second stop to render area outside 35263 // LinearGradient. So we can only infinitesimally extend area defined in 35264 // LinearGradient to render `outerColors`. 35265 35266 var axis = coordSys.getAxis(coordDim); 35267 35268 // dataToCoor mapping may not be linear, but must be monotonic. 35269 var colorStops = map(visualMeta.stops, function (stop) { 35270 return { 35271 coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)), 35272 color: stop.color 35273 }; 35274 }); 35275 var stopLen = colorStops.length; 35276 var outerColors = visualMeta.outerColors.slice(); 35277 35278 if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) { 35279 colorStops.reverse(); 35280 outerColors.reverse(); 35281 } 35282 35283 var tinyExtent = 10; // Arbitrary value: 10px 35284 var minCoord = colorStops[0].coord - tinyExtent; 35285 var maxCoord = colorStops[stopLen - 1].coord + tinyExtent; 35286 var coordSpan = maxCoord - minCoord; 35287 35288 if (coordSpan < 1e-3) { 35289 return 'transparent'; 35290 } 35291 35292 each$1(colorStops, function (stop) { 35293 stop.offset = (stop.coord - minCoord) / coordSpan; 35294 }); 35295 colorStops.push({ 35296 offset: stopLen ? colorStops[stopLen - 1].offset : 0.5, 35297 color: outerColors[1] || 'transparent'
35298 }); 35299 colorStops.unshift({ // notice colorStops.length have been changed. 35300 offset: stopLen ? colorStops[0].offset : 0.5, 35301 color: outerColors[0] || 'transparent' 35302 }); 35303 35304 // zrUtil.each(colorStops, function (colorStop) { 35305 // // Make sure each offset has rounded px to avoid not sharp edge 35306 // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start); 35307 // }); 35308 35309 var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true); 35310 gradient[coordDim] = minCoord; 35311 gradient[coordDim + '2'] = maxCoord; 35312 35313 return gradient; 35314 } 35315 35316 function getIsIgnoreFunc(seriesModel, data, coordSys) { 35317 var showAllSymbol = seriesModel.get('showAllSymbol'); 35318 var isAuto = showAllSymbol === 'auto'; 35319 35320 if (showAllSymbol && !isAuto) { 35321 return; 35322 } 35323 35324 var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; 35325 if (!categoryAxis) { 35326 return; 35327 } 35328 35329 // Note that category label interval strategy might bring some weird effect 35330 // in some scenario: users may wonder why some of the symbols are not 35331 // displayed. So we show all symbols as possible as we can. 35332 if (isAuto 35333 // Simplify the logic, do not determine label overlap here. 35334 && canShowAllSymbolForCategory(categoryAxis, data) 35335 ) { 35336 return; 35337 } 35338 35339 // Otherwise follow the label interval strategy on category axis. 35340 var categoryDataDim = data.mapDimension(categoryAxis.dim); 35341 var labelMap = {}; 35342 35343 each$1(categoryAxis.getViewLabels(), function (labelItem) { 35344 labelMap[labelItem.tickValue] = 1; 35345 }); 35346 35347 return function (dataIndex) { 35348 return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex)); 35349 }; 35350 } 35351 35352 function canShowAllSymbolForCategory(categoryAxis, data) { 35353 // In mose cases, line is monotonous on category axis, and the label size 35354 // is close with each other. So we check the symbol size and some of the 35355 // label size alone with the category axis to estimate whether all symbol 35356 // can be shown without overlap. 35357 var axisExtent = categoryAxis.getExtent(); 35358 var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count(); 35359 isNaN(availSize) && (availSize = 0); // 0/0 is NaN. 35360 35361 // Sampling some points, max 5. 35362 var dataLen = data.count(); 35363 var step = Math.max(1, Math.round(dataLen / 5)); 35364 for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) { 35365 if (SymbolClz$1.getSymbolSize( 35366 data, dataIndex 35367 // Only for cartesian, where `isHorizontal` exists. 35368 )[categoryAxis.isHorizontal() ? 1 : 0] 35369 // Empirical number 35370 * 1.5 > availSize 35371 ) { 35372 return false; 35373 } 35374 } 35375 35376 return true; 35377 } 35378 35379 Chart.extend({ 35380 35381 type: 'line', 35382 35383 init: function () { 35384 var lineGroup = new Group(); 35385 35386 var symbolDraw = new SymbolDraw(); 35387 this.group.add(symbolDraw.group); 35388 35389 this._symbolDraw = symbolDraw; 35390 this._lineGroup = lineGroup; 35391 }, 35392 35393 render: function (seriesModel, ecModel, api) { 35394 var coordSys = seriesModel.coordinateSystem; 35395 var group = this.group; 35396 var data = seriesModel.getData(); 35397 var lineStyleModel = seriesModel.getModel('lineStyle'); 35398 var areaStyleModel = seriesModel.getModel('areaStyle'); 35399 35400 var points = data.mapArray(data.getItemLayout); 35401 35402 var isCoordSysPolar = coordSys.type === 'polar'; 35403 var prevCoordSys = this._coordSys; 35404 35405 var symbolDraw = this._symbolDraw; 35406 var polyline = this._polyline; 35407 var polygon = this._polygon; 35408 35409 var lineGroup = this._lineGroup; 35410
35411 var hasAnimation = seriesModel.get('animation'); 35412 35413 var isAreaChart = !areaStyleModel.isEmpty(); 35414 35415 var valueOrigin = areaStyleModel.get('origin'); 35416 var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin); 35417 35418 var stackedOnPoints = getStackedOnPoints(coordSys, data, dataCoordInfo); 35419 35420 var showSymbol = seriesModel.get('showSymbol'); 35421 35422 var isIgnoreFunc = showSymbol && !isCoordSysPolar 35423 && getIsIgnoreFunc(seriesModel, data, coordSys); 35424 35425 // Remove temporary symbols 35426 var oldData = this._data; 35427 oldData && oldData.eachItemGraphicEl(function (el, idx) { 35428 if (el.__temp) { 35429 group.remove(el); 35430 oldData.setItemGraphicEl(idx, null); 35431 } 35432 }); 35433 35434 // Remove previous created symbols if showSymbol changed to false 35435 if (!showSymbol) { 35436 symbolDraw.remove(); 35437 } 35438 35439 group.add(lineGroup); 35440 35441 // FIXME step not support polar 35442 var step = !isCoordSysPolar && seriesModel.get('step'); 35443 // Initialization animation or coordinate system changed 35444 if ( 35445 !(polyline && prevCoordSys.type === coordSys.type && step === this._step) 35446 ) { 35447 showSymbol && symbolDraw.updateData(data, { 35448 isIgnore: isIgnoreFunc, 35449 clipShape: createClipShape(coordSys, false, true, seriesModel) 35450 }); 35451 35452 if (step) { 35453 // TODO If stacked series is not step 35454 points = turnPointsIntoStep(points, coordSys, step); 35455 stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step); 35456 } 35457 35458 polyline = this._newPolyline(points, coordSys, hasAnimation); 35459 if (isAreaChart) { 35460 polygon = this._newPolygon( 35461 points, stackedOnPoints, 35462 coordSys, hasAnimation 35463 ); 35464 } 35465 lineGroup.setClipPath(createClipShape(coordSys, true, false, seriesModel)); 35466 } 35467 else { 35468 if (isAreaChart && !polygon) { 35469 // If areaStyle is added 35470 polygon = this._newPolygon( 35471 points, stackedOnPoints, 35472 coordSys, hasAnimation 35473 ); 35474 } 35475 else if (polygon && !isAreaChart) { 35476 // If areaStyle is removed 35477 lineGroup.remove(polygon); 35478 polygon = this._polygon = null; 35479 } 35480 35481 // Update clipPath 35482 lineGroup.setClipPath(createClipShape(coordSys, false, false, seriesModel)); 35483 35484 // Always update, or it is wrong in the case turning on legend 35485 // because points are not changed 35486 showSymbol && symbolDraw.updateData(data, { 35487 isIgnore: isIgnoreFunc, 35488 clipShape: createClipShape(coordSys, false, true, seriesModel) 35489 }); 35490 35491 // Stop symbol animation and sync with line points 35492 // FIXME performance? 35493 data.eachItemGraphicEl(function (el) { 35494 el.stopAnimation(true); 35495 }); 35496 35497 // In the case data zoom triggerred refreshing frequently 35498 // Data may not change if line has a category axis. So it should animate nothing 35499 if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) 35500 || !isPointsSame(this._points, points) 35501 ) { 35502 if (hasAnimation) { 35503 this._updateAnimation( 35504 data, stackedOnPoints, coordSys, api, step, valueOrigin 35505 ); 35506 } 35507 else { 35508 // Not do it in update with animation 35509 if (step) { 35510 // TODO If stacked series is not step 35511 points = turnPointsIntoStep(points, coordSys, step); 35512 stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step); 35513 } 35514 35515 polyline.setShape({ 35516 points: points 35517 }); 35518 polygon && polygon.setShape({ 35519 points: points, 35520 stackedOnPoints: stackedOnPoints 35521 }); 35522 } 35523 } 35524 } 35525 35526 var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color'); 35527 35528 polyline.useStyle(defaults( 35529 // Use color in lineStyle first 35530 lineStyleModel.getLineStyle(), 35531 { 35532 fill: 'none', 35533 stroke: visualColor, 35534 lineJoin: 'bevel' 35535 } 35536 )); 35537 35538 var smooth = seriesModel.get('smooth');
35539 smooth = getSmooth(seriesModel.get('smooth')); 35540 polyline.setShape({ 35541 smooth: smooth, 35542 smoothMonotone: seriesModel.get('smoothMonotone'), 35543 connectNulls: seriesModel.get('connectNulls') 35544 }); 35545 35546 if (polygon) { 35547 var stackedOnSeries = data.getCalculationInfo('stackedOnSeries'); 35548 var stackedOnSmooth = 0; 35549 35550 polygon.useStyle(defaults( 35551 areaStyleModel.getAreaStyle(), 35552 { 35553 fill: visualColor, 35554 opacity: 0.7, 35555 lineJoin: 'bevel' 35556 } 35557 )); 35558 35559 if (stackedOnSeries) { 35560 stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth')); 35561 } 35562 35563 polygon.setShape({ 35564 smooth: smooth, 35565 stackedOnSmooth: stackedOnSmooth, 35566 smoothMonotone: seriesModel.get('smoothMonotone'), 35567 connectNulls: seriesModel.get('connectNulls') 35568 }); 35569 } 35570 35571 this._data = data; 35572 // Save the coordinate system for transition animation when data changed 35573 this._coordSys = coordSys; 35574 this._stackedOnPoints = stackedOnPoints; 35575 this._points = points; 35576 this._step = step; 35577 this._valueOrigin = valueOrigin; 35578 }, 35579 35580 dispose: function () {}, 35581 35582 highlight: function (seriesModel, ecModel, api, payload) { 35583 var data = seriesModel.getData(); 35584 var dataIndex = queryDataIndex(data, payload); 35585 35586 if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) { 35587 var symbol = data.getItemGraphicEl(dataIndex); 35588 if (!symbol) { 35589 // Create a temporary symbol if it is not exists 35590 var pt = data.getItemLayout(dataIndex); 35591 if (!pt) { 35592 // Null data 35593 return; 35594 } 35595 symbol = new SymbolClz$1(data, dataIndex); 35596 symbol.position = pt; 35597 symbol.setZ( 35598 seriesModel.get('zlevel'), 35599 seriesModel.get('z') 35600 ); 35601 symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]); 35602 symbol.__temp = true; 35603 data.setItemGraphicEl(dataIndex, symbol); 35604 35605 // Stop scale animation 35606 symbol.stopSymbolAnimation(true); 35607 35608 this.group.add(symbol); 35609 } 35610 symbol.highlight(); 35611 } 35612 else { 35613 // Highlight whole series 35614 Chart.prototype.highlight.call( 35615 this, seriesModel, ecModel, api, payload 35616 ); 35617 } 35618 }, 35619 35620 downplay: function (seriesModel, ecModel, api, payload) { 35621 var data = seriesModel.getData(); 35622 var dataIndex = queryDataIndex(data, payload); 35623 if (dataIndex != null && dataIndex >= 0) { 35624 var symbol = data.getItemGraphicEl(dataIndex); 35625 if (symbol) { 35626 if (symbol.__temp) { 35627 data.setItemGraphicEl(dataIndex, null); 35628 this.group.remove(symbol); 35629 } 35630 else { 35631 symbol.downplay(); 35632 } 35633 } 35634 } 35635 else { 35636 // FIXME 35637 // can not downplay completely. 35638 // Downplay whole series 35639 Chart.prototype.downplay.call( 35640 this, seriesModel, ecModel, api, payload 35641 ); 35642 } 35643 }, 35644 35645 /** 35646 * @param {module:zrender/container/Group} group 35647 * @param {Array.<Array.<number>>} points 35648 * @private 35649 */ 35650 _newPolyline: function (points) { 35651 var polyline = this._polyline; 35652 // Remove previous created polyline 35653 if (polyline) { 35654 this._lineGroup.remove(polyline); 35655 } 35656 35657 polyline = new Polyline$1({ 35658 shape: { 35659 points: points 35660 }, 35661 silent: true, 35662 z2: 10 35663 }); 35664 35665 this._lineGroup.add(polyline); 35666 35667 this._polyline = polyline; 35668 35669 return polyline; 35670 }, 35671 35672 /** 35673 * @param {module:zrender/container/Group} group 35674 * @param {Array.<Array.<number>>} stackedOnPoints 35675 * @param {Array.<Array.<number>>} points 35676 * @private 35677 */ 35678 _newPolygon: function (points, stackedOnPoints) { 35679 var polygon = this._polygon; 35680 // Remove previous created polygon 35681 if (polygon) { 35682 this._lineGroup.remove(polygon); 35683 } 35684 35685 polygon = new Polygon$1({ 35686 shape: { 35687 points: points, 35688 stackedOnPoints: stackedOnPoints 35689 }, 35690 silent: true 35691 }); 35692 35693 this._lineGroup.add(polygon); 35694 35695 this._polygon = polygon; 35696 return polygon; 35697 }, 35698 35699 /** 35700 * @private 35701 */ 35702 // FIXME Two value axis 35703 _updateAnimation: function (data, stackedOnPoints, coordSys, api, step, valueOrigin) { 35704 var polyline = this._polyline; 35705 var polygon = this._polygon; 35706 var seriesModel = data.hostModel; 35707 35708 var diff = lineAnimationDiff( 35709 this._data, data, 35710 this._stackedOnPoints, stackedOnPoints, 35711 this._coordSys, coordSys, 35712 this._valueOrigin, valueOrigin 35713 ); 35714 35715 var current = diff.current; 35716 var stackedOnCurrent = diff.stackedOnCurrent; 35717 var next = diff.next; 35718 var stackedOnNext = diff.stackedOnNext; 35719 if (step) { 35720 // TODO If stacked series is not step 35721 current = turnPointsIntoStep(diff.current, coordSys, step); 35722 stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step); 35723 next = turnPointsIntoStep(diff.next, coordSys, step); 35724 stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step); 35725 } 35726 // `diff.current` is subset of `current` (which should be ensured by 35727 // turnPointsIntoStep), so points in `__points` can be updated when 35728 // points in `current` are update during animation. 35729 polyline.shape.__points = diff.current; 35730 polyline.shape.points = current; 35731 35732 updateProps(polyline, { 35733 shape: { 35734 points: next 35735 } 35736 }, seriesModel); 35737 35738 if (polygon) { 35739 polygon.setShape({ 35740 points: current, 35741 stackedOnPoints: stackedOnCurrent 35742 }); 35743 updateProps(polygon, { 35744 shape: { 35745 points: next, 35746 stackedOnPoints: stackedOnNext 35747 } 35748 }, seriesModel); 35749 } 35750 35751 var updatedDataInfo = []; 35752 var diffStatus = diff.status; 35753 35754 for (var i = 0; i < diffStatus.length; i++) { 35755 var cmd = diffStatus[i].cmd; 35756 if (cmd === '=') { 35757 var el = data.getItemGraphicEl(diffStatus[i].idx1); 35758 if (el) { 35759 updatedDataInfo.push({ 35760 el: el, 35761 ptIdx: i // Index of points 35762 }); 35763 } 35764 } 35765 } 35766
35767 if (polyline.animators && polyline.animators.length) { 35768 polyline.animators[0].during(function () { 35769 for (var i = 0; i < updatedDataInfo.length; i++) { 35770 var el = updatedDataInfo[i].el; 35771 el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]); 35772 } 35773 }); 35774 } 35775 }, 35776 35777 remove: function (ecModel) { 35778 var group = this.group; 35779 var oldData = this._data; 35780 this._lineGroup.removeAll(); 35781 this._symbolDraw.remove(true); 35782 // Remove temporary created elements when highlighting 35783 oldData && oldData.eachItemGraphicEl(function (el, idx) { 35784 if (el.__temp) { 35785 group.remove(el); 35786 oldData.setItemGraphicEl(idx, null); 35787 } 35788 }); 35789 35790 this._polyline = 35791 this._polygon = 35792 this._coordSys = 35793 this._points = 35794 this._stackedOnPoints = 35795 this._data = null; 35796 } 35797 }); 35798 35799 /* 35800* Licensed to the Apache Software Foundation (ASF) under one 35801* or more contributor license agreements. See the NOTICE file 35802* distributed with this work for additional information 35803* regarding copyright ownership. The ASF licenses this file 35804* to you under the Apache License, Version 2.0 (the 35805* "License"); you may not use this file except in compliance 35806* with the License. You may obtain a copy of the License at 35807* 35808* http://www.apache.org/licenses/LICENSE-2.0 35809* 35810* Unless required by applicable law or agreed to in writing, 35811* software distributed under the License is distributed on an 35812* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 35813* KIND, either express or implied. See the License for the 35814* specific language governing permissions and limitations 35815* under the License. 35816*/ 35817 35818 35819 var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol) { 35820 // Encoding visual for all series include which is filtered for legend drawing 35821 return { 35822 seriesType: seriesType, 35823 35824 // For legend. 35825 performRawSeries: true, 35826 35827 reset: function (seriesModel, ecModel, api) { 35828 var data = seriesModel.getData(); 35829 35830 var symbolType = seriesModel.get('symbol') || defaultSymbolType; 35831 var symbolSize = seriesModel.get('symbolSize'); 35832 var keepAspect = seriesModel.get('symbolKeepAspect'); 35833 35834 data.setVisual({ 35835 legendSymbol: legendSymbol || symbolType, 35836 symbol: symbolType, 35837 symbolSize: symbolSize, 35838 symbolKeepAspect: keepAspect 35839 }); 35840 35841 // Only visible series has each data be visual encoded 35842 if (ecModel.isSeriesFiltered(seriesModel)) { 35843 return; 35844 } 35845 35846 var hasCallback = typeof symbolSize === 'function'; 35847 35848 function dataEach(data, idx) { 35849 if (typeof symbolSize === 'function') { 35850 var rawValue = seriesModel.getRawValue(idx); 35851 // FIXME 35852 var params = seriesModel.getDataParams(idx); 35853 data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params)); 35854 } 35855 35856 if (data.hasItemOption) { 35857 var itemModel = data.getItemModel(idx); 35858 var itemSymbolType = itemModel.getShallow('symbol', true); 35859 var itemSymbolSize = itemModel.getShallow('symbolSize', 35860 true); 35861 var itemSymbolKeepAspect = 35862 itemModel.getShallow('symbolKeepAspect',true); 35863 35864 // If has item symbol 35865 if (itemSymbolType != null) { 35866 data.setItemVisual(idx, 'symbol', itemSymbolType); 35867 } 35868 if (itemSymbolSize != null) { 35869 // PENDING Transform symbolSize ? 35870 data.setItemVisual(idx, 'symbolSize', itemSymbolSize); 35871 } 35872 if (itemSymbolKeepAspect != null) { 35873 data.setItemVisual(idx, 'symbolKeepAspect', 35874 itemSymbolKeepAspect); 35875 } 35876 } 35877 } 35878 35879 return { dataEach: (data.hasItemOption || hasCallback) ? dataEach : null }; 35880 } 35881 }; 35882 }; 35883 35884 /* 35885* Licensed to the Apache Software Foundation (ASF) under one 35886* or more contributor license agreements. See the NOTICE file 35887* distributed with this work for additional information 35888* regarding copyright ownership. The ASF licenses this file 35889* to you under the Apache License, Version 2.0 (the 35890* "License");
35890 you may not use this file except in compliance 35891* with the License. You may obtain a copy of the License at 35892* 35893* http://www.apache.org/licenses/LICENSE-2.0 35894* 35895* Unless required by applicable law or agreed to in writing, 35896* software distributed under the License is distributed on an 35897* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 35898* KIND, either express or implied. See the License for the 35899* specific language governing permissions and limitations 35900* under the License. 35901*/ 35902 35903 var pointsLayout = function (seriesType) { 35904 return { 35905 seriesType: seriesType, 35906 35907 plan: createRenderPlanner(), 35908 35909 reset: function (seriesModel) { 35910 var data = seriesModel.getData(); 35911 var coordSys = seriesModel.coordinateSystem; 35912 var pipelineContext = seriesModel.pipelineContext; 35913 var isLargeRender = pipelineContext.large; 35914 35915 if (!coordSys) { 35916 return; 35917 } 35918 35919 var dims = map(coordSys.dimensions, function (dim) { 35920 return data.mapDimension(dim); 35921 }).slice(0, 2); 35922 var dimLen = dims.length; 35923 35924 var stackResultDim = data.getCalculationInfo('stackResultDimension'); 35925 if (isDimensionStacked(data, dims[0] /*, dims[1]*/)) { 35926 dims[0] = stackResultDim; 35927 } 35928 if (isDimensionStacked(data, dims[1] /*, dims[0]*/)) { 35929 dims[1] = stackResultDim; 35930 } 35931 35932 function progress(params, data) { 35933 var segCount = params.end - params.start; 35934 var points = isLargeRender && new Float32Array(segCount * dimLen); 35935 35936 for (var i = params.start, offset = 0, tmpIn = [], tmpOut = []; i < params.end; i++) { 35937 var point; 35938 35939 if (dimLen === 1) { 35940 var x = data.get(dims[0], i); 35941 point = !isNaN(x) && coordSys.dataToPoint(x, null, tmpOut); 35942 } 35943 else { 35944 var x = tmpIn[0] = data.get(dims[0], i); 35945 var y = tmpIn[1] = data.get(dims[1], i); 35946 // Also {Array.<number>}, not undefined to avoid if...else... statement 35947 point = !isNaN(x) && !isNaN(y) && coordSys.dataToPoint(tmpIn, null, tmpOut); 35948 } 35949 35950 if (isLargeRender) { 35951 points[offset++] = point ? point[0] : NaN; 35952 points[offset++] = point ? point[1] : NaN; 35953 } 35954 else { 35955 data.setItemLayout(i, (point && point.slice()) || [NaN, NaN]); 35956 } 35957 } 35958 35959 isLargeRender && data.setLayout('symbolPoints', points); 35960 } 35961 35962 return dimLen && {progress: progress}; 35963 } 35964 }; 35965 }; 35966 35967 /* 35968* Licensed to the Apache Software Foundation (ASF) under one 35969* or more contributor license agreements. See the NOTICE file 35970* distributed with this work for additional information 35971* regarding copyright ownership. The ASF licenses this file 35972* to you under the Apache License, Version 2.0 (the 35973* "License"); you may not use this file except in compliance 35974* with the License. You may obtain a copy of the License at 35975* 35976* http://www.apache.org/licenses/LICENSE-2.0 35977* 35978* Unless required by applicable law or agreed to in writing, 35979* software distributed under the License is distributed on an 35980* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 35981* KIND, either express or implied. See the License for the 35982* specific language governing permissions and limitations 35983* under the License. 35984*/ 35985 35986 35987 var samplers = { 35988 average: function (frame) { 35989 var sum = 0; 35990 var count = 0; 35991 for (var i = 0; i < frame.length; i++) { 35992 if (!isNaN(frame[i])) { 35993 sum += frame[i]; 35994 count++; 35995 } 35996 } 35997 // Return NaN if count is 0 35998 return count === 0 ? NaN : sum / count; 35999 }, 36000 sum: function (frame) { 36001 var sum = 0; 36002 for (var i = 0; i < frame.length; i++) { 36003 // Ignore NaN 36004 sum += frame[i] || 0; 36005 } 36006 return sum; 36007 }, 36008 max: function (frame) { 36009 var max = -Infinity; 36010 for (var i = 0; i < frame.length; i++) { 36011 frame[i] > max && (max = frame[i]); 36012 } 36013 // NaN will cause illegal axis extent.
36014 return isFinite(max) ? max : NaN; 36015 }, 36016 min: function (frame) { 36017 var min = Infinity; 36018 for (var i = 0; i < frame.length; i++) { 36019 frame[i] < min && (min = frame[i]); 36020 } 36021 // NaN will cause illegal axis extent. 36022 return isFinite(min) ? min : NaN; 36023 }, 36024 // TODO 36025 // Median 36026 nearest: function (frame) { 36027 return frame[0]; 36028 } 36029 }; 36030 36031 var indexSampler = function (frame, value) { 36032 return Math.round(frame.length / 2); 36033 }; 36034 36035 var dataSample = function (seriesType) { 36036 return { 36037 36038 seriesType: seriesType, 36039 36040 modifyOutputEnd: true, 36041 36042 reset: function (seriesModel, ecModel, api) { 36043 var data = seriesModel.getData(); 36044 var sampling = seriesModel.get('sampling'); 36045 var coordSys = seriesModel.coordinateSystem; 36046 // Only cartesian2d support down sampling 36047 if (coordSys.type === 'cartesian2d' && sampling) { 36048 var baseAxis = coordSys.getBaseAxis(); 36049 var valueAxis = coordSys.getOtherAxis(baseAxis); 36050 var extent = baseAxis.getExtent(); 36051 // Coordinste system has been resized 36052 var size = extent[1] - extent[0]; 36053 var rate = Math.round(data.count() / size); 36054 if (rate > 1) { 36055 var sampler; 36056 if (typeof sampling === 'string') { 36057 sampler = samplers[sampling]; 36058 } 36059 else if (typeof sampling === 'function') { 36060 sampler = sampling; 36061 } 36062 if (sampler) { 36063 // Only support sample the first dim mapped from value axis. 36064 seriesModel.setData(data.downSample( 36065 data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler 36066 )); 36067 } 36068 } 36069 } 36070 } 36071 }; 36072 }; 36073 36074 /* 36075* Licensed to the Apache Software Foundation (ASF) under one 36076* or more contributor license agreements. See the NOTICE file 36077* distributed with this work for additional information 36078* regarding copyright ownership. The ASF licenses this file 36079* to you under the Apache License, Version 2.0 (the 36080* "License"); you may not use this file except in compliance 36081* with the License. You may obtain a copy of the License at 36082* 36083* http://www.apache.org/licenses/LICENSE-2.0 36084* 36085* Unless required by applicable law or agreed to in writing, 36086* software distributed under the License is distributed on an 36087* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36088* KIND, either express or implied. See the License for the 36089* specific language governing permissions and limitations 36090* under the License. 36091*/ 36092 36093 /** 36094 * Cartesian coordinate system 36095 * @module echarts/coord/Cartesian 36096 * 36097 */ 36098 36099 function dimAxisMapper(dim) { 36100 return this._axes[dim]; 36101 } 36102 36103 /** 36104 * @alias module:echarts/coord/Cartesian 36105 * @constructor 36106 */ 36107 var Cartesian = function (name) { 36108 this._axes = {}; 36109 36110 this._dimList = []; 36111 36112 /** 36113 * @type {string} 36114 */ 36115 this.name = name || ''; 36116 }; 36117 36118 Cartesian.prototype = { 36119 36120 constructor: Cartesian, 36121 36122 type: 'cartesian', 36123 36124 /** 36125 * Get axis 36126 * @param {number|string} dim 36127 * @return {module:echarts/coord/Cartesian~Axis} 36128 */ 36129 getAxis: function (dim) { 36130 return this._axes[dim]; 36131 }, 36132 36133 /** 36134 * Get axes list 36135 * @return {Array.<module:echarts/coord/Cartesian~Axis>} 36136 */ 36137 getAxes: function () { 36138 return map(this._dimList, dimAxisMapper, this); 36139 }, 36140 36141 /** 36142 * Get axes list by given scale type 36143 */ 36144 getAxesByScale: function (scaleType) { 36145 scaleType = scaleType.toLowerCase(); 36146 return filter( 36147 this.getAxes(), 36148 function (axis) { 36149 return axis.scale.type === scaleType; 36150 } 36151 ); 36152 }, 36153 36154 /** 36155 * Add axis 36156 * @param {module:echarts/coord/Cartesian.Axis} 36157 */ 36158 addAxis: function (axis) { 36159 var dim = axis.dim; 36160 36161 this._axes[dim] = axis; 36162 36163 this._dimList.push(dim); 36164 }, 36165 36166 /** 36167 * Convert data to coord in nd space 36168 * @param {Array.<number>|Object.<string, number>} val 36169 * @return {Array.<number>|Object.<string, number>} 36170 */ 36171 dataToCoord: function (val) { 36172 return this._dataCoordConvert(val, 'dataToCoord'); 36173 }, 36174 36175 /**
36176 * Convert coord in nd space to data 36177 * @param {Array.<number>|Object.<string, number>} val 36178 * @return {Array.<number>|Object.<string, number>} 36179 */ 36180 coordToData: function (val) { 36181 return this._dataCoordConvert(val, 'coordToData'); 36182 }, 36183 36184 _dataCoordConvert: function (input, method) { 36185 var dimList = this._dimList; 36186 36187 var output = input instanceof Array ? [] : {}; 36188 36189 for (var i = 0; i < dimList.length; i++) { 36190 var dim = dimList[i]; 36191 var axis = this._axes[dim]; 36192 36193 output[dim] = axis[method](input[dim]); 36194 } 36195 36196 return output; 36197 } 36198 }; 36199 36200 /* 36201* Licensed to the Apache Software Foundation (ASF) under one 36202* or more contributor license agreements. See the NOTICE file 36203* distributed with this work for additional information 36204* regarding copyright ownership. The ASF licenses this file 36205* to you under the Apache License, Version 2.0 (the 36206* "License"); you may not use this file except in compliance 36207* with the License. You may obtain a copy of the License at 36208* 36209* http://www.apache.org/licenses/LICENSE-2.0 36210* 36211* Unless required by applicable law or agreed to in writing, 36212* software distributed under the License is distributed on an 36213* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36214* KIND, either express or implied. See the License for the 36215* specific language governing permissions and limitations 36216* under the License. 36217*/ 36218 36219 36220 function Cartesian2D(name) { 36221 36222 Cartesian.call(this, name); 36223 } 36224 36225 Cartesian2D.prototype = { 36226 36227 constructor: Cartesian2D, 36228 36229 type: 'cartesian2d', 36230 36231 /** 36232 * @type {Array.<string>} 36233 * @readOnly 36234 */ 36235 dimensions: ['x', 'y'], 36236 36237 /** 36238 * Base axis will be used on stacking. 36239 * 36240 * @return {module:echarts/coord/cartesian/Axis2D} 36241 */ 36242 getBaseAxis: function () { 36243 return this.getAxesByScale('ordinal')[0] 36244 || this.getAxesByScale('time')[0] 36245 || this.getAxis('x'); 36246 }, 36247 36248 /** 36249 * If contain point 36250 * @param {Array.<number>} point 36251 * @return {boolean} 36252 */ 36253 containPoint: function (point) { 36254 var axisX = this.getAxis('x'); 36255 var axisY = this.getAxis('y'); 36256 return axisX.contain(axisX.toLocalCoord(point[0])) 36257 && axisY.contain(axisY.toLocalCoord(point[1])); 36258 }, 36259 36260 /** 36261 * If contain data 36262 * @param {Array.<number>} data 36263 * @return {boolean} 36264 */ 36265 containData: function (data) { 36266 return this.getAxis('x').containData(data[0]) 36267 && this.getAxis('y').containData(data[1]); 36268 }, 36269 36270 /** 36271 * @param {Array.<number>} data 36272 * @param {Array.<number>} out 36273 * @return {Array.<number>} 36274 */ 36275 dataToPoint: function (data, reserved, out) { 36276 var xAxis = this.getAxis('x'); 36277 var yAxis = this.getAxis('y'); 36278 out = out || []; 36279 out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(data[0])); 36280 out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(data[1])); 36281 return out; 36282 }, 36283 36284 /** 36285 * @param {Array.<number>} data 36286 * @param {Array.<number>} out 36287 * @return {Array.<number>} 36288 */ 36289 clampData: function (data, out) { 36290 var xScale = this.getAxis('x').scale; 36291 var yScale = this.getAxis('y').scale; 36292 var xAxisExtent = xScale.getExtent(); 36293 var yAxisExtent = yScale.getExtent(); 36294 var x = xScale.parse(data[0]); 36295 var y = yScale.parse(data[1]); 36296 out = out || []; 36297 out[0] = Math.min( 36298 Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), 36299 Math.max(xAxisExtent[0], xAxisExtent[1]) 36300 ); 36301 out[1] = Math.min( 36302 Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), 36303 Math.max(yAxisExtent[0], yAxisExtent[1]) 36304 ); 36305 36306 return out; 36307 }, 36308 36309 /** 36310 * @param {Array.<number>} point 36311 * @param {Array.<number>} out 36312 * @return {Array.<number>} 36313 */ 36314 pointToData: function (point, out) { 36315 var xAxis = this.getAxis('x'); 36316 var yAxis = this.getAxis('y'); 36317 out = out || []; 36318 out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0])); 36319 out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1])); 36320 return out; 36321 }, 36322 36323 /** 36324 * Get other axis 36325 * @param {module:echarts/coord/cartesian/Axis2D} axis 36326 */ 36327 getOtherAxis: function (axis) { 36328 return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); 36329 } 36330 36331 }; 36332 36333 inherits(Cartesian2D, Cartesian); 36334 36335 /* 36336* Licensed to the Apache Software Foundation (ASF) under one 36337* or more contributor license agreements. See the NOTICE file 36338* distributed with this work for additional information 36339* regarding copyright ownership. The ASF licenses this file 36340* to you under the Apache License, Version 2.0 (the 36341* "License");
36341 you may not use this file except in compliance 36342* with the License. You may obtain a copy of the License at 36343* 36344* http://www.apache.org/licenses/LICENSE-2.0 36345* 36346* Unless required by applicable law or agreed to in writing, 36347* software distributed under the License is distributed on an 36348* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36349* KIND, either express or implied. See the License for the 36350* specific language governing permissions and limitations 36351* under the License. 36352*/ 36353 36354 /** 36355 * Extend axis 2d 36356 * @constructor module:echarts/coord/cartesian/Axis2D 36357 * @extends {module:echarts/coord/cartesian/Axis} 36358 * @param {string} dim 36359 * @param {*} scale 36360 * @param {Array.<number>} coordExtent 36361 * @param {string} axisType 36362 * @param {string} position 36363 */ 36364 var Axis2D = function (dim, scale, coordExtent, axisType, position) { 36365 Axis.call(this, dim, scale, coordExtent); 36366 /** 36367 * Axis type 36368 * - 'category' 36369 * - 'value' 36370 * - 'time' 36371 * - 'log' 36372 * @type {string} 36373 */ 36374 this.type = axisType || 'value'; 36375 36376 /** 36377 * Axis position 36378 * - 'top' 36379 * - 'bottom' 36380 * - 'left' 36381 * - 'right' 36382 */ 36383 this.position = position || 'bottom'; 36384 }; 36385 36386 Axis2D.prototype = { 36387 36388 constructor: Axis2D, 36389 36390 /** 36391 * Index of axis, can be used as key 36392 */ 36393 index: 0, 36394 36395 /** 36396 * Implemented in <module:echarts/coord/cartesian/Grid>. 36397 * @return {Array.<module:echarts/coord/cartesian/Axis2D>} 36398 * If not on zero of other axis, return null/undefined. 36399 * If no axes, return an empty array. 36400 */ 36401 getAxesOnZeroOf: null, 36402 36403 /** 36404 * Axis model 36405 * @param {module:echarts/coord/cartesian/AxisModel} 36406 */ 36407 model: null, 36408 36409 isHorizontal: function () { 36410 var position = this.position; 36411 return position === 'top' || position === 'bottom'; 36412 }, 36413 36414 /** 36415 * Each item cooresponds to this.getExtent(), which 36416 * means globalExtent[0] may greater than globalExtent[1], 36417 * unless `asc` is input. 36418 * 36419 * @param {boolean} [asc] 36420 * @return {Array.<number>} 36421 */ 36422 getGlobalExtent: function (asc) { 36423 var ret = this.getExtent(); 36424 ret[0] = this.toGlobalCoord(ret[0]); 36425 ret[1] = this.toGlobalCoord(ret[1]); 36426 asc && ret[0] > ret[1] && ret.reverse(); 36427 return ret; 36428 }, 36429 36430 getOtherAxis: function () { 36431 this.grid.getOtherAxis(); 36432 }, 36433 36434 /** 36435 * @override 36436 */ 36437 pointToData: function (point, clamp) { 36438 return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp); 36439 }, 36440 36441 /** 36442 * Transform global coord to local coord, 36443 * i.e. var localCoord = axis.toLocalCoord(80); 36444 * designate by module:echarts/coord/cartesian/Grid. 36445 * @type {Function} 36446 */ 36447 toLocalCoord: null, 36448 36449 /** 36450 * Transform global coord to local coord, 36451 * i.e. var globalCoord = axis.toLocalCoord(40); 36452 * designate by module:echarts/coord/cartesian/Grid. 36453 * @type {Function} 36454 */ 36455 toGlobalCoord: null 36456 36457 }; 36458 36459 inherits(Axis2D, Axis); 36460 36461 /* 36462* Licensed to the Apache Software Foundation (ASF) under one 36463* or more contributor license agreements. See the NOTICE file 36464* distributed with this work for additional information 36465* regarding copyright ownership. The ASF licenses this file 36466* to you under the Apache License, Version 2.0 (the 36467* "License"); you may not use this file except in compliance 36468* with the License. You may obtain a copy of the License at 36469* 36470* http://www.apache.org/licenses/LICENSE-2.0 36471* 36472* Unless required by applicable law or agreed to in writing, 36473* software distributed under the License is distributed on an 36474* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36475* KIND, either express or implied. See the License for the 36476* specific language governing permissions and limitations 36477* under the License. 36478*/ 36479 36480 var defaultOption = { 36481 show: true, 36482 zlevel: 0, 36483 z: 0, 36484 // Inverse the axis. 36485 inverse: false, 36486 36487 // Axis name displayed. 36488 name: '', 36489 // 'start' | 'middle' | 'end' 36490 nameLocation: 'end', 36491 // By degree. By defualt auto rotate by nameLocation. 36492 nameRotate: null, 36493 nameTruncate: { 36494 maxWidth: null, 36495 ellipsis: '...', 36496 placeholder: '.' 36497 }, 36498 // Use global text style by default. 36499 nameTextStyle: {}, 36500 // The gap between axisName and axisLine. 36501 nameGap: 15, 36502
36503 // Default `false` to support tooltip. 36504 silent: false, 36505 // Default `false` to avoid legacy user event listener fail. 36506 triggerEvent: false, 36507 36508 tooltip: { 36509 show: false 36510 }, 36511 36512 axisPointer: {}, 36513 36514 axisLine: { 36515 show: true, 36516 onZero: true, 36517 onZeroAxisIndex: null, 36518 lineStyle: { 36519 color: '#333', 36520 width: 1, 36521 type: 'solid' 36522 }, 36523 // The arrow at both ends the the axis. 36524 symbol: ['none', 'none'], 36525 symbolSize: [10, 15] 36526 }, 36527 axisTick: { 36528 show: true, 36529 // Whether axisTick is inside the grid or outside the grid. 36530 inside: false, 36531 // The length of axisTick. 36532 length: 5, 36533 lineStyle: { 36534 width: 1 36535 } 36536 }, 36537 axisLabel: { 36538 show: true, 36539 // Whether axisLabel is inside the grid or outside the grid. 36540 inside: false, 36541 rotate: 0, 36542 // true | false | null/undefined (auto) 36543 showMinLabel: null, 36544 // true | false | null/undefined (auto) 36545 showMaxLabel: null, 36546 margin: 8, 36547 // formatter: null, 36548 fontSize: 12 36549 }, 36550 splitLine: { 36551 show: true, 36552 lineStyle: { 36553 color: ['#ccc'], 36554 width: 1, 36555 type: 'solid' 36556 } 36557 }, 36558 splitArea: { 36559 show: false, 36560 areaStyle: { 36561 color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)'] 36562 } 36563 } 36564 }; 36565 36566 var axisDefault = {}; 36567 36568 axisDefault.categoryAxis = merge({ 36569 // The gap at both ends of the axis. For categoryAxis, boolean. 36570 boundaryGap: true, 36571 // Set false to faster category collection. 36572 // Only usefull in the case like: category is 36573 // ['2012-01-01', '2012-01-02', ...], where the input 36574 // data has been ensured not duplicate and is large data. 36575 // null means "auto": 36576 // if axis.data provided, do not deduplication, 36577 // else do deduplication. 36578 deduplication: null, 36579 // splitArea: { 36580 // show: false 36581 // }, 36582 splitLine: { 36583 show: false 36584 }, 36585 axisTick: { 36586 // If tick is align with label when boundaryGap is true 36587 alignWithLabel: false, 36588 interval: 'auto' 36589 }, 36590 axisLabel: { 36591 interval: 'auto' 36592 } 36593 }, defaultOption); 36594 36595 axisDefault.valueAxis = merge({ 36596 // The gap at both ends of the axis. For value axis, [GAP, GAP], where 36597 // `GAP` can be an absolute pixel number (like `35`), or percent (like `'30%'`) 36598 boundaryGap: [0, 0], 36599 36600 // TODO 36601 // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60] 36602 36603 // Min value of the axis. can be: 36604 // + a number 36605 // + 'dataMin': use the min value in data. 36606 // + null/undefined: auto decide min value (consider pretty look and boundaryGap). 36607 // min: null, 36608 36609 // Max value of the axis. can be: 36610 // + a number 36611 // + 'dataMax': use the max value in data. 36612 // + null/undefined: auto decide max value (consider pretty look and boundaryGap). 36613 // max: null, 36614 36615 // Readonly prop, specifies start value of the range when using data zoom. 36616 // rangeStart: null 36617 36618 // Readonly prop, specifies end value of the range when using data zoom. 36619 // rangeEnd: null 36620 36621 // Optional value can be: 36622 // + `false`: always include value 0. 36623 // + `true`: the extent do not consider value 0. 36624 // scale: false, 36625 36626 // AxisTick and axisLabel and splitLine are caculated based on splitNumber. 36627 splitNumber: 5 36628 36629 // Interval specifies the span of the ticks is mandatorily. 36630 // interval: null 36631 36632 // Specify min interval when auto calculate tick interval. 36633 // minInterval: null 36634 36635 // Specify max interval when auto calculate tick interval. 36636 // maxInterval: null 36637 36638 }, defaultOption); 36639
36640 axisDefault.timeAxis = defaults({ 36641 scale: true, 36642 min: 'dataMin', 36643 max: 'dataMax' 36644 }, axisDefault.valueAxis); 36645 36646 axisDefault.logAxis = defaults({ 36647 scale: true, 36648 logBase: 10 36649 }, axisDefault.valueAxis); 36650 36651 /* 36652* Licensed to the Apache Software Foundation (ASF) under one 36653* or more contributor license agreements. See the NOTICE file 36654* distributed with this work for additional information 36655* regarding copyright ownership. The ASF licenses this file 36656* to you under the Apache License, Version 2.0 (the 36657* "License"); you may not use this file except in compliance 36658* with the License. You may obtain a copy of the License at 36659* 36660* http://www.apache.org/licenses/LICENSE-2.0 36661* 36662* Unless required by applicable law or agreed to in writing, 36663* software distributed under the License is distributed on an 36664* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36665* KIND, either express or implied. See the License for the 36666* specific language governing permissions and limitations 36667* under the License. 36668*/ 36669 36670// FIXME axisType is fixed ? 36671 var AXIS_TYPES = ['value', 'category', 'time', 'log']; 36672 36673 /** 36674 * Generate sub axis model class 36675 * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel' 36676 * @param {module:echarts/model/Component} BaseAxisModelClass 36677 * @param {Function} axisTypeDefaulter 36678 * @param {Object} [extraDefaultOption] 36679 */ 36680 var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) { 36681 36682 each$1(AXIS_TYPES, function (axisType) { 36683 36684 BaseAxisModelClass.extend({ 36685 36686 /** 36687 * @readOnly 36688 */ 36689 type: axisName + 'Axis.' + axisType, 36690 36691 mergeDefaultAndTheme: function (option, ecModel) { 36692 var layoutMode = this.layoutMode; 36693 var inputPositionParams = layoutMode 36694 ? getLayoutParams(option) : {}; 36695 36696 var themeModel = ecModel.getTheme(); 36697 merge(option, themeModel.get(axisType + 'Axis')); 36698 merge(option, this.getDefaultOption()); 36699 36700 option.type = axisTypeDefaulter(axisName, option); 36701 36702 if (layoutMode) { 36703 mergeLayoutParam(option, inputPositionParams, layoutMode); 36704 } 36705 }, 36706 36707 /** 36708 * @override 36709 */ 36710 optionUpdated: function () { 36711 var thisOption = this.option; 36712 if (thisOption.type === 'category') { 36713 this.__ordinalMeta = OrdinalMeta.createByAxisModel(this); 36714 } 36715 }, 36716 36717 /** 36718 * Should not be called before all of 'getInitailData' finished. 36719 * Because categories are collected during initializing data. 36720 */ 36721 getCategories: function (rawData) { 36722 var option = this.option; 36723 // FIXME 36724 // warning if called before all of 'getInitailData' finished. 36725 if (option.type === 'category') { 36726 if (rawData) { 36727 return option.data; 36728 } 36729 return this.__ordinalMeta.categories; 36730 } 36731 }, 36732 36733 getOrdinalMeta: function () { 36734 return this.__ordinalMeta; 36735 }, 36736 36737 defaultOption: mergeAll( 36738 [ 36739 {}, 36740 axisDefault[axisType + 'Axis'], 36741 extraDefaultOption 36742 ], 36743 true 36744 ) 36745 }); 36746 }); 36747 36748 ComponentModel.registerSubTypeDefaulter( 36749 axisName + 'Axis', 36750 curry(axisTypeDefaulter, axisName) 36751 ); 36752 }; 36753 36754 /* 36755* Licensed to the Apache Software Foundation (ASF) under one 36756* or more contributor license agreements. See the NOTICE file 36757* distributed with this work for additional information 36758* regarding copyright ownership. The ASF licenses this file 36759* to you under the Apache License, Version 2.0 (the 36760* "License");
36760 you may not use this file except in compliance 36761* with the License. You may obtain a copy of the License at 36762* 36763* http://www.apache.org/licenses/LICENSE-2.0 36764* 36765* Unless required by applicable law or agreed to in writing, 36766* software distributed under the License is distributed on an 36767* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36768* KIND, either express or implied. See the License for the 36769* specific language governing permissions and limitations 36770* under the License. 36771*/ 36772 36773 var AxisModel = ComponentModel.extend({ 36774 36775 type: 'cartesian2dAxis', 36776 36777 /** 36778 * @type {module:echarts/coord/cartesian/Axis2D} 36779 */ 36780 axis: null, 36781 36782 /** 36783 * @override 36784 */ 36785 init: function () { 36786 AxisModel.superApply(this, 'init', arguments); 36787 this.resetRange(); 36788 }, 36789 36790 /** 36791 * @override 36792 */ 36793 mergeOption: function () { 36794 AxisModel.superApply(this, 'mergeOption', arguments); 36795 this.resetRange(); 36796 }, 36797 36798 /** 36799 * @override 36800 */ 36801 restoreData: function () { 36802 AxisModel.superApply(this, 'restoreData', arguments); 36803 this.resetRange(); 36804 }, 36805 36806 /** 36807 * @override 36808 * @return {module:echarts/model/Component} 36809 */ 36810 getCoordSysModel: function () { 36811 return this.ecModel.queryComponents({ 36812 mainType: 'grid', 36813 index: this.option.gridIndex, 36814 id: this.option.gridId 36815 })[0]; 36816 } 36817 36818 }); 36819 36820 function getAxisType(axisDim, option) { 36821 // Default axis with data is category axis 36822 return option.type || (option.data ? 'category' : 'value'); 36823 } 36824 36825 merge(AxisModel.prototype, axisModelCommonMixin); 36826 36827 var extraOption = { 36828 // gridIndex: 0, 36829 // gridId: '', 36830 36831 // Offset is for multiple axis on the same position 36832 offset: 0 36833 }; 36834 36835 axisModelCreator('x', AxisModel, getAxisType, extraOption); 36836 axisModelCreator('y', AxisModel, getAxisType, extraOption); 36837 36838 /* 36839* Licensed to the Apache Software Foundation (ASF) under one 36840* or more contributor license agreements. See the NOTICE file 36841* distributed with this work for additional information 36842* regarding copyright ownership. The ASF licenses this file 36843* to you under the Apache License, Version 2.0 (the 36844* "License"); you may not use this file except in compliance 36845* with the License. You may obtain a copy of the License at 36846* 36847* http://www.apache.org/licenses/LICENSE-2.0 36848* 36849* Unless required by applicable law or agreed to in writing, 36850* software distributed under the License is distributed on an 36851* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36852* KIND, either express or implied. See the License for the 36853* specific language governing permissions and limitations 36854* under the License. 36855*/ 36856 36857// Grid æË¯åŨæÅâ°Ã§âºÂ´Ã¨Â§âÃ¥ÂÂæ â¡Ã§Â³Â»Ã§Å¡âæâ¶åâ¬â¢Ã¥Â¿â¦Ã©Â¡Â»Ã¨Â¦ÂÃ¥ÂËåŨçšâ 36858// æâ°â¬Ã¤Â»Â¥Ã¨Â¿â¢Ã©â¡Å也è¦Â被 Cartesian2D ä¾Âèµâ 36859 36860 ComponentModel.extend({ 36861 36862 type: 'grid', 36863 36864 dependencies: ['xAxis', 'yAxis'], 36865 36866 layoutMode: 'box', 36867 36868 /** 36869 * @type {module:echarts/coord/cartesian/Grid} 36870 */ 36871 coordinateSystem: null, 36872 36873 defaultOption: { 36874 show: false, 36875 zlevel: 0, 36876 z: 0, 36877 left: '10%', 36878 top: 60, 36879 right: '10%', 36880 bottom: 60, 36881 // If grid size contain label 36882 containLabel: false, 36883 // width: {totalWidth} - left - right, 36884 // height: {totalHeight} - top - bottom, 36885 backgroundColor: 'rgba(0,0,0,0)', 36886 borderWidth: 1, 36887 borderColor: '#ccc' 36888 } 36889 }); 36890 36891 /* 36892* Licensed to the Apache Software Foundation (ASF) under one 36893* or more contributor license agreements. See the NOTICE file 36894* distributed with this work for additional information 36895* regarding copyright ownership. The ASF licenses this file 36896* to you under the Apache License, Version 2.0 (the 36897* "License");
36897 you may not use this file except in compliance 36898* with the License. You may obtain a copy of the License at 36899* 36900* http://www.apache.org/licenses/LICENSE-2.0 36901* 36902* Unless required by applicable law or agreed to in writing, 36903* software distributed under the License is distributed on an 36904* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 36905* KIND, either express or implied. See the License for the 36906* specific language governing permissions and limitations 36907* under the License. 36908*/ 36909 36910 /** 36911 * Grid is a region which contains at most 4 cartesian systems 36912 * 36913 * TODO Default cartesian 36914 */ 36915 36916// Depends on GridModel, AxisModel, which performs preprocess. 36917 /** 36918 * Check if the axis is used in the specified grid 36919 * @inner 36920 */ 36921 function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) { 36922 return axisModel.getCoordSysModel() === gridModel; 36923 } 36924 36925 function Grid(gridModel, ecModel, api) { 36926 /** 36927 * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>} 36928 * @private 36929 */ 36930 this._coordsMap = {}; 36931 36932 /** 36933 * @type {Array.<module:echarts/coord/cartesian/Cartesian>} 36934 * @private 36935 */ 36936 this._coordsList = []; 36937 36938 /** 36939 * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>} 36940 * @private 36941 */ 36942 this._axesMap = {}; 36943 36944 /** 36945 * @type {Array.<module:echarts/coord/cartesian/Axis2D>} 36946 * @private 36947 */ 36948 this._axesList = []; 36949 36950 this._initCartesian(gridModel, ecModel, api); 36951 36952 this.model = gridModel; 36953 } 36954 36955 var gridProto = Grid.prototype; 36956 36957 gridProto.type = 'grid'; 36958 36959 gridProto.axisPointerEnabled = true; 36960 36961 gridProto.getRect = function () { 36962 return this._rect; 36963 }; 36964 36965 gridProto.update = function (ecModel, api) { 36966 36967 var axesMap = this._axesMap; 36968 36969 this._updateScale(ecModel, this.model); 36970 36971 each$1(axesMap.x, function (xAxis) { 36972 niceScaleExtent(xAxis.scale, xAxis.model); 36973 }); 36974 each$1(axesMap.y, function (yAxis) { 36975 niceScaleExtent(yAxis.scale, yAxis.model); 36976 }); 36977 each$1(axesMap.x, function (xAxis) { 36978 fixAxisOnZero(axesMap, 'y', xAxis); 36979 }); 36980 each$1(axesMap.y, function (yAxis) { 36981 fixAxisOnZero(axesMap, 'x', yAxis); 36982 }); 36983 36984 // Resize again if containLabel is enabled 36985 // FIXME It may cause getting wrong grid size in data processing stage 36986 this.resize(this.model, api); 36987 }; 36988 36989 function fixAxisOnZero(axesMap, otherAxisDim, axis) { 36990 36991 axis.getAxesOnZeroOf = function () { 36992 // TODO: onZero of multiple axes. 36993 return otherAxis ? [otherAxis] : []; 36994 }; 36995 36996 // onZero can not be enabled in these two situations: 36997 // 1. When any other axis is a category axis. 36998 // 2. When no axis is cross 0 point. 36999 var otherAxes = axesMap[otherAxisDim]; 37000 37001 var otherAxis; 37002 var axisModel = axis.model; 37003 var onZero = axisModel.get('axisLine.onZero'); 37004 var onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex'); 37005 37006 if (!onZero) { 37007 return; 37008 } 37009 37010 // If target axis is specified. 37011 if (onZeroAxisIndex != null) { 37012 if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) { 37013 otherAxis = otherAxes[onZeroAxisIndex]; 37014 } 37015 return; 37016 } 37017 37018 // Find the first available other axis. 37019 for (var idx in otherAxes) { 37020 if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx])) { 37021 otherAxis = otherAxes[idx]; 37022 break; 37023 } 37024 } 37025 } 37026 37027 function canOnZeroToAxis(axis) { 37028 return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis); 37029 } 37030 37031 /** 37032 * Resize the grid 37033 * @param {module:echarts/coord/cartesian/GridModel} gridModel 37034 * @param {module:echarts/ExtensionAPI} api 37035 */ 37036 gridProto.resize = function (gridModel, api, ignoreContainLabel) { 37037 37038 var gridRect = getLayoutRect( 37039 gridModel.getBoxLayoutParams(), { 37040 width: api.getWidth(), 37041 height: api.getHeight() 37042 }); 37043 37044 this._rect = gridRect; 37045 37046 var axesList = this._axesList; 37047 37048 adjustAxes(); 37049 37050 // Minus label size 37051 if (!ignoreContainLabel && gridModel.get('containLabel')) { 37052 each$1(axesList, function (axis) { 37053 if (!axis.model.get('axisLabel.inside')) { 37054 var labelUnionRect = estimateLabelUnionRect(axis); 37055 if (labelUnionRect) { 37056 var dim = axis.isHorizontal() ? 'height' : 'width'; 37057 var margin = axis.model.get('axisLabel.margin'); 37058 gridRect[dim] -= labelUnionRect[dim] + margin; 37059 if (axis.position === 'top') { 37060 gridRect.y += labelUnionRect.height + margin; 37061 } 37062 else if (axis.position === 'left') { 37063 gridRect.x += labelUnionRect.width + margin; 37064 } 37065 } 37066 } 37067 }); 37068 37069 adjustAxes(); 37070 } 37071 37072 function adjustAxes() { 37073 each$1(axesList, function (axis) { 37074 var isHorizontal = axis.isHorizontal(); 37075 var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height]; 37076 var idx = axis.inverse ? 1 : 0; 37077 axis.setExtent(extent[idx], extent[1 - idx]); 37078 updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y); 37079 }); 37080 } 37081 }; 37082 37083 /** 37084 * @param {string} axisType 37085 * @param {number} [axisIndex] 37086 */ 37087 gridProto.getAxis = function (axisType, axisIndex) { 37088 var axesMapOnDim = this._axesMap[axisType]; 37089 if (axesMapOnDim != null) { 37090 if (axisIndex == null) { 37091 // Find first axis 37092 for (var name in axesMapOnDim) { 37093 if (axesMapOnDim.hasOwnProperty(name)) { 37094 return axesMapOnDim[name]; 37095 } 37096 } 37097 } 37098 return axesMapOnDim[axisIndex]; 37099 } 37100 }; 37101 37102 /** 37103 * @return {Array.<module:echarts/coord/Axis>} 37104 */ 37105 gridProto.getAxes = function () { 37106 return this._axesList.slice(); 37107 }; 37108 37109 /** 37110 * Usage: 37111 * grid.getCartesian(xAxisIndex, yAxisIndex); 37112 * grid.getCartesian(xAxisIndex); 37113 * grid.getCartesian(null, yAxisIndex); 37114 * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...}); 37115 * 37116 * @param {number|Object} [xAxisIndex] 37117 * @param {number} [yAxisIndex] 37118 */ 37119 gridProto.getCartesian = function (xAxisIndex, yAxisIndex) { 37120 if (xAxisIndex != null && yAxisIndex != null) { 37121 var key = 'x' + xAxisIndex + 'y' + yAxisIndex; 37122 return this._coordsMap[key]; 37123 } 37124 37125 if (isObject$1(xAxisIndex)) { 37126 yAxisIndex = xAxisIndex.yAxisIndex; 37127 xAxisIndex = xAxisIndex.xAxisIndex; 37128 } 37129 // When only xAxisIndex or yAxisIndex given, find its first cartesian. 37130 for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) { 37131 if (coordList[i].getAxis('x').index === xAxisIndex 37132 || coordList[i].getAxis('y').index === yAxisIndex 37133 ) { 37134 return coordList[i]; 37135 } 37136 } 37137 }; 37138 37139 gridProto.getCartesians = function () { 37140 return this._coordsList.slice(); 37141 }; 37142 37143 /** 37144 * @implements 37145 * see {module:echarts/CoodinateSystem} 37146 */ 37147 gridProto.convertToPixel = function (ecModel, finder, value) { 37148 var target = this._findConvertTarget(ecModel, finder); 37149 37150 return target.cartesian 37151 ? target.cartesian.dataToPoint(value) 37152 : target.axis 37153 ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) 37154 : null; 37155 }; 37156 37157 /** 37158 * @implements 37159 * see {module:echarts/CoodinateSystem} 37160 */ 37161 gridProto.convertFromPixel = function (ecModel, finder, value) { 37162 var target = this._findConvertTarget(ecModel, finder); 37163 37164 return target.cartesian 37165 ? target.cartesian.pointToData(value) 37166 : target.axis 37167 ? target.axis.coordToData(target.axis.toLocalCoord(value)) 37168 : null; 37169 }; 37170 37171 /** 37172 * @inner 37173 */ 37174 gridProto._findConvertTarget = function (ecModel, finder) { 37175 var seriesModel = finder.seriesModel; 37176 var xAxisModel = finder.xAxisModel 37177 || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]); 37178 var yAxisModel = finder.yAxisModel 37179 || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]); 37180 var gridModel = finder.gridModel; 37181 var coordsList = this._coordsList; 37182 var cartesian; 37183 var axis; 37184 37185 if (seriesModel) { 37186 cartesian = seriesModel.coordinateSystem; 37187 indexOf(coordsList, cartesian) < 0 && (cartesian = null); 37188 } 37189 else if (xAxisModel && yAxisModel) { 37190 cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); 37191 } 37192 else if (xAxisModel) { 37193 axis = this.getAxis('x', xAxisModel.componentIndex); 37194 } 37195 else if (yAxisModel) { 37196 axis = this.getAxis('y', yAxisModel.componentIndex); 37197 } 37198 // Lowest priority. 37199 else if (gridModel) { 37200 var grid = gridModel.coordinateSystem; 37201 if (grid === this) { 37202 cartesian = this._coordsList[0]; 37203 } 37204 } 37205 37206 return {cartesian: cartesian, axis: axis}; 37207 }; 37208 37209 /** 37210 * @implements 37211 * see {module:echarts/CoodinateSystem} 37212 */ 37213 gridProto.containPoint = function (point) { 37214 var coord = this._coordsList[0]; 37215 if (coord) { 37216 return coord.containPoint(point); 37217 } 37218 }; 37219 37220 /** 37221 * Initialize cartesian coordinate systems 37222 * @private 37223 */ 37224 gridProto._initCartesian = function (gridModel, ecModel, api) { 37225 var axisPositionUsed = { 37226 left: false, 37227 right: false, 37228 top: false, 37229 bottom: false 37230 }; 37231 37232 var axesMap = { 37233 x: {}, 37234 y: {} 37235 }; 37236 var axesCount = { 37237 x: 0, 37238 y: 0 37239 }; 37240 37241 /// Create axis 37242 ecModel.eachComponent('xAxis', createAxisCreator('x'), this); 37243 ecModel.eachComponent('yAxis', createAxisCreator('y'), this); 37244 37245 if (!axesCount.x || !axesCount.y) { 37246 // Roll back when there no either x or y axis 37247 this._axesMap = {}; 37248 this._axesList = []; 37249 return; 37250 } 37251 37252 this._axesMap = axesMap; 37253 37254 /// Create cartesian2d 37255 each$1(axesMap.x, function (xAxis, xAxisIndex) { 37256 each$1(axesMap.y, function (yAxis, yAxisIndex) { 37257 var key = 'x' + xAxisIndex + 'y' + yAxisIndex; 37258 var cartesian = new Cartesian2D(key); 37259 37260 cartesian.grid = this; 37261 cartesian.model = gridModel; 37262 37263 this._coordsMap[key] = cartesian; 37264 this._coordsList.push(cartesian); 37265 37266 cartesian.addAxis(xAxis); 37267 cartesian.addAxis(yAxis); 37268 }, this); 37269 }, this); 37270 37271 function createAxisCreator(axisType) { 37272 return function (axisModel, idx) { 37273 if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) { 37274 return; 37275 } 37276 37277 var axisPosition = axisModel.get('position'); 37278 if (axisType === 'x') { 37279 // Fix position 37280 if (axisPosition !== 'top' && axisPosition !== 'bottom') { 37281 // Default bottom of X 37282 axisPosition = 'bottom'; 37283 if (axisPositionUsed[axisPosition]) { 37284 axisPosition = axisPosition === 'top' ? 'bottom' : 'top'; 37285 } 37286 } 37287 } 37288 else { 37289 // Fix position 37290 if (axisPosition !== 'left' && axisPosition !== 'right') { 37291 // Default left of Y 37292 axisPosition = 'left'; 37293 if (axisPositionUsed[axisPosition]) { 37294 axisPosition = axisPosition === 'left' ? 'right' : 'left'; 37295 } 37296 } 37297 } 37298 axisPositionUsed[axisPosition] = true; 37299 37300 var axis = new Axis2D( 37301 axisType, createScaleByModel(axisModel), 37302 [0, 0], 37303 axisModel.get('type'), 37304 axisPosition 37305 ); 37306 37307 var isCategory = axis.type === 'category'; 37308 axis.onBand = isCategory && axisModel.get('boundaryGap'); 37309 axis.inverse = axisModel.get('inverse'); 37310 37311 // Inject axis into axisModel 37312 axisModel.axis = axis; 37313 37314 // Inject axisModel into axis 37315 axis.model = axisModel; 37316 37317 // Inject grid info axis 37318 axis.grid = this; 37319 37320 // Index of axis, can be used as key 37321 axis.index = idx; 37322 37323 this._axesList.push(axis); 37324 37325 axesMap[axisType][idx] = axis; 37326 axesCount[axisType]++; 37327 }; 37328 } 37329 }; 37330 37331 /** 37332 * Update cartesian properties from series 37333 * @param {module:echarts/model/Option} option 37334 * @private 37335 */ 37336 gridProto._updateScale = function (ecModel, gridModel) { 37337 // Reset scale 37338 each$1(this._axesList, function (axis) { 37339 axis.scale.setExtent(Infinity, -Infinity); 37340 }); 37341 ecModel.eachSeries(function (seriesModel) {
37342 if (isCartesian2D(seriesModel)) { 37343 var axesModels = findAxesModels(seriesModel, ecModel); 37344 var xAxisModel = axesModels[0]; 37345 var yAxisModel = axesModels[1]; 37346 37347 if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel) 37348 || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel) 37349 ) { 37350 return; 37351 } 37352 37353 var cartesian = this.getCartesian( 37354 xAxisModel.componentIndex, yAxisModel.componentIndex 37355 ); 37356 var data = seriesModel.getData(); 37357 var xAxis = cartesian.getAxis('x'); 37358 var yAxis = cartesian.getAxis('y'); 37359 37360 if (data.type === 'list') { 37361 unionExtent(data, xAxis, seriesModel); 37362 unionExtent(data, yAxis, seriesModel); 37363 } 37364 } 37365 }, this); 37366 37367 function unionExtent(data, axis, seriesModel) { 37368 each$1(data.mapDimension(axis.dim, true), function (dim) { 37369 axis.scale.unionExtentFromData( 37370 // For example, the extent of the orginal dimension 37371 // is [0.1, 0.5], the extent of the `stackResultDimension` 37372 // is [7, 9], the final extent should not include [0.1, 0.5]. 37373 data, getStackedDimension(data, dim) 37374 ); 37375 }); 37376 } 37377 }; 37378 37379 /** 37380 * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined 37381 * @return {Object} {baseAxes: [], otherAxes: []} 37382 */ 37383 gridProto.getTooltipAxes = function (dim) { 37384 var baseAxes = []; 37385 var otherAxes = []; 37386 37387 each$1(this.getCartesians(), function (cartesian) { 37388 var baseAxis = (dim != null && dim !== 'auto') 37389 ? cartesian.getAxis(dim) : cartesian.getBaseAxis(); 37390 var otherAxis = cartesian.getOtherAxis(baseAxis); 37391 indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis); 37392 indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis); 37393 }); 37394 37395 return {baseAxes: baseAxes, otherAxes: otherAxes}; 37396 }; 37397 37398 /** 37399 * @inner 37400 */ 37401 function updateAxisTransform(axis, coordBase) { 37402 var axisExtent = axis.getExtent(); 37403 var axisExtentSum = axisExtent[0] + axisExtent[1]; 37404 37405 // Fast transform 37406 axis.toGlobalCoord = axis.dim === 'x' 37407 ? function (coord) { 37408 return coord + coordBase; 37409 } 37410 : function (coord) { 37411 return axisExtentSum - coord + coordBase; 37412 }; 37413 axis.toLocalCoord = axis.dim === 'x' 37414 ? function (coord) { 37415 return coord - coordBase; 37416 } 37417 : function (coord) { 37418 return axisExtentSum - coord + coordBase; 37419 }; 37420 } 37421 37422 var axesTypes = ['xAxis', 'yAxis']; 37423 /** 37424 * @inner 37425 */ 37426 function findAxesModels(seriesModel, ecModel) { 37427 return map(axesTypes, function (axisType) { 37428 var axisModel = seriesModel.getReferringComponents(axisType)[0]; 37429 37430 if (__DEV__) { 37431 if (!axisModel) { 37432 throw new Error(axisType + ' "' + retrieve( 37433 seriesModel.get(axisType + 'Index'), 37434 seriesModel.get(axisType + 'Id'), 37435 0 37436 ) + '" not found'); 37437 } 37438 } 37439 return axisModel; 37440 }); 37441 } 37442 37443 /** 37444 * @inner 37445 */ 37446 function isCartesian2D(seriesModel) { 37447 return seriesModel.get('coordinateSystem') === 'cartesian2d'; 37448 } 37449 37450 Grid.create = function (ecModel, api) { 37451 var grids = []; 37452 ecModel.eachComponent('grid', function (gridModel, idx) { 37453 var grid = new Grid(gridModel, ecModel, api); 37454 grid.name = 'grid_' + idx; 37455 // dataSampling requires axis extent, so resize 37456 // should be performed in create stage. 37457 grid.resize(gridModel, api, true); 37458 37459 gridModel.coordinateSystem = grid; 37460 37461 grids.push(grid); 37462 }); 37463 37464 // Inject the coordinateSystems into seriesModel 37465 ecModel.eachSeries(function (seriesModel) {
37466 if (!isCartesian2D(seriesModel)) { 37467 return; 37468 } 37469 37470 var axesModels = findAxesModels(seriesModel, ecModel); 37471 var xAxisModel = axesModels[0]; 37472 var yAxisModel = axesModels[1]; 37473 37474 var gridModel = xAxisModel.getCoordSysModel(); 37475 37476 if (__DEV__) { 37477 if (!gridModel) { 37478 throw new Error( 37479 'Grid "' + retrieve( 37480 xAxisModel.get('gridIndex'), 37481 xAxisModel.get('gridId'), 37482 0 37483 ) + '" not found' 37484 ); 37485 } 37486 if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) { 37487 throw new Error('xAxis and yAxis must use the same grid'); 37488 } 37489 } 37490 37491 var grid = gridModel.coordinateSystem; 37492 37493 seriesModel.coordinateSystem = grid.getCartesian( 37494 xAxisModel.componentIndex, yAxisModel.componentIndex 37495 ); 37496 }); 37497 37498 return grids; 37499 }; 37500 37501// For deciding which dimensions to use when creating list data 37502 Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions; 37503 37504 CoordinateSystemManager.register('cartesian2d', Grid); 37505 37506 /* 37507* Licensed to the Apache Software Foundation (ASF) under one 37508* or more contributor license agreements. See the NOTICE file 37509* distributed with this work for additional information 37510* regarding copyright ownership. The ASF licenses this file 37511* to you under the Apache License, Version 2.0 (the 37512* "License"); you may not use this file except in compliance 37513* with the License. You may obtain a copy of the License at 37514* 37515* http://www.apache.org/licenses/LICENSE-2.0 37516* 37517* Unless required by applicable law or agreed to in writing, 37518* software distributed under the License is distributed on an 37519* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 37520* KIND, either express or implied. See the License for the 37521* specific language governing permissions and limitations 37522* under the License. 37523*/ 37524 37525 var PI$2 = Math.PI; 37526 37527 function makeAxisEventDataBase(axisModel) { 37528 var eventData = { 37529 componentType: axisModel.mainType 37530 }; 37531 eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex; 37532 return eventData; 37533 } 37534 37535 /** 37536 * A final axis is translated and rotated from a "standard axis". 37537 * So opt.position and opt.rotation is required. 37538 * 37539 * A standard axis is and axis from [0, 0] to [0, axisExtent[1]], 37540 * for example: (0, 0) ------------> (0, 50) 37541 * 37542 * nameDirection or tickDirection or labelDirection is 1 means tick 37543 * or label is below the standard axis, whereas is -1 means above 37544 * the standard axis. labelOffset means offset between label and axis, 37545 * which is useful when 'onZero', where axisLabel is in the grid and 37546 * label in outside grid. 37547 * 37548 * Tips: like always, 37549 * positive rotation represents anticlockwise, and negative rotation 37550 * represents clockwise. 37551 * The direction of position coordinate is the same as the direction 37552 * of screen coordinate. 37553 * 37554 * Do not need to consider axis 'inverse', which is auto processed by 37555 * axis extent. 37556 * 37557 * @param {module:zrender/container/Group} group 37558 * @param {Object} axisModel 37559 * @param {Object} opt Standard axis parameters. 37560 * @param {Array.<number>} opt.position [x, y] 37561 * @param {number} opt.rotation by radian 37562 * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'. 37563 * @param {number} [opt.tickDirection=1] 1 or -1 37564 * @param {number} [opt.labelDirection=1] 1 or -1 37565 * @param {number} [opt.labelOffset=0] Usefull when onZero. 37566 * @param {string} [opt.axisLabelShow] default get from axisModel. 37567 * @param {string} [opt.axisName] default get from axisModel. 37568 * @param {number} [opt.axisNameAvailableWidth] 37569 * @param {number} [opt.labelRotate] by degree, default get from axisModel. 37570 * @param {number} [opt.strokeContainThreshold] Default label interval when label 37571 * @param {number} [opt.nameTruncateMaxWidth] 37572 */ 37573 var AxisBuilder = function (axisModel, opt) { 37574 37575 /** 37576 * @readOnly 37577 */ 37578 this.opt = opt; 37579 37580 /** 37581 * @readOnly 37582 */ 37583 this.axisModel = axisModel; 37584 37585 // Default value 37586 defaults( 37587 opt, 37588 { 37589 labelOffset: 0, 37590 nameDirection: 1, 37591 tickDirection: 1, 37592 labelDirection: 1, 37593 silent: true 37594 } 37595 ); 37596 37597 /** 37598 * @readOnly 37599 */ 37600 this.group = new Group(); 37601 37602 // FIXME Not use a seperate text group? 37603 var dumbGroup = new Group({ 37604 position: opt.position.slice(), 37605 rotation: opt.rotation 37606 }); 37607 37608 // this.group.add(dumbGroup); 37609 // this._dumbGroup = dumbGroup; 37610 37611 dumbGroup.updateTransform(); 37612 this._transform = dumbGroup.transform; 37613 37614 this._dumbGroup = dumbGroup; 37615 }; 37616 37617 AxisBuilder.prototype = { 37618 37619 constructor: AxisBuilder, 37620 37621 hasBuilder: function (name) { 37622 return !!builders[name]; 37623 }, 37624 37625 add: function (name) { 37626 builders[name].call(this); 37627 }, 37628 37629 getGroup: function () { 37630 return this.group; 37631 } 37632 37633 }; 37634 37635 var builders = { 37636 37637 /** 37638 * @private 37639 */
37640 axisLine: function () { 37641 var opt = this.opt; 37642 var axisModel = this.axisModel; 37643 37644 if (!axisModel.get('axisLine.show')) { 37645 return; 37646 } 37647 37648 var extent = this.axisModel.axis.getExtent(); 37649 37650 var matrix = this._transform; 37651 var pt1 = [extent[0], 0]; 37652 var pt2 = [extent[1], 0]; 37653 if (matrix) { 37654 applyTransform(pt1, pt1, matrix); 37655 applyTransform(pt2, pt2, matrix); 37656 } 37657 37658 var lineStyle = extend( 37659 { 37660 lineCap: 'round' 37661 }, 37662 axisModel.getModel('axisLine.lineStyle').getLineStyle() 37663 ); 37664 37665 this.group.add(new Line(subPixelOptimizeLine({ 37666 // Id for animation 37667 anid: 'line', 37668 37669 shape: { 37670 x1: pt1[0], 37671 y1: pt1[1], 37672 x2: pt2[0], 37673 y2: pt2[1] 37674 }, 37675 style: lineStyle, 37676 strokeContainThreshold: opt.strokeContainThreshold || 5, 37677 silent: true, 37678 z2: 1 37679 }))); 37680 37681 var arrows = axisModel.get('axisLine.symbol'); 37682 var arrowSize = axisModel.get('axisLine.symbolSize'); 37683 37684 var arrowOffset = axisModel.get('axisLine.symbolOffset') || 0; 37685 if (typeof arrowOffset === 'number') { 37686 arrowOffset = [arrowOffset, arrowOffset]; 37687 } 37688 37689 if (arrows != null) { 37690 if (typeof arrows === 'string') { 37691 // Use the same arrow for start and end point 37692 arrows = [arrows, arrows]; 37693 } 37694 if (typeof arrowSize === 'string' 37695 || typeof arrowSize === 'number' 37696 ) { 37697 // Use the same size for width and height 37698 arrowSize = [arrowSize, arrowSize]; 37699 } 37700
37701 var symbolWidth = arrowSize[0]; 37702 var symbolHeight = arrowSize[1]; 37703 37704 each$1([{ 37705 rotate: opt.rotation + Math.PI / 2, 37706 offset: arrowOffset[0], 37707 r: 0 37708 }, { 37709 rotate: opt.rotation - Math.PI / 2, 37710 offset: arrowOffset[1], 37711 r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) 37712 + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) 37713 }], function (point, index) { 37714 if (arrows[index] !== 'none' && arrows[index] != null) { 37715 var symbol = createSymbol( 37716 arrows[index], 37717 -symbolWidth / 2, 37718 -symbolHeight / 2, 37719 symbolWidth, 37720 symbolHeight, 37721 lineStyle.stroke, 37722 true 37723 ); 37724 37725 // Calculate arrow position with offset 37726 var r = point.r + point.offset; 37727 var pos = [ 37728 pt1[0] + r * Math.cos(opt.rotation), 37729 pt1[1] - r * Math.sin(opt.rotation) 37730 ]; 37731 37732 symbol.attr({ 37733 rotation: point.rotate, 37734 position: pos, 37735 silent: true 37736 }); 37737 this.group.add(symbol); 37738 } 37739 }, this); 37740 } 37741 }, 37742 37743 /** 37744 * @private 37745 */ 37746 axisTickLabel: function () { 37747 var axisModel = this.axisModel; 37748 var opt = this.opt; 37749 37750 var tickEls = buildAxisTick(this, axisModel, opt); 37751 var labelEls = buildAxisLabel(this, axisModel, opt); 37752 37753 fixMinMaxLabelShow(axisModel, labelEls, tickEls); 37754 }, 37755 37756 /** 37757 * @private 37758 */ 37759 axisName: function () { 37760 var opt = this.opt; 37761 var axisModel = this.axisModel; 37762 var name = retrieve(opt.axisName, axisModel.get('name')); 37763 37764 if (!name) { 37765 return; 37766 } 37767 37768 var nameLocation = axisModel.get('nameLocation'); 37769 var nameDirection = opt.nameDirection; 37770 var textStyleModel = axisModel.getModel('nameTextStyle'); 37771 var gap = axisModel.get('nameGap') || 0; 37772 37773 var extent = this.axisModel.axis.getExtent(); 37774 var gapSignal = extent[0] > extent[1] ? -1 : 1; 37775 var pos = [ 37776 nameLocation === 'start' 37777 ? extent[0] - gapSignal * gap 37778 : nameLocation === 'end' 37779 ? extent[1] + gapSignal * gap 37780 : (extent[0] + extent[1]) / 2, // 'middle' 37781 // Reuse labelOffset. 37782 isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0 37783 ]; 37784 37785 var labelLayout; 37786 37787 var nameRotation = axisModel.get('nameRotate'); 37788 if (nameRotation != null) { 37789 nameRotation = nameRotation * PI$2 / 180; // To radian. 37790 } 37791 37792 var axisNameAvailableWidth; 37793 37794 if (isNameLocationCenter(nameLocation)) { 37795 labelLayout = innerTextLayout( 37796 opt.rotation, 37797 nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis. 37798 nameDirection 37799 ); 37800 } 37801 else { 37802 labelLayout = endTextLayout( 37803 opt, nameLocation, nameRotation || 0, extent 37804 ); 37805 37806 axisNameAvailableWidth = opt.axisNameAvailableWidth; 37807 if (axisNameAvailableWidth != null) { 37808 axisNameAvailableWidth = Math.abs( 37809 axisNameAvailableWidth / Math.sin(labelLayout.rotation) 37810 ); 37811 !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null); 37812 } 37813 } 37814 37815 var textFont = textStyleModel.getFont(); 37816 37817 var truncateOpt = axisModel.get('nameTruncate', true) || {}; 37818 var ellipsis = truncateOpt.ellipsis; 37819 var maxWidth = retrieve( 37820 opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth 37821 ); 37822 // FIXME 37823 // truncate rich text? (consider performance) 37824 var truncatedText = (ellipsis != null && maxWidth != null) 37825 ? truncateText$1( 37826 name, maxWidth, textFont, ellipsis, 37827 {minChar: 2, placeholder: truncateOpt.placeholder} 37828 ) 37829 : name; 37830 37831 var tooltipOpt = axisModel.get('tooltip', true); 37832 37833 var mainType = axisModel.mainType; 37834 var formatterParams = { 37835 componentType: mainType, 37836 name: name, 37837 $vars: ['name'] 37838 }; 37839 formatterParams[mainType + 'Index'] = axisModel.componentIndex; 37840 37841 var textEl = new Text({ 37842 // Id for animation 37843 anid: 'name', 37844 37845 __fullText: name, 37846 __truncatedText: truncatedText, 37847 37848 position: pos, 37849 rotation: labelLayout.rotation, 37850 silent: isSilent(axisModel), 37851 z2: 1, 37852 tooltip: (tooltipOpt && tooltipOpt.show) 37853 ? extend({ 37854 content: name, 37855 formatter: function () { 37856 return name; 37857 }, 37858 formatterParams: formatterParams 37859 }, tooltipOpt) 37860 : null 37861 }); 37862 37863 setTextStyle(textEl.style, textStyleModel, { 37864 text: truncatedText, 37865 textFont: textFont, 37866 textFill: textStyleModel.getTextColor() 37867 || axisModel.get('axisLine.lineStyle.color'), 37868 textAlign: labelLayout.textAlign, 37869 textVerticalAlign: labelLayout.textVerticalAlign 37870 }); 37871 37872 if (axisModel.get('triggerEvent')) { 37873 textEl.eventData = makeAxisEventDataBase(axisModel); 37874 textEl.eventData.targetType = 'axisName'; 37875 textEl.eventData.name = name; 37876 } 37877 37878 // FIXME 37879 this._dumbGroup.add(textEl); 37880 textEl.updateTransform(); 37881 37882 this.group.add(textEl); 37883 37884 textEl.decomposeTransform(); 37885 } 37886 37887 }; 37888 37889 /** 37890 * @public 37891 * @static 37892 * @param {Object} opt 37893 * @param {number} axisRotation in radian 37894 * @param {number} textRotation in radian 37895 * @param {number} direction 37896 * @return {Object} { 37897 * rotation, // according to axis 37898 * textAlign, 37899 * textVerticalAlign 37900 * } 37901 */ 37902 var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) { 37903 var rotationDiff = remRadian(textRotation - axisRotation); 37904 var textAlign; 37905 var textVerticalAlign; 37906 37907 if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line. 37908 textVerticalAlign = direction > 0 ? 'top' : 'bottom'; 37909 textAlign = 'center'; 37910 } 37911 else if (isRadianAroundZero(rotationDiff - PI$2)) { // Label is inverse parallel with axis line. 37912 textVerticalAlign = direction > 0 ? 'bottom' : 'top'; 37913 textAlign = 'center'; 37914 } 37915 else { 37916 textVerticalAlign = 'middle'; 37917 37918 if (rotationDiff > 0 && rotationDiff < PI$2) { 37919 textAlign = direction > 0 ? 'right' : 'left'; 37920 } 37921 else { 37922 textAlign = direction > 0 ? 'left' : 'right'; 37923 } 37924 } 37925 37926 return { 37927 rotation: rotationDiff, 37928 textAlign: textAlign, 37929 textVerticalAlign: textVerticalAlign 37930 }; 37931 }; 37932 37933 function endTextLayout(opt, textPosition, textRotate, extent) { 37934 var rotationDiff = remRadian(textRotate - opt.rotation); 37935 var textAlign; 37936 var textVerticalAlign; 37937 var inverse = extent[0] > extent[1];
37938 var onLeft = (textPosition === 'start' && !inverse) 37939 || (textPosition !== 'start' && inverse); 37940 37941 if (isRadianAroundZero(rotationDiff - PI$2 / 2)) { 37942 textVerticalAlign = onLeft ? 'bottom' : 'top'; 37943 textAlign = 'center'; 37944 } 37945 else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) { 37946 textVerticalAlign = onLeft ? 'top' : 'bottom'; 37947 textAlign = 'center'; 37948 } 37949 else { 37950 textVerticalAlign = 'middle'; 37951 if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) { 37952 textAlign = onLeft ? 'left' : 'right'; 37953 } 37954 else { 37955 textAlign = onLeft ? 'right' : 'left'; 37956 } 37957 } 37958 37959 return { 37960 rotation: rotationDiff, 37961 textAlign: textAlign, 37962 textVerticalAlign: textVerticalAlign 37963 }; 37964 } 37965 37966 function isSilent(axisModel) { 37967 var tooltipOpt = axisModel.get('tooltip'); 37968 return axisModel.get('silent') 37969 // Consider mouse cursor, add these restrictions. 37970 || !( 37971 axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show) 37972 ); 37973 } 37974 37975 function fixMinMaxLabelShow(axisModel, labelEls, tickEls) { 37976 // If min or max are user set, we need to check 37977 // If the tick on min(max) are overlap on their neighbour tick 37978 // If they are overlapped, we need to hide the min(max) tick label 37979 var showMinLabel = axisModel.get('axisLabel.showMinLabel'); 37980 var showMaxLabel = axisModel.get('axisLabel.showMaxLabel'); 37981 37982 // FIXME 37983 // Have not consider onBand yet, where tick els is more than label els. 37984 37985 labelEls = labelEls || []; 37986 tickEls = tickEls || []; 37987 37988 var firstLabel = labelEls[0]; 37989 var nextLabel = labelEls[1]; 37990 var lastLabel = labelEls[labelEls.length - 1]; 37991 var prevLabel = labelEls[labelEls.length - 2]; 37992 37993 var firstTick = tickEls[0]; 37994 var nextTick = tickEls[1]; 37995 var lastTick = tickEls[tickEls.length - 1]; 37996 var prevTick = tickEls[tickEls.length - 2]; 37997 37998 if (showMinLabel === false) { 37999 ignoreEl(firstLabel); 38000 ignoreEl(firstTick); 38001 } 38002 else if (isTwoLabelOverlapped(firstLabel, nextLabel)) { 38003 if (showMinLabel) { 38004 ignoreEl(nextLabel); 38005 ignoreEl(nextTick); 38006 } 38007 else { 38008 ignoreEl(firstLabel); 38009 ignoreEl(firstTick); 38010 } 38011 } 38012 38013 if (showMaxLabel === false) { 38014 ignoreEl(lastLabel); 38015 ignoreEl(lastTick); 38016 } 38017 else if (isTwoLabelOverlapped(prevLabel, lastLabel)) { 38018 if (showMaxLabel) { 38019 ignoreEl(prevLabel); 38020 ignoreEl(prevTick); 38021 } 38022 else { 38023 ignoreEl(lastLabel); 38024 ignoreEl(lastTick); 38025 } 38026 } 38027 } 38028 38029 function ignoreEl(el) { 38030 el && (el.ignore = true); 38031 } 38032 38033 function isTwoLabelOverlapped(current, next, labelLayout) { 38034 // current and next has the same rotation. 38035 var firstRect = current && current.getBoundingRect().clone(); 38036 var nextRect = next && next.getBoundingRect().clone(); 38037 38038 if (!firstRect || !nextRect) { 38039 return; 38040 } 38041 38042 // When checking intersect of two rotated labels, we use mRotationBack 38043 // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`. 38044 var mRotationBack = identity([]); 38045 rotate(mRotationBack, mRotationBack, -current.rotation); 38046 38047 firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform())); 38048 nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform())); 38049 38050 return firstRect.intersect(nextRect); 38051 } 38052 38053 function isNameLocationCenter(nameLocation) { 38054 return nameLocation === 'middle' || nameLocation === 'center'; 38055 } 38056 38057 function buildAxisTick(axisBuilder, axisModel, opt) { 38058 var axis = axisModel.axis; 38059 38060 if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) { 38061 return; 38062 } 38063 38064 var tickModel = axisModel.getModel('axisTick'); 38065 38066 var lineStyleModel = tickModel.getModel('lineStyle'); 38067 var tickLen = tickModel.get('length'); 38068 38069 var ticksCoords = axis.getTicksCoords(); 38070 38071 var pt1 = []; 38072 var pt2 = []; 38073 var matrix = axisBuilder._transform; 38074 38075 var tickEls = []; 38076 38077 for (var i = 0; i < ticksCoords.length; i++) { 38078 var tickCoord = ticksCoords[i].coord; 38079 38080 pt1[0] = tickCoord; 38081 pt1[1] = 0; 38082 pt2[0] = tickCoord; 38083 pt2[1] = opt.tickDirection * tickLen; 38084 38085 if (matrix) { 38086 applyTransform(pt1, pt1, matrix);
38087 applyTransform(pt2, pt2, matrix); 38088 } 38089 // Tick line, Not use group transform to have better line draw 38090 var tickEl = new Line(subPixelOptimizeLine({ 38091 // Id for animation 38092 anid: 'tick_' + ticksCoords[i].tickValue, 38093 38094 shape: { 38095 x1: pt1[0], 38096 y1: pt1[1], 38097 x2: pt2[0], 38098 y2: pt2[1] 38099 }, 38100 style: defaults( 38101 lineStyleModel.getLineStyle(), 38102 { 38103 stroke: axisModel.get('axisLine.lineStyle.color') 38104 } 38105 ), 38106 z2: 2, 38107 silent: true 38108 })); 38109 axisBuilder.group.add(tickEl); 38110 tickEls.push(tickEl); 38111 } 38112 38113 return tickEls; 38114 } 38115 38116 function buildAxisLabel(axisBuilder, axisModel, opt) { 38117 var axis = axisModel.axis; 38118 var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show')); 38119 38120 if (!show || axis.scale.isBlank()) { 38121 return; 38122 } 38123 38124 var labelModel = axisModel.getModel('axisLabel'); 38125 var labelMargin = labelModel.get('margin'); 38126 var labels = axis.getViewLabels(); 38127 38128 // Special label rotate. 38129 var labelRotation = ( 38130 retrieve(opt.labelRotate, labelModel.get('rotate')) || 0 38131 ) * PI$2 / 180; 38132 38133 var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection); 38134 var rawCategoryData = axisModel.getCategories(true); 38135 38136 var labelEls = []; 38137 var silent = isSilent(axisModel); 38138 var triggerEvent = axisModel.get('triggerEvent'); 38139 38140 each$1(labels, function (labelItem, index) { 38141 var tickValue = labelItem.tickValue; 38142 var formattedLabel = labelItem.formattedLabel; 38143 var rawLabel = labelItem.rawLabel; 38144 38145 var itemLabelModel = labelModel; 38146 if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) { 38147 itemLabelModel = new Model( 38148 rawCategoryData[tickValue].textStyle, labelModel, axisModel.ecModel 38149 ); 38150 } 38151 38152 var textColor = itemLabelModel.getTextColor() 38153 || axisModel.get('axisLine.lineStyle.color'); 38154 38155 var tickCoord = axis.dataToCoord(tickValue); 38156 var pos = [ 38157 tickCoord, 38158 opt.labelOffset + opt.labelDirection * labelMargin 38159 ]; 38160 38161 var textEl = new Text({ 38162 // Id for animation 38163 anid: 'label_' + tickValue, 38164 position: pos, 38165 rotation: labelLayout.rotation, 38166 silent: silent, 38167 z2: 10 38168 }); 38169 38170 setTextStyle(textEl.style, itemLabelModel, { 38171 text: formattedLabel, 38172 textAlign: itemLabelModel.getShallow('align', true) 38173 || labelLayout.textAlign, 38174 textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true) 38175 || itemLabelModel.getShallow('baseline', true) 38176 || labelLayout.textVerticalAlign, 38177 textFill: typeof textColor === 'function' 38178 ? textColor( 38179 // (1) In category axis with data zoom, tick is not the original 38180 // index of axis.data. So tick should not be exposed to user 38181 // in category axis. 38182 // (2) Compatible with previous version, which always use formatted label as 38183 // input. But in interval scale the formatted label is like '223,445', which 38184 // maked user repalce ','. So we modify it to return original val but remain 38185 // it as 'string' to avoid error in replacing. 38186 axis.type === 'category' 38187 ? rawLabel 38188 : axis.type === 'value' 38189 ? tickValue + '' 38190 : tickValue, 38191 index 38192 ) 38193 : textColor 38194 }); 38195 38196 // Pack data for mouse event 38197 if (triggerEvent) { 38198 textEl.eventData = makeAxisEventDataBase(axisModel); 38199 textEl.eventData.targetType = 'axisLabel'; 38200 textEl.eventData.value = rawLabel; 38201 } 38202 38203 // FIXME 38204 axisBuilder._dumbGroup.add(textEl); 38205 textEl.updateTransform(); 38206 38207 labelEls.push(textEl); 38208 axisBuilder.group.add(textEl); 38209 38210 textEl.decomposeTransform(); 38211 38212 }); 38213 38214 return labelEls; 38215 } 38216 38217 /* 38218* Licensed to the Apache Software Foundation (ASF) under one 38219* or more contributor license agreements. See the NOTICE file 38220* distributed with this work for additional information 38221* regarding copyright ownership. The ASF licenses this file 38222* to you under the Apache License, Version 2.0 (the 38223* "License");
38223 you may not use this file except in compliance 38224* with the License. You may obtain a copy of the License at 38225* 38226* http://www.apache.org/licenses/LICENSE-2.0 38227* 38228* Unless required by applicable law or agreed to in writing, 38229* software distributed under the License is distributed on an 38230* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 38231* KIND, either express or implied. See the License for the 38232* specific language governing permissions and limitations 38233* under the License. 38234*/ 38235 38236 var each$6 = each$1; 38237 var curry$1 = curry; 38238 38239// Build axisPointerModel, mergin tooltip.axisPointer model for each axis. 38240// allAxesInfo should be updated when setOption performed. 38241 function collect(ecModel, api) { 38242 var result = { 38243 /** 38244 * key: makeKey(axis.model) 38245 * value: { 38246 * axis, 38247 * coordSys, 38248 * axisPointerModel, 38249 * triggerTooltip, 38250 * involveSeries, 38251 * snap, 38252 * seriesModels, 38253 * seriesDataCount 38254 * } 38255 */ 38256 axesInfo: {}, 38257 seriesInvolved: false, 38258 /** 38259 * key: makeKey(coordSys.model) 38260 * value: Object: key makeKey(axis.model), value: axisInfo 38261 */ 38262 coordSysAxesInfo: {}, 38263 coordSysMap: {} 38264 }; 38265 38266 collectAxesInfo(result, ecModel, api); 38267 38268 // Check seriesInvolved for performance, in case too many series in some chart. 38269 result.seriesInvolved && collectSeriesInfo(result, ecModel); 38270 38271 return result; 38272 } 38273 38274 function collectAxesInfo(result, ecModel, api) { 38275 var globalTooltipModel = ecModel.getComponent('tooltip'); 38276 var globalAxisPointerModel = ecModel.getComponent('axisPointer'); 38277 // links can only be set on global. 38278 var linksOption = globalAxisPointerModel.get('link', true) || []; 38279 var linkGroups = []; 38280 38281 // Collect axes info. 38282 each$6(api.getCoordinateSystems(), function (coordSys) { 38283 // Some coordinate system do not support axes, like geo. 38284 if (!coordSys.axisPointerEnabled) { 38285 return; 38286 } 38287 38288 var coordSysKey = makeKey(coordSys.model); 38289 var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {}; 38290 result.coordSysMap[coordSysKey] = coordSys; 38291 38292 // Set tooltip (like 'cross') is a convienent way to show axisPointer 38293 // for user. So we enable seting tooltip on coordSys model. 38294 var coordSysModel = coordSys.model; 38295 var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel); 38296 38297 each$6(coordSys.getAxes(), curry$1(saveTooltipAxisInfo, false, null)); 38298 38299 // If axis tooltip used, choose tooltip axis for each coordSys. 38300 // Notice this case: coordSys is `grid` but not `cartesian2D` here. 38301 if (coordSys.getTooltipAxes 38302 && globalTooltipModel 38303 // If tooltip.showContent is set as false, tooltip will not 38304 // show but axisPointer will show as normal. 38305 && baseTooltipModel.get('show') 38306 ) { 38307 // Compatible with previous logic. But series.tooltip.trigger: 'axis' 38308 // or series.data[n].tooltip.trigger: 'axis' are not support any more. 38309 var triggerAxis = baseTooltipModel.get('trigger') === 'axis'; 38310 var cross = baseTooltipModel.get('axisPointer.type') === 'cross'; 38311 var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis')); 38312 if (triggerAxis || cross) { 38313 each$6(tooltipAxes.baseAxes, curry$1( 38314 saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis 38315 )); 38316 } 38317 if (cross) { 38318 each$6(tooltipAxes.otherAxes, curry$1(saveTooltipAxisInfo, 'cross', false)); 38319 } 38320 } 38321 38322 // fromTooltip: true | false | 'cross' 38323 // triggerTooltip: true | false | null 38324 function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) { 38325 var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel); 38326 38327 var axisPointerShow = axisPointerModel.get('show'); 38328 if (!axisPointerShow || ( 38329 axisPointerShow === 'auto' 38330 && !fromTooltip 38331 && !isHandleTrigger(axisPointerModel) 38332 )) { 38333 return; 38334 } 38335 38336 if (triggerTooltip == null) { 38337 triggerTooltip = axisPointerModel.get('triggerTooltip'); 38338 } 38339 38340 axisPointerModel = fromTooltip 38341 ? makeAxisPointerModel( 38342 axis, baseTooltipModel, globalAxisPointerModel, ecModel, 38343 fromTooltip, triggerTooltip 38344 ) 38345 : axisPointerModel; 38346 38347 var snap = axisPointerModel.get('snap'); 38348 var key = makeKey(axis.model); 38349 var involveSeries = triggerTooltip || snap || axis.type === 'category'; 38350 38351 // If result.axesInfo[key] exist, override it (tooltip has higher priority). 38352 var axisInfo = result.axesInfo[key] = { 38353 key: key, 38354 axis: axis, 38355 coordSys: coordSys, 38356 axisPointerModel: axisPointerModel, 38357 triggerTooltip: triggerTooltip, 38358 involveSeries: involveSeries, 38359 snap: snap, 38360 useHandle: isHandleTrigger(axisPointerModel), 38361 seriesModels: [] 38362 }; 38363 axesInfoInCoordSys[key] = axisInfo; 38364 result.seriesInvolved |= involveSeries; 38365 38366 var groupIndex = getLinkGroupIndex(linksOption, axis);
vendor: 3,646 bytes, lines 38367-38445
38367 if (groupIndex != null) { 38368 var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {axesInfo: {}}); 38369 linkGroup.axesInfo[key] = axisInfo; 38370 linkGroup.mapper = linksOption[groupIndex].mapper; 38371 axisInfo.linkGroup = linkGroup; 38372 } 38373 } 38374 }); 38375 } 38376 38377 function makeAxisPointerModel( 38378 axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip 38379 ) { 38380 var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer'); 38381 var volatileOption = {}; 38382 38383 each$6( 38384 [ 38385 'type', 'snap', 'lineStyle', 'shadowStyle', 'label', 38386 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z' 38387 ], 38388 function (field) { 38389 volatileOption[field] = clone(tooltipAxisPointerModel.get(field)); 38390 } 38391 ); 38392 38393 // category axis do not auto snap, otherwise some tick that do not 38394 // has value can not be hovered. value/time/log axis default snap if 38395 // triggered from tooltip and trigger tooltip. 38396 volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; 38397 38398 // Compatibel with previous behavior, tooltip axis do not show label by default. 38399 // Only these properties can be overrided from tooltip to axisPointer. 38400 if (tooltipAxisPointerModel.get('type') === 'cross') { 38401 volatileOption.type = 'line'; 38402 } 38403 var labelOption = volatileOption.label || (volatileOption.label = {}); 38404 // Follow the convention, do not show label when triggered by tooltip by default. 38405 labelOption.show == null && (labelOption.show = false); 38406 38407 if (fromTooltip === 'cross') { 38408 // When 'cross', both axes show labels. 38409 var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get('label.show'); 38410 labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; 38411 // If triggerTooltip, this is a base axis, which should better not use cross style 38412 // (cross style is dashed by default) 38413 if (!triggerTooltip) { 38414 var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle'); 38415 crossStyle && defaults(labelOption, crossStyle.textStyle); 38416 } 38417 } 38418 38419 return axis.model.getModel( 38420 'axisPointer', 38421 new Model(volatileOption, globalAxisPointerModel, ecModel) 38422 ); 38423 } 38424 38425 function collectSeriesInfo(result, ecModel) { 38426 // Prepare data for axis trigger 38427 ecModel.eachSeries(function (seriesModel) { 38428 38429 // Notice this case: this coordSys is `cartesian2D` but not `grid`. 38430 var coordSys = seriesModel.coordinateSystem; 38431 var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true); 38432 var seriesTooltipShow = seriesModel.get('tooltip.show', true); 38433 if (!coordSys 38434 || seriesTooltipTrigger === 'none' 38435 || seriesTooltipTrigger === false 38436 || seriesTooltipTrigger === 'item' 38437 || seriesTooltipShow === false 38438 || seriesModel.get('axisPointer.show', true) === false 38439 ) { 38440 return; 38441 } 38442 38443 each$6(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) { 38444 var axis = axisInfo.axis; 38445 if (coordSys.getAxis(axis.dim) === axis) {
38446 axisInfo.seriesModels.push(seriesModel); 38447 axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0); 38448 axisInfo.seriesDataCount += seriesModel.getData().count(); 38449 } 38450 }); 38451 38452 }, this); 38453 } 38454 38455 /** 38456 * For example: 38457 * { 38458 * axisPointer: { 38459 * links: [{ 38460 * xAxisIndex: [2, 4], 38461 * yAxisIndex: 'all' 38462 * }, { 38463 * xAxisId: ['a5', 'a7'], 38464 * xAxisName: 'xxx' 38465 * }] 38466 * } 38467 * } 38468 */ 38469 function getLinkGroupIndex(linksOption, axis) { 38470 var axisModel = axis.model; 38471 var dim = axis.dim; 38472 for (var i = 0; i < linksOption.length; i++) { 38473 var linkOption = linksOption[i] || {}; 38474 if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) 38475 || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) 38476 || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name) 38477 ) { 38478 return i; 38479 } 38480 } 38481 } 38482 38483 function checkPropInLink(linkPropValue, axisPropValue) { 38484 return linkPropValue === 'all' 38485 || (isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0) 38486 || linkPropValue === axisPropValue; 38487 } 38488 38489 function fixValue(axisModel) { 38490 var axisInfo = getAxisInfo(axisModel); 38491 if (!axisInfo) { 38492 return; 38493 } 38494 38495 var axisPointerModel = axisInfo.axisPointerModel; 38496 var scale = axisInfo.axis.scale; 38497 var option = axisPointerModel.option; 38498 var status = axisPointerModel.get('status'); 38499 var value = axisPointerModel.get('value'); 38500 38501 // Parse init value for category and time axis. 38502 if (value != null) { 38503 value = scale.parse(value); 38504 } 38505 38506 var useHandle = isHandleTrigger(axisPointerModel); 38507 // If `handle` used, `axisPointer` will always be displayed, so value 38508 // and status should be initialized. 38509 if (status == null) { 38510 option.status = useHandle ? 'show' : 'hide'; 38511 } 38512 38513 var extent = scale.getExtent().slice(); 38514 extent[0] > extent[1] && extent.reverse(); 38515 38516 if (// Pick a value on axis when initializing. 38517 value == null 38518 // If both `handle` and `dataZoom` are used, value may be out of axis extent, 38519 // where we should re-pick a value to keep `handle` displaying normally. 38520 || value > extent[1] 38521 ) { 38522 // Make handle displayed on the end of the axis when init, which looks better. 38523 value = extent[1]; 38524 } 38525 if (value < extent[0]) { 38526 value = extent[0]; 38527 } 38528 38529 option.value = value; 38530 38531 if (useHandle) { 38532 option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show'; 38533 } 38534 } 38535 38536 function getAxisInfo(axisModel) { 38537 var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo; 38538 return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)]; 38539 } 38540 38541 function getAxisPointerModel(axisModel) { 38542 var axisInfo = getAxisInfo(axisModel); 38543 return axisInfo && axisInfo.axisPointerModel; 38544 } 38545 38546 function isHandleTrigger(axisPointerModel) { 38547 return !!axisPointerModel.get('handle.show'); 38548 } 38549 38550 /** 38551 * @param {module:echarts/model/Model} model 38552 * @return {string} unique key 38553 */ 38554 function makeKey(model) { 38555 return model.type + '||' + model.id; 38556 } 38557 38558 /* 38559* Licensed to the Apache Software Foundation (ASF) under one 38560* or more contributor license agreements. See the NOTICE file 38561* distributed with this work for additional information 38562* regarding copyright ownership. The ASF licenses this file 38563* to you under the Apache License, Version 2.0 (the 38564* "License"); you may not use this file except in compliance 38565* with the License. You may obtain a copy of the License at 38566* 38567* http://www.apache.org/licenses/LICENSE-2.0 38568* 38569* Unless required by applicable law or agreed to in writing, 38570* software distributed under the License is distributed on an 38571* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 38572* KIND, either express or implied. See the License for the 38573* specific language governing permissions and limitations 38574* under the License. 38575*/ 38576 38577 /** 38578 * Base class of AxisView. 38579 */ 38580 var AxisView = extendComponentView({
38581 38582 type: 'axis', 38583 38584 /** 38585 * @private 38586 */ 38587 _axisPointer: null, 38588 38589 /** 38590 * @protected 38591 * @type {string} 38592 */ 38593 axisPointerClass: null, 38594 38595 /** 38596 * @override 38597 */ 38598 render: function (axisModel, ecModel, api, payload) { 38599 // FIXME 38600 // This process should proformed after coordinate systems updated 38601 // (axis scale updated), and should be performed each time update. 38602 // So put it here temporarily, although it is not appropriate to 38603 // put a model-writing procedure in `view`. 38604 this.axisPointerClass && fixValue(axisModel); 38605 38606 AxisView.superApply(this, 'render', arguments); 38607 38608 updateAxisPointer(this, axisModel, ecModel, api, payload, true); 38609 }, 38610 38611 /** 38612 * Action handler. 38613 * @public 38614 * @param {module:echarts/coord/cartesian/AxisModel} axisModel 38615 * @param {module:echarts/model/Global} ecModel 38616 * @param {module:echarts/ExtensionAPI} api 38617 * @param {Object} payload 38618 */ 38619 updateAxisPointer: function (axisModel, ecModel, api, payload, force) { 38620 updateAxisPointer(this, axisModel, ecModel, api, payload, false); 38621 }, 38622 38623 /** 38624 * @override 38625 */ 38626 remove: function (ecModel, api) { 38627 var axisPointer = this._axisPointer; 38628 axisPointer && axisPointer.remove(api); 38629 AxisView.superApply(this, 'remove', arguments); 38630 }, 38631 38632 /** 38633 * @override 38634 */ 38635 dispose: function (ecModel, api) { 38636 disposeAxisPointer(this, api); 38637 AxisView.superApply(this, 'dispose', arguments); 38638 } 38639 38640 }); 38641 38642 function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) { 38643 var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass); 38644 if (!Clazz) { 38645 return; 38646 } 38647 var axisPointerModel = getAxisPointerModel(axisModel); 38648 axisPointerModel 38649 ? (axisView._axisPointer || (axisView._axisPointer = new Clazz())) 38650 .render(axisModel, axisPointerModel, api, forceRender) 38651 : disposeAxisPointer(axisView, api); 38652 } 38653 38654 function disposeAxisPointer(axisView, ecModel, api) { 38655 var axisPointer = axisView._axisPointer; 38656 axisPointer && axisPointer.dispose(ecModel, api); 38657 axisView._axisPointer = null; 38658 } 38659 38660 var axisPointerClazz = []; 38661 38662 AxisView.registerAxisPointerClass = function (type, clazz) { 38663 if (__DEV__) { 38664 if (axisPointerClazz[type]) { 38665 throw new Error('axisPointer ' + type + ' exists'); 38666 } 38667 } 38668 axisPointerClazz[type] = clazz; 38669 }; 38670 38671 AxisView.getAxisPointerClass = function (type) { 38672 return type && axisPointerClazz[type]; 38673 }; 38674 38675 /* 38676* Licensed to the Apache Software Foundation (ASF) under one 38677* or more contributor license agreements. See the NOTICE file 38678* distributed with this work for additional information 38679* regarding copyright ownership. The ASF licenses this file 38680* to you under the Apache License, Version 2.0 (the 38681* "License"); you may not use this file except in compliance 38682* with the License. You may obtain a copy of the License at 38683* 38684* http://www.apache.org/licenses/LICENSE-2.0 38685* 38686* Unless required by applicable law or agreed to in writing, 38687* software distributed under the License is distributed on an 38688* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 38689* KIND, either express or implied. See the License for the 38690* specific language governing permissions and limitations 38691* under the License. 38692*/ 38693 38694 /** 38695 * Can only be called after coordinate system creation stage. 38696 * (Can be called before coordinate system update stage). 38697 * 38698 * @param {Object} opt {labelInside} 38699 * @return {Object} { 38700 * position, rotation, labelDirection, labelOffset, 38701 * tickDirection, labelRotate, z2 38702 * } 38703 */ 38704 function layout$1(gridModel, axisModel, opt) { 38705 opt = opt || {}; 38706 var grid = gridModel.coordinateSystem; 38707 var axis = axisModel.axis; 38708 var layout = {}; 38709 var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0]; 38710 38711 var rawAxisPosition = axis.position; 38712 var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition; 38713 var axisDim = axis.dim; 38714 38715 var rect = grid.getRect(); 38716 var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; 38717 var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2}; 38718 var axisOffset = axisModel.get('offset') || 0; 38719 38720 var posBound = axisDim === 'x' 38721 ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] 38722 : [rectBound[0] - axisOffset, rectBound[1] + axisOffset]; 38723 38724 if (otherAxisOnZeroOf) { 38725 var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0)); 38726 posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]); 38727 } 38728 38729 // Axis position 38730 layout.position = [ 38731 axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], 38732 axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3] 38733 ]; 38734 38735 // Axis rotation 38736 layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); 38737 38738 // Tick and label direction, x y is axisDim 38739 var dirMap = {top: -1, bottom: 1, left: -1, right: 1}; 38740 38741 layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition]; 38742 layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0; 38743 38744 if (axisModel.get('axisTick.inside')) { 38745 layout.tickDirection = -layout.tickDirection; 38746 } 38747 if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) { 38748 layout.labelDirection = -layout.labelDirection; 38749 } 38750 38751 // Special label rotation 38752 var labelRotate = axisModel.get('axisLabel.rotate'); 38753 layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; 38754 38755 // Over splitLine and splitArea 38756 layout.z2 = 1; 38757 38758 return layout; 38759 } 38760 38761 /* 38762* Licensed to the Apache Software Foundation (ASF) under one 38763* or more contributor license agreements. See the NOTICE file 38764* distributed with this work for additional information 38765* regarding copyright ownership. The ASF licenses this file 38766* to you under the Apache License, Version 2.0 (the 38767* "License");
38767 you may not use this file except in compliance 38768* with the License. You may obtain a copy of the License at 38769* 38770* http://www.apache.org/licenses/LICENSE-2.0 38771* 38772* Unless required by applicable law or agreed to in writing, 38773* software distributed under the License is distributed on an 38774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 38775* KIND, either express or implied. See the License for the 38776* specific language governing permissions and limitations 38777* under the License. 38778*/ 38779 38780 var axisBuilderAttrs = [ 38781 'axisLine', 'axisTickLabel', 'axisName' 38782 ]; 38783 var selfBuilderAttrs = [ 38784 'splitArea', 'splitLine' 38785 ]; 38786 38787// function getAlignWithLabel(model, axisModel) { 38788// var alignWithLabel = model.get('alignWithLabel'); 38789// if (alignWithLabel === 'auto') { 38790// alignWithLabel = axisModel.get('axisTick.alignWithLabel'); 38791// } 38792// return alignWithLabel; 38793// } 38794 38795 var CartesianAxisView = AxisView.extend({ 38796 38797 type: 'cartesianAxis', 38798 38799 axisPointerClass: 'CartesianAxisPointer', 38800 38801 /** 38802 * @override 38803 */ 38804 render: function (axisModel, ecModel, api, payload) { 38805 38806 this.group.removeAll(); 38807 38808 var oldAxisGroup = this._axisGroup; 38809 this._axisGroup = new Group(); 38810 38811 this.group.add(this._axisGroup); 38812 38813 if (!axisModel.get('show')) { 38814 return; 38815 } 38816 38817 var gridModel = axisModel.getCoordSysModel(); 38818 38819 var layout = layout$1(gridModel, axisModel); 38820 38821 var axisBuilder = new AxisBuilder(axisModel, layout); 38822 38823 each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder); 38824 38825 this._axisGroup.add(axisBuilder.getGroup()); 38826 38827 each$1(selfBuilderAttrs, function (name) { 38828 if (axisModel.get(name + '.show')) { 38829 this['_' + name](axisModel, gridModel); 38830 } 38831 }, this); 38832 38833 groupTransition(oldAxisGroup, this._axisGroup, axisModel); 38834 38835 CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload); 38836 }, 38837 38838 remove: function () { 38839 this._splitAreaColors = null; 38840 }, 38841 38842 /** 38843 * @param {module:echarts/coord/cartesian/AxisModel} axisModel 38844 * @param {module:echarts/coord/cartesian/GridModel} gridModel 38845 * @private 38846 */ 38847 _splitLine: function (axisModel, gridModel) { 38848 var axis = axisModel.axis; 38849 38850 if (axis.scale.isBlank()) { 38851 return; 38852 } 38853 38854 var splitLineModel = axisModel.getModel('splitLine'); 38855 var lineStyleModel = splitLineModel.getModel('lineStyle'); 38856 var lineColors = lineStyleModel.get('color'); 38857 38858 lineColors = isArray(lineColors) ? lineColors : [lineColors]; 38859 38860 var gridRect = gridModel.coordinateSystem.getRect(); 38861 var isHorizontal = axis.isHorizontal(); 38862 38863 var lineCount = 0; 38864 38865 var ticksCoords = axis.getTicksCoords({ 38866 tickModel: splitLineModel 38867 }); 38868 38869 var p1 = []; 38870 var p2 = []; 38871 38872 // Simple optimization 38873 // Batching the lines if color are the same 38874 var lineStyle = lineStyleModel.getLineStyle(); 38875 for (var i = 0; i < ticksCoords.length; i++) { 38876 var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); 38877 38878 if (isHorizontal) { 38879 p1[0] = tickCoord; 38880 p1[1] = gridRect.y; 38881 p2[0] = tickCoord; 38882 p2[1] = gridRect.y + gridRect.height; 38883 } 38884 else { 38885 p1[0] = gridRect.x; 38886 p1[1] = tickCoord;
38887 p2[0] = gridRect.x + gridRect.width; 38888 p2[1] = tickCoord; 38889 } 38890 38891 var colorIndex = (lineCount++) % lineColors.length; 38892 var tickValue = ticksCoords[i].tickValue; 38893 this._axisGroup.add(new Line(subPixelOptimizeLine({ 38894 anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null, 38895 shape: { 38896 x1: p1[0], 38897 y1: p1[1], 38898 x2: p2[0], 38899 y2: p2[1] 38900 }, 38901 style: defaults({ 38902 stroke: lineColors[colorIndex] 38903 }, lineStyle), 38904 silent: true 38905 }))); 38906 } 38907 }, 38908 38909 /** 38910 * @param {module:echarts/coord/cartesian/AxisModel} axisModel 38911 * @param {module:echarts/coord/cartesian/GridModel} gridModel 38912 * @private 38913 */ 38914 _splitArea: function (axisModel, gridModel) { 38915 var axis = axisModel.axis; 38916 38917 if (axis.scale.isBlank()) { 38918 return; 38919 } 38920 38921 var splitAreaModel = axisModel.getModel('splitArea'); 38922 var areaStyleModel = splitAreaModel.getModel('areaStyle'); 38923 var areaColors = areaStyleModel.get('color'); 38924 38925 var gridRect = gridModel.coordinateSystem.getRect(); 38926 38927 var ticksCoords = axis.getTicksCoords({ 38928 tickModel: splitAreaModel, 38929 clamp: true 38930 }); 38931 38932 if (!ticksCoords.length) { 38933 return; 38934 } 38935 38936 // For Making appropriate splitArea animation, the color and anid 38937 // should be corresponding to previous one if possible. 38938 var areaColorsLen = areaColors.length; 38939 var lastSplitAreaColors = this._splitAreaColors; 38940 var newSplitAreaColors = createHashMap(); 38941 var colorIndex = 0; 38942 if (lastSplitAreaColors) { 38943 for (var i = 0; i < ticksCoords.length; i++) { 38944 var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue); 38945 if (cIndex != null) { 38946 colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen; 38947 break; 38948 } 38949 } 38950 } 38951 38952 var prev = axis.toGlobalCoord(ticksCoords[0].coord); 38953 38954 var areaStyle = areaStyleModel.getAreaStyle(); 38955 areaColors = isArray(areaColors) ? areaColors : [areaColors]; 38956 38957 for (var i = 1; i < ticksCoords.length; i++) { 38958 var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); 38959 38960 var x; 38961 var y; 38962 var width; 38963 var height; 38964 if (axis.isHorizontal()) { 38965 x = prev; 38966 y = gridRect.y; 38967 width = tickCoord - x; 38968 height = gridRect.height; 38969 prev = x + width; 38970 } 38971 else { 38972 x = gridRect.x; 38973 y = prev; 38974 width = gridRect.width; 38975 height = tickCoord - y; 38976 prev = y + height; 38977 } 38978 38979 var tickValue = ticksCoords[i - 1].tickValue; 38980 tickValue != null && newSplitAreaColors.set(tickValue, colorIndex); 38981 38982 this._axisGroup.add(new Rect({ 38983 anid: tickValue != null ? 'area_' + tickValue : null, 38984 shape: { 38985 x: x, 38986 y: y, 38987 width: width, 38988 height: height 38989 }, 38990 style: defaults({ 38991 fill: areaColors[colorIndex] 38992 }, areaStyle), 38993 silent: true 38994 })); 38995 38996 colorIndex = (colorIndex + 1) % areaColorsLen; 38997 } 38998 38999 this._splitAreaColors = newSplitAreaColors; 39000 } 39001 });
39002 39003 CartesianAxisView.extend({ 39004 type: 'xAxis' 39005 }); 39006 CartesianAxisView.extend({ 39007 type: 'yAxis' 39008 }); 39009 39010 /* 39011* Licensed to the Apache Software Foundation (ASF) under one 39012* or more contributor license agreements. See the NOTICE file 39013* distributed with this work for additional information 39014* regarding copyright ownership. The ASF licenses this file 39015* to you under the Apache License, Version 2.0 (the 39016* "License"); you may not use this file except in compliance 39017* with the License. You may obtain a copy of the License at 39018* 39019* http://www.apache.org/licenses/LICENSE-2.0 39020* 39021* Unless required by applicable law or agreed to in writing, 39022* software distributed under the License is distributed on an 39023* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39024* KIND, either express or implied. See the License for the 39025* specific language governing permissions and limitations 39026* under the License. 39027*/ 39028 39029 /* 39030* Licensed to the Apache Software Foundation (ASF) under one 39031* or more contributor license agreements. See the NOTICE file 39032* distributed with this work for additional information 39033* regarding copyright ownership. The ASF licenses this file 39034* to you under the Apache License, Version 2.0 (the 39035* "License"); you may not use this file except in compliance 39036* with the License. You may obtain a copy of the License at 39037* 39038* http://www.apache.org/licenses/LICENSE-2.0 39039* 39040* Unless required by applicable law or agreed to in writing, 39041* software distributed under the License is distributed on an 39042* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39043* KIND, either express or implied. See the License for the 39044* specific language governing permissions and limitations 39045* under the License. 39046*/ 39047 39048// Grid view 39049 extendComponentView({ 39050 39051 type: 'grid', 39052 39053 render: function (gridModel, ecModel) { 39054 this.group.removeAll(); 39055 if (gridModel.get('show')) { 39056 this.group.add(new Rect({ 39057 shape: gridModel.coordinateSystem.getRect(), 39058 style: defaults({ 39059 fill: gridModel.get('backgroundColor') 39060 }, gridModel.getItemStyle()), 39061 silent: true, 39062 z2: -1 39063 })); 39064 } 39065 } 39066 39067 }); 39068 39069 registerPreprocessor(function (option) { 39070 // Only create grid when need 39071 if (option.xAxis && option.yAxis && !option.grid) { 39072 option.grid = {}; 39073 } 39074 }); 39075 39076 /* 39077* Licensed to the Apache Software Foundation (ASF) under one 39078* or more contributor license agreements. See the NOTICE file 39079* distributed with this work for additional information 39080* regarding copyright ownership. The ASF licenses this file 39081* to you under the Apache License, Version 2.0 (the 39082* "License"); you may not use this file except in compliance 39083* with the License. You may obtain a copy of the License at 39084* 39085* http://www.apache.org/licenses/LICENSE-2.0 39086* 39087* Unless required by applicable law or agreed to in writing, 39088* software distributed under the License is distributed on an 39089* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39090* KIND, either express or implied. See the License for the 39091* specific language governing permissions and limitations 39092* under the License. 39093*/ 39094 39095// In case developer forget to include grid component 39096 registerVisual(visualSymbol('line', 'circle', 'line')); 39097 registerLayout(pointsLayout('line')); 39098 39099// Down sample after filter 39100 registerProcessor( 39101 PRIORITY.PROCESSOR.STATISTIC, 39102 dataSample('line') 39103 ); 39104 39105 /* 39106* Licensed to the Apache Software Foundation (ASF) under one 39107* or more contributor license agreements. See the NOTICE file 39108* distributed with this work for additional information 39109* regarding copyright ownership. The ASF licenses this file 39110* to you under the Apache License, Version 2.0 (the 39111* "License"); you may not use this file except in compliance 39112* with the License. You may obtain a copy of the License at 39113* 39114* http://www.apache.org/licenses/LICENSE-2.0 39115* 39116* Unless required by applicable law or agreed to in writing, 39117* software distributed under the License is distributed on an 39118* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39119* KIND, either express or implied. See the License for the 39120* specific language governing permissions and limitations 39121* under the License. 39122*/ 39123 39124 var BaseBarSeries = SeriesModel.extend({ 39125 39126 type: 'series.__base_bar__', 39127 39128 getInitialData: function (option, ecModel) { 39129 return createListFromArray(this.getSource(), this); 39130 }, 39131 39132 getMarkerPosition: function (value) { 39133 var coordSys = this.coordinateSystem; 39134 if (coordSys) { 39135 // PENDING if clamp ? 39136 var pt = coordSys.dataToPoint(coordSys.clampData(value)); 39137 var data = this.getData(); 39138 var offset = data.getLayout('offset'); 39139 var size = data.getLayout('size'); 39140 var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1; 39141 pt[offsetIndex] += offset + size / 2; 39142 return pt; 39143 } 39144 return [NaN, NaN]; 39145 }, 39146 39147 defaultOption: { 39148 zlevel: 0, // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 39149 z: 2, // äºÅ级å±âå 39150 coordinateSystem: 'cartesian2d', 39151 legendHoverLink: true, 39152 // stack: null 39153 39154 // Cartesian coordinate system 39155 // xAxisIndex: 0, 39156 // yAxisIndex: 0, 39157 39158 // æÅâ¬Ã¥Â°Âé«Ë度æâ¹ä¸º0 39159 barMinHeight: 0, 39160 // æÅâ¬Ã¥Â°Âè§â度为0ï¼Åä»â¦Ã¥Â¯Â¹Ã¦Å¾ÂÃ¥ÂÂæ â¡Ã§Â³Â»Ã¤Â¸â¹Ã§Å¡â柱状åâºÂ¾Ã¦Åâ°Ã¦â¢Ë 39161 barMinAngle: 0, 39162 // cursor: null, 39163 39164 large: false, 39165 largeThreshold: 400, 39166 progressive: 3e3, 39167 progressiveChunkMode: 'mod', 39168 39169 // barMaxWidth: null,
39170 // é»Ë认èâ¡ÂªÃ©â¬âåºâ 39171 // barWidth: null, 39172 // 柱éâ´è·Â离ï¼Åé»Ë认为柱形宽度çšâ30%ï¼Åå¯设åâºÂºÃ¥Â®Å¡Ã¥â¬Â¼ 39173 // barGap: '30%', 39174 // ç±»çâºÂ®Ã©â´æŸ±å½¢è·Â离ï¼Åé»Ë认为类çâºÂ®Ã©â´è·Âçšâ20%ï¼Åå¯设åâºÂºÃ¥Â®Å¡Ã¥â¬Â¼ 39175 // barCategoryGap: '20%', 39176 // label: { 39177 // show: false 39178 // }, 39179 itemStyle: {}, 39180 emphasis: {} 39181 } 39182 }); 39183 39184 /* 39185* Licensed to the Apache Software Foundation (ASF) under one 39186* or more contributor license agreements. See the NOTICE file 39187* distributed with this work for additional information 39188* regarding copyright ownership. The ASF licenses this file 39189* to you under the Apache License, Version 2.0 (the 39190* "License"); you may not use this file except in compliance 39191* with the License. You may obtain a copy of the License at 39192* 39193* http://www.apache.org/licenses/LICENSE-2.0 39194* 39195* Unless required by applicable law or agreed to in writing, 39196* software distributed under the License is distributed on an 39197* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39198* KIND, either express or implied. See the License for the 39199* specific language governing permissions and limitations 39200* under the License. 39201*/ 39202 39203 BaseBarSeries.extend({ 39204 39205 type: 'series.bar', 39206 39207 dependencies: ['grid', 'polar'], 39208 39209 brushSelector: 'rect', 39210 39211 /** 39212 * @override 39213 */ 39214 getProgressive: function () { 39215 // Do not support progressive in normal mode. 39216 return this.get('large') 39217 ? this.get('progressive') 39218 : false; 39219 }, 39220 39221 /** 39222 * @override 39223 */ 39224 getProgressiveThreshold: function () { 39225 // Do not support progressive in normal mode. 39226 var progressiveThreshold = this.get('progressiveThreshold'); 39227 var largeThreshold = this.get('largeThreshold'); 39228 if (largeThreshold > progressiveThreshold) { 39229 progressiveThreshold = largeThreshold; 39230 } 39231 return progressiveThreshold; 39232 } 39233 39234 }); 39235 39236 /* 39237* Licensed to the Apache Software Foundation (ASF) under one 39238* or more contributor license agreements. See the NOTICE file 39239* distributed with this work for additional information 39240* regarding copyright ownership. The ASF licenses this file 39241* to you under the Apache License, Version 2.0 (the 39242* "License"); you may not use this file except in compliance 39243* with the License. You may obtain a copy of the License at 39244* 39245* http://www.apache.org/licenses/LICENSE-2.0 39246* 39247* Unless required by applicable law or agreed to in writing, 39248* software distributed under the License is distributed on an 39249* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39250* KIND, either express or implied. See the License for the 39251* specific language governing permissions and limitations 39252* under the License. 39253*/ 39254 39255 function setLabel( 39256 normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside 39257 ) { 39258 var labelModel = itemModel.getModel('label'); 39259 var hoverLabelModel = itemModel.getModel('emphasis.label'); 39260 39261 setLabelStyle( 39262 normalStyle, hoverStyle, labelModel, hoverLabelModel, 39263 { 39264 labelFetcher: seriesModel, 39265 labelDataIndex: dataIndex, 39266 defaultText: getDefaultLabel(seriesModel.getData(), dataIndex), 39267 isRectText: true, 39268 autoColor: color 39269 } 39270 ); 39271 39272 fixPosition(normalStyle); 39273 fixPosition(hoverStyle); 39274 } 39275 39276 function fixPosition(style, labelPositionOutside) { 39277 if (style.textPosition === 'outside') { 39278 style.textPosition = labelPositionOutside; 39279 } 39280 } 39281 39282 /* 39283* Licensed to the Apache Software Foundation (ASF) under one 39284* or more contributor license agreements. See the NOTICE file 39285* distributed with this work for additional information 39286* regarding copyright ownership. The ASF licenses this file 39287* to you under the Apache License, Version 2.0 (the 39288* "License");
39288 you may not use this file except in compliance 39289* with the License. You may obtain a copy of the License at 39290* 39291* http://www.apache.org/licenses/LICENSE-2.0 39292* 39293* Unless required by applicable law or agreed to in writing, 39294* software distributed under the License is distributed on an 39295* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39296* KIND, either express or implied. See the License for the 39297* specific language governing permissions and limitations 39298* under the License. 39299*/ 39300 39301 var getBarItemStyle = makeStyleMapper( 39302 [ 39303 ['fill', 'color'], 39304 ['stroke', 'borderColor'], 39305 ['lineWidth', 'borderWidth'], 39306 // Compatitable with 2 39307 ['stroke', 'barBorderColor'], 39308 ['lineWidth', 'barBorderWidth'], 39309 ['opacity'], 39310 ['shadowBlur'], 39311 ['shadowOffsetX'], 39312 ['shadowOffsetY'], 39313 ['shadowColor'] 39314 ] 39315 ); 39316 39317 var barItemStyle = { 39318 getBarItemStyle: function (excludes) { 39319 var style = getBarItemStyle(this, excludes); 39320 if (this.getBorderLineDash) { 39321 var lineDash = this.getBorderLineDash(); 39322 lineDash && (style.lineDash = lineDash); 39323 } 39324 return style; 39325 } 39326 }; 39327 39328 /* 39329* Licensed to the Apache Software Foundation (ASF) under one 39330* or more contributor license agreements. See the NOTICE file 39331* distributed with this work for additional information 39332* regarding copyright ownership. The ASF licenses this file 39333* to you under the Apache License, Version 2.0 (the 39334* "License"); you may not use this file except in compliance 39335* with the License. You may obtain a copy of the License at 39336* 39337* http://www.apache.org/licenses/LICENSE-2.0 39338* 39339* Unless required by applicable law or agreed to in writing, 39340* software distributed under the License is distributed on an 39341* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39342* KIND, either express or implied. See the License for the 39343* specific language governing permissions and limitations 39344* under the License. 39345*/ 39346 39347 var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'barBorderWidth']; 39348 39349// FIXME 39350// Just for compatible with ec2. 39351 extend(Model.prototype, barItemStyle); 39352 39353 extendChartView({ 39354 39355 type: 'bar', 39356 39357 render: function (seriesModel, ecModel, api) { 39358 this._updateDrawMode(seriesModel); 39359 39360 var coordinateSystemType = seriesModel.get('coordinateSystem'); 39361 39362 if (coordinateSystemType === 'cartesian2d' 39363 || coordinateSystemType === 'polar' 39364 ) { 39365 this._isLargeDraw 39366 ? this._renderLarge(seriesModel, ecModel, api) 39367 : this._renderNormal(seriesModel, ecModel, api); 39368 } 39369 else if (__DEV__) { 39370 console.warn('Only cartesian2d and polar supported for bar.'); 39371 } 39372 39373 return this.group; 39374 }, 39375 39376 incrementalPrepareRender: function (seriesModel, ecModel, api) { 39377 this._clear(); 39378 this._updateDrawMode(seriesModel); 39379 }, 39380 39381 incrementalRender: function (params, seriesModel, ecModel, api) { 39382 // Do not support progressive in normal mode. 39383 this._incrementalRenderLarge(params, seriesModel); 39384 }, 39385 39386 _updateDrawMode: function (seriesModel) { 39387 var isLargeDraw = seriesModel.pipelineContext.large; 39388 if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) { 39389 this._isLargeDraw = isLargeDraw; 39390 this._clear(); 39391 } 39392 }, 39393 39394 _renderNormal: function (seriesModel, ecModel, api) { 39395 var group = this.group; 39396 var data = seriesModel.getData(); 39397 var oldData = this._data; 39398 39399 var coord = seriesModel.coordinateSystem; 39400 var baseAxis = coord.getBaseAxis(); 39401 var isHorizontalOrRadial; 39402 39403 if (coord.type === 'cartesian2d') { 39404 isHorizontalOrRadial = baseAxis.isHorizontal(); 39405 } 39406 else if (coord.type === 'polar') { 39407 isHorizontalOrRadial = baseAxis.dim === 'angle'; 39408 } 39409 39410 var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null; 39411 39412 data.diff(oldData) 39413 .add(function (dataIndex) { 39414 if (!data.hasValue(dataIndex)) { 39415 return; 39416 } 39417 39418 var itemModel = data.getItemModel(dataIndex); 39419 var layout = getLayout[coord.type](data, dataIndex, itemModel); 39420 var el = elementCreator[coord.type]( 39421 data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel 39422 ); 39423 data.setItemGraphicEl(dataIndex, el); 39424 group.add(el); 39425 39426 updateStyle( 39427 el, data, dataIndex, itemModel, layout, 39428 seriesModel, isHorizontalOrRadial, coord.type === 'polar'
39429 ); 39430 }) 39431 .update(function (newIndex, oldIndex) { 39432 var el = oldData.getItemGraphicEl(oldIndex); 39433 39434 if (!data.hasValue(newIndex)) { 39435 group.remove(el); 39436 return; 39437 } 39438 39439 var itemModel = data.getItemModel(newIndex); 39440 var layout = getLayout[coord.type](data, newIndex, itemModel); 39441 39442 if (el) { 39443 updateProps(el, {shape: layout}, animationModel, newIndex); 39444 } 39445 else { 39446 el = elementCreator[coord.type]( 39447 data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true 39448 ); 39449 } 39450 39451 data.setItemGraphicEl(newIndex, el); 39452 // Add back 39453 group.add(el); 39454 39455 updateStyle( 39456 el, data, newIndex, itemModel, layout, 39457 seriesModel, isHorizontalOrRadial, coord.type === 'polar' 39458 ); 39459 }) 39460 .remove(function (dataIndex) { 39461 var el = oldData.getItemGraphicEl(dataIndex); 39462 if (coord.type === 'cartesian2d') { 39463 el && removeRect(dataIndex, animationModel, el); 39464 } 39465 else { 39466 el && removeSector(dataIndex, animationModel, el); 39467 } 39468 }) 39469 .execute(); 39470 39471 this._data = data; 39472 }, 39473 39474 _renderLarge: function (seriesModel, ecModel, api) { 39475 this._clear(); 39476 createLarge(seriesModel, this.group); 39477 }, 39478 39479 _incrementalRenderLarge: function (params, seriesModel) { 39480 createLarge(seriesModel, this.group, true); 39481 }, 39482 39483 dispose: noop, 39484 39485 remove: function (ecModel) { 39486 this._clear(ecModel); 39487 }, 39488 39489 _clear: function (ecModel) { 39490 var group = this.group; 39491 var data = this._data; 39492 if (ecModel && ecModel.get('animation') && data && !this._isLargeDraw) { 39493 data.eachItemGraphicEl(function (el) { 39494 if (el.type === 'sector') { 39495 removeSector(el.dataIndex, ecModel, el); 39496 } 39497 else { 39498 removeRect(el.dataIndex, ecModel, el); 39499 } 39500 }); 39501 } 39502 else { 39503 group.removeAll(); 39504 } 39505 this._data = null; 39506 } 39507 39508 }); 39509 39510 var elementCreator = { 39511 39512 cartesian2d: function ( 39513 data, dataIndex, itemModel, layout, isHorizontal, 39514 animationModel, isUpdate 39515 ) { 39516 var rect = new Rect({shape: extend({}, layout)}); 39517 39518 // Animation 39519 if (animationModel) { 39520 var rectShape = rect.shape; 39521 var animateProperty = isHorizontal ? 'height' : 'width'; 39522 var animateTarget = {}; 39523 rectShape[animateProperty] = 0; 39524 animateTarget[animateProperty] = layout[animateProperty]; 39525 graphic[isUpdate ? 'updateProps' : 'initProps'](rect, { 39526 shape: animateTarget 39527 }, animationModel, dataIndex); 39528 } 39529 39530 return rect; 39531 }, 39532 39533 polar: function ( 39534 data, dataIndex, itemModel, layout, isRadial, 39535 animationModel, isUpdate 39536 ) { 39537 // Keep the same logic with bar in catesion: use end value to control 39538 // direction. Notice that if clockwise is true (by default), the sector 39539 // will always draw clockwisely, no matter whether endAngle is greater 39540 // or less than startAngle. 39541 var clockwise = layout.startAngle < layout.endAngle; 39542 var sector = new Sector({ 39543 shape: defaults({clockwise: clockwise}, layout) 39544 }); 39545 39546 // Animation 39547 if (animationModel) { 39548 var sectorShape = sector.shape; 39549 var animateProperty = isRadial ? 'r' : 'endAngle'; 39550 var animateTarget = {}; 39551 sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle; 39552 animateTarget[animateProperty] = layout[animateProperty]; 39553 graphic[isUpdate ? 'updateProps' : 'initProps'](sector, { 39554 shape: animateTarget 39555 }, animationModel, dataIndex); 39556 } 39557 39558 return sector; 39559 } 39560 }; 39561 39562 function removeRect(dataIndex, animationModel, el) { 39563 // Not show text when animating 39564 el.style.text = null; 39565 updateProps(el, { 39566 shape: { 39567 width: 0 39568 } 39569 }, animationModel, dataIndex, function () { 39570 el.parent && el.parent.remove(el); 39571 }); 39572 } 39573 39574 function removeSector(dataIndex, animationModel, el) { 39575 // Not show text when animating 39576 el.style.text = null; 39577 updateProps(el, { 39578 shape: { 39579 r: el.shape.r0 39580 } 39581 }, animationModel, dataIndex, function () { 39582 el.parent && el.parent.remove(el); 39583 }); 39584 } 39585 39586 var getLayout = { 39587 cartesian2d: function (data, dataIndex, itemModel) { 39588 var layout = data.getItemLayout(dataIndex);
39589 var fixedLineWidth = getLineWidth(itemModel, layout); 39590 39591 // fix layout with lineWidth 39592 var signX = layout.width > 0 ? 1 : -1; 39593 var signY = layout.height > 0 ? 1 : -1; 39594 return { 39595 x: layout.x + signX * fixedLineWidth / 2, 39596 y: layout.y + signY * fixedLineWidth / 2, 39597 width: layout.width - signX * fixedLineWidth, 39598 height: layout.height - signY * fixedLineWidth 39599 }; 39600 }, 39601 39602 polar: function (data, dataIndex, itemModel) { 39603 var layout = data.getItemLayout(dataIndex); 39604 return { 39605 cx: layout.cx, 39606 cy: layout.cy, 39607 r0: layout.r0, 39608 r: layout.r, 39609 startAngle: layout.startAngle, 39610 endAngle: layout.endAngle 39611 }; 39612 } 39613 }; 39614 39615 function updateStyle( 39616 el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar 39617 ) { 39618 var color = data.getItemVisual(dataIndex, 'color'); 39619 var opacity = data.getItemVisual(dataIndex, 'opacity'); 39620 var itemStyleModel = itemModel.getModel('itemStyle'); 39621 var hoverStyle = itemModel.getModel('emphasis.itemStyle').getBarItemStyle(); 39622 39623 if (!isPolar) { 39624 el.setShape('r', itemStyleModel.get('barBorderRadius') || 0); 39625 } 39626 39627 el.useStyle(defaults( 39628 { 39629 fill: color, 39630 opacity: opacity 39631 }, 39632 itemStyleModel.getBarItemStyle() 39633 )); 39634 39635 var cursorStyle = itemModel.getShallow('cursor'); 39636 cursorStyle && el.attr('cursor', cursorStyle); 39637 39638 var labelPositionOutside = isHorizontal 39639 ? (layout.height > 0 ? 'bottom' : 'top') 39640 : (layout.width > 0 ? 'left' : 'right'); 39641 39642 if (!isPolar) { 39643 setLabel( 39644 el.style, hoverStyle, itemModel, color, 39645 seriesModel, dataIndex, labelPositionOutside 39646 ); 39647 } 39648 39649 setHoverStyle(el, hoverStyle); 39650 } 39651 39652// In case width or height are too small. 39653 function getLineWidth(itemModel, rawLayout) { 39654 var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0; 39655 return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height)); 39656 } 39657 39658 39659 var LargePath = Path.extend({ 39660 39661 type: 'largeBar', 39662 39663 shape: {points: []}, 39664 39665 buildPath: function (ctx, shape) { 39666 // Drawing lines is more efficient than drawing 39667 // a whole line or drawing rects. 39668 var points = shape.points; 39669 var startPoint = this.__startPoint; 39670 var valueIdx = this.__valueIdx; 39671 39672 for (var i = 0; i < points.length; i += 2) { 39673 startPoint[this.__valueIdx] = points[i + valueIdx]; 39674 ctx.moveTo(startPoint[0], startPoint[1]); 39675 ctx.lineTo(points[i], points[i + 1]); 39676 } 39677 } 39678 }); 39679 39680 function createLarge(seriesModel, group, incremental) { 39681 // TODO support polar 39682 var data = seriesModel.getData(); 39683 var startPoint = []; 39684 var valueIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0; 39685 startPoint[1 - valueIdx] = data.getLayout('valueAxisStart'); 39686 39687 var el = new LargePath({ 39688 shape: {points: data.getLayout('largePoints')}, 39689 incremental: !!incremental, 39690 __startPoint: startPoint, 39691 __valueIdx: valueIdx 39692 }); 39693 group.add(el); 39694 setLargeStyle(el, seriesModel, data); 39695 } 39696 39697 function setLargeStyle(el, seriesModel, data) { 39698 var borderColor = data.getVisual('borderColor') || data.getVisual('color'); 39699 var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(['color', 'borderColor']); 39700 39701 el.useStyle(itemStyle); 39702 el.style.fill = null; 39703 el.style.stroke = borderColor; 39704 el.style.lineWidth = data.getLayout('barWidth'); 39705 } 39706 39707 /* 39708* Licensed to the Apache Software Foundation (ASF) under one 39709* or more contributor license agreements. See the NOTICE file 39710* distributed with this work for additional information 39711* regarding copyright ownership. The ASF licenses this file 39712* to you under the Apache License, Version 2.0 (the 39713* "License");
39713 you may not use this file except in compliance 39714* with the License. You may obtain a copy of the License at 39715* 39716* http://www.apache.org/licenses/LICENSE-2.0 39717* 39718* Unless required by applicable law or agreed to in writing, 39719* software distributed under the License is distributed on an 39720* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39721* KIND, either express or implied. See the License for the 39722* specific language governing permissions and limitations 39723* under the License. 39724*/ 39725 39726// In case developer forget to include grid component 39727 registerLayout(curry(layout, 'bar')); 39728// Should after normal bar layout, otherwise it is blocked by normal bar layout. 39729 registerLayout(largeLayout); 39730 39731 registerVisual({ 39732 seriesType: 'bar', 39733 reset: function (seriesModel) { 39734 // Visual coding for legend 39735 seriesModel.getData().setVisual('legendSymbol', 'roundRect'); 39736 } 39737 }); 39738 39739 /* 39740* Licensed to the Apache Software Foundation (ASF) under one 39741* or more contributor license agreements. See the NOTICE file 39742* distributed with this work for additional information 39743* regarding copyright ownership. The ASF licenses this file 39744* to you under the Apache License, Version 2.0 (the 39745* "License"); you may not use this file except in compliance 39746* with the License. You may obtain a copy of the License at 39747* 39748* http://www.apache.org/licenses/LICENSE-2.0 39749* 39750* Unless required by applicable law or agreed to in writing, 39751* software distributed under the License is distributed on an 39752* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39753* KIND, either express or implied. See the License for the 39754* specific language governing permissions and limitations 39755* under the License. 39756*/ 39757 39758 39759 /** 39760 * [Usage]: 39761 * (1) 39762 * createListSimply(seriesModel, ['value']); 39763 * (2) 39764 * createListSimply(seriesModel, { 39765 * coordDimensions: ['value'], 39766 * dimensionsCount: 5 39767 * }); 39768 * 39769 * @param {module:echarts/model/Series} seriesModel 39770 * @param {Object|Array.<string|Object>} opt opt or coordDimensions 39771 * The options in opt, see `echarts/data/helper/createDimensions` 39772 * @param {Array.<string>} [nameList] 39773 * @return {module:echarts/data/List} 39774 */ 39775 var createListSimply = function (seriesModel, opt, nameList) { 39776 opt = isArray(opt) && {coordDimensions: opt} || extend({}, opt); 39777 39778 var source = seriesModel.getSource(); 39779 39780 var dimensionsInfo = createDimensions(source, opt); 39781 39782 var list = new List(dimensionsInfo, seriesModel); 39783 list.initData(source, nameList); 39784 39785 return list; 39786 }; 39787 39788 /* 39789* Licensed to the Apache Software Foundation (ASF) under one 39790* or more contributor license agreements. See the NOTICE file 39791* distributed with this work for additional information 39792* regarding copyright ownership. The ASF licenses this file 39793* to you under the Apache License, Version 2.0 (the 39794* "License"); you may not use this file except in compliance 39795* with the License. You may obtain a copy of the License at 39796* 39797* http://www.apache.org/licenses/LICENSE-2.0 39798* 39799* Unless required by applicable law or agreed to in writing, 39800* software distributed under the License is distributed on an 39801* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39802* KIND, either express or implied. See the License for the 39803* specific language governing permissions and limitations 39804* under the License. 39805*/ 39806 39807 /** 39808 * Data selectable mixin for chart series. 39809 * To eanble data select, option of series must have `selectedMode`. 39810 * And each data item will use `selected` to toggle itself selected status 39811 */ 39812 39813 var selectableMixin = { 39814 39815 /** 39816 * @param {Array.<Object>} targetList [{name, value, selected}, ...] 39817 * If targetList is an array, it should like [{name: ..., value: ...}, ...]. 39818 * If targetList is a "List", it must have coordDim: 'value' dimension and name. 39819 */ 39820 updateSelectedMap: function (targetList) { 39821 this._targetList = isArray(targetList) ? targetList.slice() : []; 39822 39823 this._selectTargetMap = reduce(targetList || [], function (targetMap, target) { 39824 targetMap.set(target.name, target); 39825 return targetMap;
39826 }, createHashMap()); 39827 }, 39828 39829 /** 39830 * Either name or id should be passed as input here. 39831 * If both of them are defined, id is used. 39832 * 39833 * @param {string|undefined} name name of data 39834 * @param {number|undefined} id dataIndex of data 39835 */ 39836 // PENGING If selectedMode is null ? 39837 select: function (name, id) { 39838 var target = id != null 39839 ? this._targetList[id] 39840 : this._selectTargetMap.get(name); 39841 var selectedMode = this.get('selectedMode'); 39842 if (selectedMode === 'single') { 39843 this._selectTargetMap.each(function (target) { 39844 target.selected = false; 39845 }); 39846 } 39847 target && (target.selected = true); 39848 }, 39849 39850 /** 39851 * Either name or id should be passed as input here. 39852 * If both of them are defined, id is used. 39853 * 39854 * @param {string|undefined} name name of data 39855 * @param {number|undefined} id dataIndex of data 39856 */ 39857 unSelect: function (name, id) { 39858 var target = id != null 39859 ? this._targetList[id] 39860 : this._selectTargetMap.get(name); 39861 // var selectedMode = this.get('selectedMode'); 39862 // selectedMode !== 'single' && target && (target.selected = false); 39863 target && (target.selected = false); 39864 }, 39865 39866 /** 39867 * Either name or id should be passed as input here. 39868 * If both of them are defined, id is used. 39869 * 39870 * @param {string|undefined} name name of data 39871 * @param {number|undefined} id dataIndex of data 39872 */ 39873 toggleSelected: function (name, id) { 39874 var target = id != null 39875 ? this._targetList[id] 39876 : this._selectTargetMap.get(name); 39877 if (target != null) { 39878 this[target.selected ? 'unSelect' : 'select'](name, id); 39879 return target.selected; 39880 } 39881 }, 39882 39883 /** 39884 * Either name or id should be passed as input here. 39885 * If both of them are defined, id is used. 39886 * 39887 * @param {string|undefined} name name of data 39888 * @param {number|undefined} id dataIndex of data 39889 */ 39890 isSelected: function (name, id) { 39891 var target = id != null 39892 ? this._targetList[id] 39893 : this._selectTargetMap.get(name); 39894 return target && target.selected; 39895 } 39896 }; 39897 39898 /* 39899* Licensed to the Apache Software Foundation (ASF) under one 39900* or more contributor license agreements. See the NOTICE file 39901* distributed with this work for additional information 39902* regarding copyright ownership. The ASF licenses this file 39903* to you under the Apache License, Version 2.0 (the 39904* "License"); you may not use this file except in compliance 39905* with the License. You may obtain a copy of the License at 39906* 39907* http://www.apache.org/licenses/LICENSE-2.0 39908* 39909* Unless required by applicable law or agreed to in writing, 39910* software distributed under the License is distributed on an 39911* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 39912* KIND, either express or implied. See the License for the 39913* specific language governing permissions and limitations 39914* under the License. 39915*/ 39916 39917 var PieSeries = extendSeriesModel({ 39918 39919 type: 'series.pie', 39920 39921 // Overwrite 39922 init: function (option) { 39923 PieSeries.superApply(this, 'init', arguments); 39924 39925 // Enable legend selection for each data item 39926 // Use a function instead of direct access because data reference may changed 39927 this.legendDataProvider = function () { 39928 return this.getRawData(); 39929 }; 39930 39931 this.updateSelectedMap(this._createSelectableList()); 39932 39933 this._defaultLabelLine(option); 39934 }, 39935 39936 // Overwrite 39937 mergeOption: function (newOption) { 39938 PieSeries.superCall(this, 'mergeOption', newOption); 39939
39940 this.updateSelectedMap(this._createSelectableList()); 39941 }, 39942 39943 getInitialData: function (option, ecModel) { 39944 return createListSimply(this, ['value']); 39945 }, 39946 39947 _createSelectableList: function () { 39948 var data = this.getRawData(); 39949 var valueDim = data.mapDimension('value'); 39950 var targetList = []; 39951 for (var i = 0, len = data.count(); i < len; i++) { 39952 targetList.push({ 39953 name: data.getName(i), 39954 value: data.get(valueDim, i), 39955 selected: retrieveRawAttr(data, i, 'selected') 39956 }); 39957 } 39958 return targetList; 39959 }, 39960 39961 // Overwrite 39962 getDataParams: function (dataIndex) { 39963 var data = this.getData(); 39964 var params = PieSeries.superCall(this, 'getDataParams', dataIndex); 39965 // FIXME toFixed? 39966 39967 var valueList = []; 39968 data.each(data.mapDimension('value'), function (value) { 39969 valueList.push(value); 39970 }); 39971 39972 params.percent = getPercentWithPrecision( 39973 valueList, 39974 dataIndex, 39975 data.hostModel.get('percentPrecision') 39976 ); 39977 39978 params.$vars.push('percent'); 39979 return params; 39980 }, 39981 39982 _defaultLabelLine: function (option) { 39983 // Extend labelLine emphasis 39984 defaultEmphasis(option, 'labelLine', ['show']); 39985 39986 var labelLineNormalOpt = option.labelLine; 39987 var labelLineEmphasisOpt = option.emphasis.labelLine; 39988 // Not show label line if `label.normal.show = false` 39989 labelLineNormalOpt.show = labelLineNormalOpt.show 39990 && option.label.show; 39991 labelLineEmphasisOpt.show = labelLineEmphasisOpt.show 39992 && option.emphasis.label.show; 39993 }, 39994 39995 defaultOption: { 39996 zlevel: 0, 39997 z: 2, 39998 legendHoverLink: true, 39999 40000 hoverAnimation: true, 40001 // é»Ë认åâ¦Â¨Ã¥Â±â¬Ã¥Â±â¦Ã¤Â¸Â 40002 center: ['50%', '50%'], 40003 radius: [0, '75%'], 40004 // é»Ë认顺æâ¶éâË 40005 clockwise: true, 40006 startAngle: 90, 40007 // æÅâ¬Ã¥Â°Âè§â度æâ¹ä¸º0 40008 minAngle: 0, 40009 // éâ¬â°Ã¤Â¸Âæâ¶æâ°â¡Ã¥ÅºåÂÂç§»éâ¡Â 40010 selectedOffset: 10, 40011 // é«Ë亮æâ°â¡Ã¥ÅºåÂÂç§»éâ¡Â 40012 hoverOffset: 10, 40013 40014 // If use strategy to avoid label overlapping 40015 avoidLabelOverlap: true, 40016 // éâ¬â°Ã¦â¹Â©Ã¦Â¨Â¡Ã¥Â¼Âï¼Åé»Ë认åâ¦Â³Ã©âÂï¼Åå¯éâ¬â°singleï¼Åmultiple 40017 // selectedMode: false, 40018 // Ã¥Ââä¸Âæ ¼å°â玫çâ°åâºÂ¾Ã¦Â¨Â¡Ã¥Â¼Âï¼Å'radius'ï¼ËåŠå¾âï¼Ⱐ| 'area'ï¼Ëé¢积ï¼Ⱐ40019 // roseType: null, 40020 40021 percentPrecision: 2, 40022 40023 // If still show when all data zero. 40024 stillShowZeroSum: true, 40025 40026 // cursor: null, 40027 40028 label: { 40029 // If rotate around circle 40030 rotate: false, 40031 show: true, 40032 // 'outer', 'inside', 'center' 40033 position: 'outer' 40034 // formatter: æ â¡Ã§Â¾æââ¡Ã¦Å¾ ¼å¼ÂÃ¥â¢Â¨Ã¯Â¼ÅÃ¥ÂÅTooltip.formatterï¼Åä¸Âæâ¯æÅÂå¼âæÂ¥åâºÅ¾Ã¨Â°Æ 40035 // é»Ë认使çâ¨åâ¦Â¨Ã¥Â±â¬Ã¦ââ¡Ã¦Å¾ ·å¼Âï¼Å详è§ÂTEXTSTYLE 40036 // distance: å½âposition为inneræâ¶æÅâ°Ã¦â¢Ëï¼Å为labelä½Âç½®å˰åÅâ å¿ÆÃ§Å¡âè·Â离与åÅâ åŠå¾â(环状åâºÂ¾Ã¤Â¸ÂºÃ¥â â¦Ã¥Â¤âåŠå¾âÃ¥âÅ)çšâæ¯âä¾â¹Ã§Â³Â»Ã¦â¢Â° 40037 }, 40038 // Enabled when label.normal.position is 'outer' 40039 labelLine: { 40040 show: true,
40041 // å¼â¢Ã¥Â¯Â¼Ã§ÂºÂ¿Ã¤Â¸Â¤Ã¦Â®ÂµÃ¤Â¸Âçšâ第ä¸â¬Ã¦Â®ÂµÃ©â¢Â¿Ã¥ÂºÂ¦ 40042 length: 15, 40043 // å¼â¢Ã¥Â¯Â¼Ã§ÂºÂ¿Ã¤Â¸Â¤Ã¦Â®ÂµÃ¤Â¸Âçšâ第äºÅ段éâ¢Â¿Ã¥ÂºÂ¦ 40044 length2: 15, 40045 smooth: false, 40046 lineStyle: { 40047 // color: Ã¥Ââå¼â, 40048 width: 1, 40049 type: 'solid' 40050 } 40051 }, 40052 itemStyle: { 40053 borderWidth: 1 40054 }, 40055 40056 // Animation type canbe expansion, scale 40057 animationType: 'expansion', 40058 40059 animationEasing: 'cubicOut' 40060 } 40061 }); 40062 40063 mixin(PieSeries, selectableMixin); 40064 40065 /* 40066* Licensed to the Apache Software Foundation (ASF) under one 40067* or more contributor license agreements. See the NOTICE file 40068* distributed with this work for additional information 40069* regarding copyright ownership. The ASF licenses this file 40070* to you under the Apache License, Version 2.0 (the 40071* "License"); you may not use this file except in compliance 40072* with the License. You may obtain a copy of the License at 40073* 40074* http://www.apache.org/licenses/LICENSE-2.0 40075* 40076* Unless required by applicable law or agreed to in writing, 40077* software distributed under the License is distributed on an 40078* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 40079* KIND, either express or implied. See the License for the 40080* specific language governing permissions and limitations 40081* under the License. 40082*/ 40083 40084 /** 40085 * @param {module:echarts/model/Series} seriesModel 40086 * @param {boolean} hasAnimation 40087 * @inner 40088 */ 40089 function updateDataSelected(uid, seriesModel, hasAnimation, api) { 40090 var data = seriesModel.getData(); 40091 var dataIndex = this.dataIndex; 40092 var name = data.getName(dataIndex); 40093 var selectedOffset = seriesModel.get('selectedOffset'); 40094 40095 api.dispatchAction({ 40096 type: 'pieToggleSelect', 40097 from: uid, 40098 name: name, 40099 seriesId: seriesModel.id 40100 }); 40101 40102 data.each(function (idx) { 40103 toggleItemSelected( 40104 data.getItemGraphicEl(idx), 40105 data.getItemLayout(idx), 40106 seriesModel.isSelected(data.getName(idx)), 40107 selectedOffset, 40108 hasAnimation 40109 ); 40110 }); 40111 } 40112 40113 /** 40114 * @param {module:zrender/graphic/Sector} el 40115 * @param {Object} layout 40116 * @param {boolean} isSelected 40117 * @param {number} selectedOffset 40118 * @param {boolean} hasAnimation 40119 * @inner 40120 */ 40121 function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) { 40122 var midAngle = (layout.startAngle + layout.endAngle) / 2; 40123 40124 var dx = Math.cos(midAngle); 40125 var dy = Math.sin(midAngle); 40126 40127 var offset = isSelected ? selectedOffset : 0; 40128 var position = [dx * offset, dy * offset]; 40129 40130 hasAnimation 40131 // animateTo will stop revious animation like update transition 40132 ? el.animate() 40133 .when(200, { 40134 position: position 40135 }) 40136 .start('bounceOut') 40137 : el.attr('position', position); 40138 } 40139 40140 /** 40141 * Piece of pie including Sector, Label, LabelLine 40142 * @constructor 40143 * @extends {module:zrender/graphic/Group} 40144 */ 40145 function PiePiece(data, idx) { 40146 40147 Group.call(this); 40148 40149 var sector = new Sector({ 40150 z2: 2 40151 }); 40152 var polyline = new Polyline(); 40153 var text = new Text(); 40154 this.add(sector); 40155 this.add(polyline); 40156 this.add(text); 40157 40158 this.updateData(data, idx, true); 40159 40160 // Hover to change label and labelLine 40161 function onEmphasis() { 40162 polyline.ignore = polyline.hoverIgnore; 40163 text.ignore = text.hoverIgnore; 40164 } 40165 function onNormal() { 40166 polyline.ignore = polyline.normalIgnore; 40167 text.ignore = text.normalIgnore; 40168 } 40169 this.on('emphasis', onEmphasis) 40170 .on('normal', onNormal) 40171 .on('mouseover', onEmphasis) 40172 .on('mouseout', onNormal); 40173 } 40174 40175 var piePieceProto = PiePiece.prototype; 40176 40177 piePieceProto.updateData = function (data, idx, firstCreate) { 40178 40179 var sector = this.childAt(0); 40180 40181 var seriesModel = data.hostModel; 40182 var itemModel = data.getItemModel(idx); 40183 var layout = data.getItemLayout(idx); 40184 var sectorShape = extend({}, layout); 40185 sectorShape.label = null; 40186 40187 if (firstCreate) { 40188 sector.setShape(sectorShape); 40189 40190 var animationType = seriesModel.getShallow('animationType'); 40191 if (animationType === 'scale') { 40192 sector.shape.r = layout.r0; 40193 initProps(sector, { 40194 shape: { 40195 r: layout.r 40196 } 40197 }, seriesModel, idx); 40198 } 40199 // Expansion 40200 else { 40201 sector.shape.endAngle = layout.startAngle; 40202 updateProps(sector, { 40203 shape: { 40204 endAngle: layout.endAngle 40205 } 40206 }, seriesModel, idx); 40207 } 40208 40209 } 40210 else { 40211 updateProps(sector, { 40212 shape: sectorShape 40213 }, seriesModel, idx); 40214 } 40215 40216 // Update common style 40217 var visualColor = data.getItemVisual(idx, 'color'); 40218 40219 sector.useStyle( 40220 defaults( 40221 { 40222 lineJoin: 'bevel', 40223 fill: visualColor 40224 }, 40225 itemModel.getModel('itemStyle').getItemStyle() 40226 ) 40227 ); 40228 sector.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); 40229 40230 var cursorStyle = itemModel.getShallow('cursor'); 40231 cursorStyle && sector.attr('cursor', cursorStyle); 40232 40233 // Toggle selected 40234 toggleItemSelected( 40235 this, 40236 data.getItemLayout(idx), 40237 seriesModel.isSelected(null, idx), 40238 seriesModel.get('selectedOffset'), 40239 seriesModel.get('animation') 40240 ); 40241 40242 function onEmphasis() { 40243 // Sector may has animation of updating data. Force to move to the last frame 40244 // Or it may stopped on the wrong shape 40245 sector.stopAnimation(true); 40246 sector.animateTo({ 40247 shape: { 40248 r: layout.r + seriesModel.get('hoverOffset') 40249 } 40250 }, 300, 'elasticOut'); 40251 } 40252 function onNormal() { 40253 sector.stopAnimation(true); 40254 sector.animateTo({ 40255 shape: { 40256 r: layout.r 40257 } 40258 }, 300, 'elasticOut'); 40259 } 40260 sector.off('mouseover').off('mouseout').off('emphasis').off('normal'); 40261 if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) { 40262 sector 40263 .on('mouseover', onEmphasis) 40264 .on('mouseout', onNormal) 40265 .on('emphasis', onEmphasis) 40266 .on('normal', onNormal); 40267 } 40268 40269 this._updateLabel(data, idx); 40270 40271 setHoverStyle(this); 40272 }; 40273 40274 piePieceProto._updateLabel = function (data, idx) { 40275 40276 var labelLine = this.childAt(1); 40277 var labelText = this.childAt(2); 40278 40279 var seriesModel = data.hostModel; 40280 var itemModel = data.getItemModel(idx); 40281 var layout = data.getItemLayout(idx); 40282 var labelLayout = layout.label; 40283 var visualColor = data.getItemVisual(idx, 'color'); 40284 40285 updateProps(labelLine, { 40286 shape: { 40287 points: labelLayout.linePoints || [ 40288 [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y] 40289 ] 40290 } 40291 }, seriesModel, idx); 40292 40293 updateProps(labelText, { 40294 style: { 40295 x: labelLayout.x, 40296 y: labelLayout.y 40297 } 40298 }, seriesModel, idx); 40299 labelText.attr({ 40300 rotation: labelLayout.rotation, 40301 origin: [labelLayout.x, labelLayout.y], 40302 z2: 10 40303 }); 40304 40305 var labelModel = itemModel.getModel('label'); 40306 var labelHoverModel = itemModel.getModel('emphasis.label'); 40307 var labelLineModel = itemModel.getModel('labelLine'); 40308 var labelLineHoverModel = itemModel.getModel('emphasis.labelLine'); 40309 var visualColor = data.getItemVisual(idx, 'color'); 40310 40311 setLabelStyle( 40312 labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel, 40313 { 40314 labelFetcher: data.hostModel,
40315 labelDataIndex: idx, 40316 defaultText: data.getName(idx), 40317 autoColor: visualColor, 40318 useInsideStyle: !!labelLayout.inside 40319 }, 40320 { 40321 textAlign: labelLayout.textAlign, 40322 textVerticalAlign: labelLayout.verticalAlign, 40323 opacity: data.getItemVisual(idx, 'opacity') 40324 } 40325 ); 40326 40327 labelText.ignore = labelText.normalIgnore = !labelModel.get('show'); 40328 labelText.hoverIgnore = !labelHoverModel.get('show'); 40329 40330 labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show'); 40331 labelLine.hoverIgnore = !labelLineHoverModel.get('show'); 40332 40333 // Default use item visual color 40334 labelLine.setStyle({ 40335 stroke: visualColor, 40336 opacity: data.getItemVisual(idx, 'opacity') 40337 }); 40338 labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle()); 40339 40340 labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); 40341 40342 var smooth = labelLineModel.get('smooth'); 40343 if (smooth && smooth === true) { 40344 smooth = 0.4; 40345 } 40346 labelLine.setShape({ 40347 smooth: smooth 40348 }); 40349 }; 40350 40351 inherits(PiePiece, Group); 40352 40353 40354// Pie view 40355 var PieView = Chart.extend({ 40356 40357 type: 'pie', 40358 40359 init: function () { 40360 var sectorGroup = new Group(); 40361 this._sectorGroup = sectorGroup; 40362 }, 40363 40364 render: function (seriesModel, ecModel, api, payload) { 40365 if (payload && (payload.from === this.uid)) { 40366 return; 40367 } 40368 40369 var data = seriesModel.getData(); 40370 var oldData = this._data; 40371 var group = this.group; 40372 40373 var hasAnimation = ecModel.get('animation'); 40374 var isFirstRender = !oldData; 40375 var animationType = seriesModel.get('animationType'); 40376 40377 var onSectorClick = curry( 40378 updateDataSelected, this.uid, seriesModel, hasAnimation, api 40379 ); 40380 40381 var selectedMode = seriesModel.get('selectedMode'); 40382 40383 data.diff(oldData) 40384 .add(function (idx) { 40385 var piePiece = new PiePiece(data, idx); 40386 // Default expansion animation 40387 if (isFirstRender && animationType !== 'scale') { 40388 piePiece.eachChild(function (child) { 40389 child.stopAnimation(true); 40390 }); 40391 } 40392 40393 selectedMode && piePiece.on('click', onSectorClick); 40394 40395 data.setItemGraphicEl(idx, piePiece); 40396 40397 group.add(piePiece); 40398 }) 40399 .update(function (newIdx, oldIdx) { 40400 var piePiece = oldData.getItemGraphicEl(oldIdx); 40401 40402 piePiece.updateData(data, newIdx); 40403 40404 piePiece.off('click'); 40405 selectedMode && piePiece.on('click', onSectorClick); 40406 group.add(piePiece); 40407 data.setItemGraphicEl(newIdx, piePiece); 40408 }) 40409 .remove(function (idx) { 40410 var piePiece = oldData.getItemGraphicEl(idx); 40411 group.remove(piePiece); 40412 }) 40413 .execute(); 40414 40415 if ( 40416 hasAnimation && isFirstRender && data.count() > 0 40417 // Default expansion animation 40418 && animationType !== 'scale' 40419 ) { 40420 var shape = data.getItemLayout(0); 40421 var r = Math.max(api.getWidth(), api.getHeight()) / 2; 40422 40423 var removeClipPath = bind(group.removeClipPath, group); 40424 group.setClipPath(this._createClipPath( 40425 shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel 40426 )); 40427 } 40428 40429 this._data = data; 40430 }, 40431 40432 dispose: function () {}, 40433 40434 _createClipPath: function ( 40435 cx, cy, r, startAngle, clockwise, cb, seriesModel 40436 ) { 40437 var clipPath = new Sector({ 40438 shape: { 40439 cx: cx, 40440 cy: cy, 40441 r0: 0, 40442 r: r, 40443 startAngle: startAngle, 40444 endAngle: startAngle, 40445 clockwise: clockwise 40446 } 40447 }); 40448 40449 initProps(clipPath, { 40450 shape: { 40451 endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2 40452 } 40453 }, seriesModel, cb); 40454 40455 return clipPath; 40456 }, 40457 40458 /** 40459 * @implement 40460 */ 40461 containPoint: function (point, seriesModel) { 40462 var data = seriesModel.getData(); 40463 var itemLayout = data.getItemLayout(0); 40464 if (itemLayout) { 40465 var dx = point[0] - itemLayout.cx; 40466 var dy = point[1] - itemLayout.cy; 40467 var radius = Math.sqrt(dx * dx + dy * dy);
40468 return radius <= itemLayout.r && radius >= itemLayout.r0; 40469 } 40470 } 40471 40472 }); 40473 40474 /* 40475* Licensed to the Apache Software Foundation (ASF) under one 40476* or more contributor license agreements. See the NOTICE file 40477* distributed with this work for additional information 40478* regarding copyright ownership. The ASF licenses this file 40479* to you under the Apache License, Version 2.0 (the 40480* "License"); you may not use this file except in compliance 40481* with the License. You may obtain a copy of the License at 40482* 40483* http://www.apache.org/licenses/LICENSE-2.0 40484* 40485* Unless required by applicable law or agreed to in writing, 40486* software distributed under the License is distributed on an 40487* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 40488* KIND, either express or implied. See the License for the 40489* specific language governing permissions and limitations 40490* under the License. 40491*/ 40492 40493 var createDataSelectAction = function (seriesType, actionInfos) { 40494 each$1(actionInfos, function (actionInfo) { 40495 actionInfo.update = 'updateView'; 40496 /** 40497 * @payload 40498 * @property {string} seriesName 40499 * @property {string} name 40500 */ 40501 registerAction(actionInfo, function (payload, ecModel) { 40502 var selected = {}; 40503 ecModel.eachComponent( 40504 {mainType: 'series', subType: seriesType, query: payload}, 40505 function (seriesModel) { 40506 if (seriesModel[actionInfo.method]) { 40507 seriesModel[actionInfo.method]( 40508 payload.name, 40509 payload.dataIndex 40510 ); 40511 } 40512 var data = seriesModel.getData(); 40513 // Create selected map 40514 data.each(function (idx) { 40515 var name = data.getName(idx); 40516 selected[name] = seriesModel.isSelected(name) 40517 || false; 40518 }); 40519 } 40520 ); 40521 return { 40522 name: payload.name, 40523 selected: selected 40524 }; 40525 }); 40526 }); 40527 }; 40528 40529 /* 40530* Licensed to the Apache Software Foundation (ASF) under one 40531* or more contributor license agreements. See the NOTICE file 40532* distributed with this work for additional information 40533* regarding copyright ownership. The ASF licenses this file 40534* to you under the Apache License, Version 2.0 (the 40535* "License"); you may not use this file except in compliance 40536* with the License. You may obtain a copy of the License at 40537* 40538* http://www.apache.org/licenses/LICENSE-2.0 40539* 40540* Unless required by applicable law or agreed to in writing, 40541* software distributed under the License is distributed on an 40542* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 40543* KIND, either express or implied. See the License for the 40544* specific language governing permissions and limitations 40545* under the License. 40546*/ 40547 40548// Pick color from palette for each data item. 40549// Applicable for charts that require applying color palette 40550// in data level (like pie, funnel, chord). 40551 var dataColor = function (seriesType) { 40552 return { 40553 getTargetSeries: function (ecModel) { 40554 // Pie and funnel may use diferrent scope 40555 var paletteScope = {}; 40556 var seiresModelMap = createHashMap(); 40557 40558 ecModel.eachSeriesByType(seriesType, function (seriesModel) { 40559 seriesModel.__paletteScope = paletteScope; 40560 seiresModelMap.set(seriesModel.uid, seriesModel); 40561 }); 40562 40563 return seiresModelMap; 40564 }, 40565 reset: function (seriesModel, ecModel) { 40566 var dataAll = seriesModel.getRawData(); 40567 var idxMap = {}; 40568 var data = seriesModel.getData(); 40569 40570 data.each(function (idx) { 40571 var rawIdx = data.getRawIndex(idx); 40572 idxMap[rawIdx] = idx; 40573 }); 40574 40575 dataAll.each(function (rawIdx) { 40576 var filteredIdx = idxMap[rawIdx]; 40577 40578 // If series.itemStyle.normal.color is a function. itemVisual may be encoded 40579 var singleDataColor = filteredIdx != null 40580 && data.getItemVisual(filteredIdx, 'color', true); 40581 40582 if (!singleDataColor) { 40583 // FIXME Performance 40584 var itemModel = dataAll.getItemModel(rawIdx); 40585 40586 var color = itemModel.get('itemStyle.color') 40587 || seriesModel.getColorFromPalette( 40588 dataAll.getName(rawIdx) || (rawIdx + ''), seriesModel.__paletteScope, 40589 dataAll.count() 40590 ); 40591 // Legend may use the visual info in data before processed 40592 dataAll.setItemVisual(rawIdx, 'color', color); 40593 40594 // Data is not filtered 40595 if (filteredIdx != null) { 40596 data.setItemVisual(filteredIdx, 'color', color); 40597 } 40598 } 40599 else { 40600 // Set data all color for legend 40601 dataAll.setItemVisual(rawIdx, 'color', singleDataColor); 40602 } 40603 }); 40604 } 40605 }; 40606 }; 40607 40608 /* 40609* Licensed to the Apache Software Foundation (ASF) under one 40610* or more contributor license agreements. See the NOTICE file 40611* distributed with this work for additional information 40612* regarding copyright ownership. The ASF licenses this file 40613* to you under the Apache License, Version 2.0 (the 40614* "License");
40614 you may not use this file except in compliance 40615* with the License. You may obtain a copy of the License at 40616* 40617* http://www.apache.org/licenses/LICENSE-2.0 40618* 40619* Unless required by applicable law or agreed to in writing, 40620* software distributed under the License is distributed on an 40621* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 40622* KIND, either express or implied. See the License for the 40623* specific language governing permissions and limitations 40624* under the License. 40625*/ 40626 40627// FIXME emphasis label position is not same with normal label position 40628 40629 function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { 40630 list.sort(function (a, b) { 40631 return a.y - b.y; 40632 }); 40633 40634 // åŽ⹠40635 function shiftDown(start, end, delta, dir) { 40636 for (var j = start; j < end; j++) { 40637 list[j].y += delta; 40638 if (j > start 40639 && j + 1 < end 40640 && list[j + 1].y > list[j].y + list[j].height 40641 ) { 40642 shiftUp(j, delta / 2); 40643 return; 40644 } 40645 } 40646 40647 shiftUp(end - 1, delta / 2); 40648 } 40649 40650 // å¼¹ 40651 function shiftUp(end, delta) { 40652 for (var j = end; j >= 0; j--) { 40653 list[j].y -= delta; 40654 if (j > 0 40655 && list[j].y > list[j - 1].y + list[j - 1].height 40656 ) { 40657 break; 40658 } 40659 } 40660 } 40661 40662 function changeX(list, isDownList, cx, cy, r, dir) { 40663 var lastDeltaX = dir > 0 40664 ? isDownList // å³侧 40665 ? Number.MAX_VALUE // ä¸⹠40666 : 0 // 上40667 : isDownList // 左侧 40668 ? Number.MAX_VALUE // ä¸⹠40669 : 0; // 上40670 40671 for (var i = 0, l = list.length; i < l; i++) { 40672 // Not change x for center label 40673 if (list[i].position === 'center') { 40674 continue; 40675 } 40676 var deltaY = Math.abs(list[i].y - cy); 40677 var length = list[i].len; 40678 var length2 = list[i].len2; 40679 var deltaX = (deltaY < r + length) 40680 ? Math.sqrt( 40681 (r + length + length2) * (r + length + length2) 40682 - deltaY * deltaY 40683 ) 40684 : Math.abs(list[i].x - cx); 40685 if (isDownList && deltaX >= lastDeltaX) { 40686 // å³ä¸â¹Ã¯Â¼Åå·¦ä¸⹠40687 deltaX = lastDeltaX - 10; 40688 } 40689 if (!isDownList && deltaX <= lastDeltaX) { 40690 // å³上ï¼Å左上40691 deltaX = lastDeltaX + 10; 40692 } 40693 40694 list[i].x = cx + deltaX * dir; 40695 lastDeltaX = deltaX; 40696 } 40697 } 40698 40699 var lastY = 0; 40700 var delta; 40701 var len = list.length; 40702 var upList = []; 40703 var downList = []; 40704 for (var i = 0; i < len; i++) { 40705 delta = list[i].y - lastY; 40706 if (delta < 0) { 40707 shiftDown(i, len, -delta, dir); 40708 } 40709 lastY = list[i].y + list[i].height; 40710 } 40711 if (viewHeight - lastY < 0) { 40712 shiftUp(len - 1, lastY - viewHeight); 40713 } 40714 for (var i = 0; i < len; i++) { 40715 if (list[i].y >= cy) { 40716 downList.push(list[i]); 40717 } 40718 else { 40719 upList.push(list[i]); 40720 } 40721 } 40722 changeX(upList, false, cx, cy, r, dir); 40723 changeX(downList, true, cx, cy, r, dir); 40724 } 40725 40726 function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) { 40727 var leftList = []; 40728 var rightList = []; 40729 for (var i = 0; i < labelLayoutList.length; i++) { 40730 if (labelLayoutList[i].x < cx) { 40731 leftList.push(labelLayoutList[i]); 40732 } 40733 else { 40734 rightList.push(labelLayoutList[i]); 40735 } 40736 } 40737 40738 adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight); 40739 adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight); 40740 40741 for (var i = 0; i < labelLayoutList.length; i++) { 40742 var linePoints = labelLayoutList[i].linePoints; 40743 if (linePoints) { 40744 var dist = linePoints[1][0] - linePoints[2][0]; 40745 if (labelLayoutList[i].x < cx) { 40746 linePoints[2][0] = labelLayoutList[i].x + 3; 40747 } 40748 else { 40749 linePoints[2][0] = labelLayoutList[i].x - 3; 40750 } 40751 linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y; 40752 linePoints[1][0] = linePoints[2][0] + dist; 40753 } 40754 } 40755 } 40756 40757 var labelLayout = function (seriesModel, r, viewWidth, viewHeight) { 40758 var data = seriesModel.getData(); 40759 var labelLayoutList = []; 40760 var cx; 40761 var cy; 40762 var hasLabelRotate = false; 40763 40764 data.each(function (idx) { 40765 var layout = data.getItemLayout(idx); 40766 40767 var itemModel = data.getItemModel(idx); 40768 var labelModel = itemModel.getModel('label'); 40769 // Use position in normal or emphasis 40770 var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position'); 40771 40772 var labelLineModel = itemModel.getModel('labelLine'); 40773 var labelLineLen = labelLineModel.get('length'); 40774 var labelLineLen2 = labelLineModel.get('length2'); 40775 40776 var midAngle = (layout.startAngle + layout.endAngle) / 2;
40777 var dx = Math.cos(midAngle); 40778 var dy = Math.sin(midAngle); 40779 40780 var textX; 40781 var textY; 40782 var linePoints; 40783 var textAlign; 40784 40785 cx = layout.cx; 40786 cy = layout.cy; 40787 40788 var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; 40789 if (labelPosition === 'center') { 40790 textX = layout.cx; 40791 textY = layout.cy; 40792 textAlign = 'center'; 40793 } 40794 else { 40795 var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx; 40796 var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy; 40797 40798 textX = x1 + dx * 3; 40799 textY = y1 + dy * 3; 40800 40801 if (!isLabelInside) { 40802 // For roseType 40803 var x2 = x1 + dx * (labelLineLen + r - layout.r); 40804 var y2 = y1 + dy * (labelLineLen + r - layout.r); 40805 var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2); 40806 var y3 = y2; 40807 40808 textX = x3 + (dx < 0 ? -5 : 5); 40809 textY = y3; 40810 linePoints = [[x1, y1], [x2, y2], [x3, y3]]; 40811 } 40812 40813 textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right'); 40814 } 40815 var font = labelModel.getFont(); 40816 40817 var labelRotate = labelModel.get('rotate') 40818 ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0; 40819 var text = seriesModel.getFormattedLabel(idx, 'normal') 40820 || data.getName(idx); 40821 var textRect = getBoundingRect( 40822 text, font, textAlign, 'top' 40823 ); 40824 hasLabelRotate = !!labelRotate; 40825 layout.label = { 40826 x: textX, 40827 y: textY, 40828 position: labelPosition, 40829 height: textRect.height, 40830 len: labelLineLen, 40831 len2: labelLineLen2, 40832 linePoints: linePoints, 40833 textAlign: textAlign, 40834 verticalAlign: 'middle', 40835 rotation: labelRotate, 40836 inside: isLabelInside 40837 }; 40838 40839 // Not layout the inside label 40840 if (!isLabelInside) { 40841 labelLayoutList.push(layout.label); 40842 } 40843 }); 40844 if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { 40845 avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight); 40846 } 40847 }; 40848 40849 /* 40850* Licensed to the Apache Software Foundation (ASF) under one 40851* or more contributor license agreements. See the NOTICE file 40852* distributed with this work for additional information 40853* regarding copyright ownership. The ASF licenses this file 40854* to you under the Apache License, Version 2.0 (the 40855* "License"); you may not use this file except in compliance 40856* with the License. You may obtain a copy of the License at 40857* 40858* http://www.apache.org/licenses/LICENSE-2.0 40859* 40860* Unless required by applicable law or agreed to in writing, 40861* software distributed under the License is distributed on an 40862* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 40863* KIND, either express or implied. See the License for the 40864* specific language governing permissions and limitations 40865* under the License. 40866*/ 40867 40868 40869 var PI2$4 = Math.PI * 2; 40870 var RADIAN = Math.PI / 180; 40871 40872 var pieLayout = function (seriesType, ecModel, api, payload) { 40873 ecModel.eachSeriesByType(seriesType, function (seriesModel) { 40874 var data = seriesModel.getData(); 40875 var valueDim = data.mapDimension('value'); 40876 40877 var center = seriesModel.get('center'); 40878 var radius = seriesModel.get('radius'); 40879 40880 if (!isArray(radius)) { 40881 radius = [0, radius]; 40882 } 40883 if (!isArray(center)) { 40884 center = [center, center]; 40885 } 40886 40887 var width = api.getWidth(); 40888 var height = api.getHeight(); 40889 var size = Math.min(width, height); 40890 var cx = parsePercent$1(center[0], width); 40891 var cy = parsePercent$1(center[1], height); 40892 var r0 = parsePercent$1(radius[0], size / 2); 40893 var r = parsePercent$1(radius[1], size / 2); 40894 40895 var startAngle = -seriesModel.get('startAngle') * RADIAN; 40896 40897 var minAngle = seriesModel.get('minAngle') * RADIAN; 40898
40899 var validDataCount = 0; 40900 data.each(valueDim, function (value) { 40901 !isNaN(value) && validDataCount++; 40902 }); 40903 40904 var sum = data.getSum(valueDim); 40905 // Sum may be 0 40906 var unitRadian = Math.PI / (sum || validDataCount) * 2; 40907 40908 var clockwise = seriesModel.get('clockwise'); 40909 40910 var roseType = seriesModel.get('roseType'); 40911 var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); 40912 40913 // [0...max] 40914 var extent = data.getDataExtent(valueDim); 40915 extent[0] = 0; 40916 40917 // In the case some sector angle is smaller than minAngle 40918 var restAngle = PI2$4; 40919 var valueSumLargerThanMinAngle = 0; 40920 40921 var currentAngle = startAngle; 40922 var dir = clockwise ? 1 : -1; 40923 40924 data.each(valueDim, function (value, idx) { 40925 var angle; 40926 if (isNaN(value)) { 40927 data.setItemLayout(idx, { 40928 angle: NaN, 40929 startAngle: NaN, 40930 endAngle: NaN, 40931 clockwise: clockwise, 40932 cx: cx, 40933 cy: cy, 40934 r0: r0, 40935 r: roseType 40936 ? NaN 40937 : r 40938 }); 40939 return; 40940 } 40941 40942 // FIXME Ã¥â¦Â¼Ã¥Â®Â¹ 2.0 ä½â æË¯ roseType æË¯ area çšâæâ¶åâ¬â¢Ã¦â°ÂæË¯è¿â¢Ã¦ ·ï¼Ÿ 40943 if (roseType !== 'area') { 40944 angle = (sum === 0 && stillShowZeroSum) 40945 ? unitRadian : (value * unitRadian); 40946 } 40947 else { 40948 angle = PI2$4 / validDataCount; 40949 } 40950 40951 if (angle < minAngle) { 40952 angle = minAngle; 40953 restAngle -= minAngle; 40954 } 40955 else { 40956 valueSumLargerThanMinAngle += value; 40957 } 40958 40959 var endAngle = currentAngle + dir * angle; 40960 data.setItemLayout(idx, { 40961 angle: angle, 40962 startAngle: currentAngle, 40963 endAngle: endAngle, 40964 clockwise: clockwise, 40965 cx: cx, 40966 cy: cy, 40967 r0: r0, 40968 r: roseType 40969 ? linearMap(value, extent, [r0, r]) 40970 : r 40971 }); 40972 40973 currentAngle = endAngle; 40974 }); 40975 40976 // Some sector is constrained by minAngle 40977 // Rest sectors needs recalculate angle 40978 if (restAngle < PI2$4 && validDataCount) { 40979 // Average the angle if rest angle is not enough after all angles is 40980 // Constrained by minAngle 40981 if (restAngle <= 1e-3) { 40982 var angle = PI2$4 / validDataCount; 40983 data.each(valueDim, function (value, idx) { 40984 if (!isNaN(value)) { 40985 var layout = data.getItemLayout(idx); 40986 layout.angle = angle; 40987 layout.startAngle = startAngle + dir * idx * angle; 40988 layout.endAngle = startAngle + dir * (idx + 1) * angle; 40989 } 40990 }); 40991 } 40992 else { 40993 unitRadian = restAngle / valueSumLargerThanMinAngle; 40994 currentAngle = startAngle; 40995 data.each(valueDim, function (value, idx) { 40996 if (!isNaN(value)) { 40997 var layout = data.getItemLayout(idx); 40998 var angle = layout.angle === minAngle 40999 ? minAngle : value * unitRadian; 41000 layout.startAngle = currentAngle; 41001 layout.endAngle = currentAngle + dir * angle; 41002 currentAngle += dir * angle; 41003 } 41004 }); 41005 } 41006 } 41007 41008 labelLayout(seriesModel, r, width, height); 41009 }); 41010 }; 41011 41012 /* 41013* Licensed to the Apache Software Foundation (ASF) under one 41014* or more contributor license agreements. See the NOTICE file 41015* distributed with this work for additional information 41016* regarding copyright ownership. The ASF licenses this file 41017* to you under the Apache License, Version 2.0 (the 41018* "License");
41018 you may not use this file except in compliance 41019* with the License. You may obtain a copy of the License at 41020* 41021* http://www.apache.org/licenses/LICENSE-2.0 41022* 41023* Unless required by applicable law or agreed to in writing, 41024* software distributed under the License is distributed on an 41025* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41026* KIND, either express or implied. See the License for the 41027* specific language governing permissions and limitations 41028* under the License. 41029*/ 41030 41031 var dataFilter = function (seriesType) { 41032 return { 41033 seriesType: seriesType, 41034 reset: function (seriesModel, ecModel) { 41035 var legendModels = ecModel.findComponents({ 41036 mainType: 'legend' 41037 }); 41038 if (!legendModels || !legendModels.length) { 41039 return; 41040 } 41041 var data = seriesModel.getData(); 41042 data.filterSelf(function (idx) { 41043 var name = data.getName(idx); 41044 // If in any legend component the status is not selected. 41045 for (var i = 0; i < legendModels.length; i++) { 41046 if (!legendModels[i].isSelected(name)) { 41047 return false; 41048 } 41049 } 41050 return true; 41051 }); 41052 } 41053 }; 41054 }; 41055 41056 /* 41057* Licensed to the Apache Software Foundation (ASF) under one 41058* or more contributor license agreements. See the NOTICE file 41059* distributed with this work for additional information 41060* regarding copyright ownership. The ASF licenses this file 41061* to you under the Apache License, Version 2.0 (the 41062* "License"); you may not use this file except in compliance 41063* with the License. You may obtain a copy of the License at 41064* 41065* http://www.apache.org/licenses/LICENSE-2.0 41066* 41067* Unless required by applicable law or agreed to in writing, 41068* software distributed under the License is distributed on an 41069* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41070* KIND, either express or implied. See the License for the 41071* specific language governing permissions and limitations 41072* under the License. 41073*/ 41074 41075 createDataSelectAction('pie', [{ 41076 type: 'pieToggleSelect', 41077 event: 'pieselectchanged', 41078 method: 'toggleSelected' 41079 }, { 41080 type: 'pieSelect', 41081 event: 'pieselected', 41082 method: 'select' 41083 }, { 41084 type: 'pieUnSelect', 41085 event: 'pieunselected', 41086 method: 'unSelect' 41087 }]); 41088 41089 registerVisual(dataColor('pie')); 41090 registerLayout(curry(pieLayout, 'pie')); 41091 registerProcessor(dataFilter('pie')); 41092 41093 /* 41094* Licensed to the Apache Software Foundation (ASF) under one 41095* or more contributor license agreements. See the NOTICE file 41096* distributed with this work for additional information 41097* regarding copyright ownership. The ASF licenses this file 41098* to you under the Apache License, Version 2.0 (the 41099* "License"); you may not use this file except in compliance 41100* with the License. You may obtain a copy of the License at 41101* 41102* http://www.apache.org/licenses/LICENSE-2.0 41103* 41104* Unless required by applicable law or agreed to in writing, 41105* software distributed under the License is distributed on an 41106* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41107* KIND, either express or implied. See the License for the 41108* specific language governing permissions and limitations 41109* under the License. 41110*/ 41111 41112 SeriesModel.extend({ 41113 41114 type: 'series.scatter', 41115 41116 dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'], 41117 41118 getInitialData: function (option, ecModel) { 41119 return createListFromArray(this.getSource(), this); 41120 }, 41121 41122 brushSelector: 'point', 41123 41124 getProgressive: function () { 41125 var progressive = this.option.progressive; 41126 if (progressive == null) { 41127 // PENDING 41128 return this.option.large ? 5e3 : this.get('progressive'); 41129 } 41130 return progressive; 41131 }, 41132 41133 getProgressiveThreshold: function () { 41134 var progressiveThreshold = this.option.progressiveThreshold; 41135 if (progressiveThreshold == null) { 41136 // PENDING 41137 return this.option.large ? 1e4 : this.get('progressiveThreshold'); 41138 } 41139 return progressiveThreshold; 41140 }, 41141 41142 defaultOption: { 41143 coordinateSystem: 'cartesian2d', 41144 zlevel: 0, 41145 z: 2, 41146 legendHoverLink: true, 41147 41148 hoverAnimation: true, 41149 // Cartesian coordinate system 41150 // xAxisIndex: 0, 41151 // yAxisIndex: 0, 41152 41153 // Polar coordinate system 41154 // polarIndex: 0, 41155 41156 // Geo coordinate system 41157 // geoIndex: 0, 41158
41159 // symbol: null, // Ã¥âºÂ¾Ã¥Â½Â¢Ã§Â±Â»Ã¥Å¾â¹ 41160 symbolSize: 10, // Ã¥âºÂ¾Ã¥Â½Â¢Ã¥Â¤Â§Ã¥Â°Âï¼ÅåŠ宽ï¼ËåŠå¾âï¼â°Ã¥Ââæâ¢Â°Ã¯Â¼Åå½âÃ¥âºÂ¾Ã¥Â½Â¢Ã¤Â¸ÂºÃ¦â¹åÂâæËâè±形åËâ¢Ã¦â¬Â»Ã¥Â®Â½Ã¥ÂºÂ¦Ã¤Â¸ÂºsymbolSize * 2 41161 // symbolRotate: null, // Ã¥âºÂ¾Ã¥Â½Â¢Ã¦ââ¹Ã¨Â½Â¬Ã¦Å½Â§Ã¥Ë¶ 41162 41163 large: false, 41164 // Available when large is true 41165 largeThreshold: 2000, 41166 // cursor: null, 41167 41168 // label: { 41169 // show: false 41170 // distance: 5, 41171 // formatter: æ â¡Ã§Â¾æââ¡Ã¦Å¾ ¼å¼ÂÃ¥â¢Â¨Ã¯Â¼ÅÃ¥ÂÅTooltip.formatterï¼Åä¸Âæâ¯æÅÂå¼âæÂ¥åâºÅ¾Ã¨Â°Æ 41172 // position: é»Ë认èâ¡ÂªÃ©â¬âåºâï¼Åæ°´å¹³å¸ÆÃ¥Â±â¬Ã¤Â¸Âº'top'ï¼ÅåžâçâºÂ´Ã¥Â¸ÆÃ¥Â±â¬Ã¤Â¸Âº'right'ï¼Åå¯éâ¬â°Ã¤Â¸Âº 41173 // 'inside'|'left'|'right'|'top'|'bottom' 41174 // é»Ë认使çâ¨åâ¦Â¨Ã¥Â±â¬Ã¦ââ¡Ã¦Å¾ ·å¼Âï¼Å详è§ÂTEXTSTYLE 41175 // }, 41176 itemStyle: { 41177 opacity: 0.8 41178 // color: Ã¥Ââå¼â 41179 } 41180 41181 // progressive: null 41182 } 41183 41184 }); 41185 41186 /* 41187* Licensed to the Apache Software Foundation (ASF) under one 41188* or more contributor license agreements. See the NOTICE file 41189* distributed with this work for additional information 41190* regarding copyright ownership. The ASF licenses this file 41191* to you under the Apache License, Version 2.0 (the 41192* "License"); you may not use this file except in compliance 41193* with the License. You may obtain a copy of the License at 41194* 41195* http://www.apache.org/licenses/LICENSE-2.0 41196* 41197* Unless required by applicable law or agreed to in writing, 41198* software distributed under the License is distributed on an 41199* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41200* KIND, either express or implied. See the License for the 41201* specific language governing permissions and limitations 41202* under the License. 41203*/ 41204 41205// TODO Batch by color 41206 41207 var BOOST_SIZE_THRESHOLD = 4; 41208 41209 var LargeSymbolPath = extendShape({ 41210 41211 shape: { 41212 points: null 41213 }, 41214 41215 symbolProxy: null, 41216 41217 buildPath: function (path, shape) { 41218 var points = shape.points; 41219 var size = shape.size; 41220 41221 var symbolProxy = this.symbolProxy; 41222 var symbolProxyShape = symbolProxy.shape; 41223 var ctx = path.getContext ? path.getContext() : path; 41224 var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD; 41225 41226 // Do draw in afterBrush. 41227 if (canBoost) { 41228 return; 41229 } 41230 41231 for (var i = 0; i < points.length;) { 41232 var x = points[i++]; 41233 var y = points[i++]; 41234 41235 if (isNaN(x) || isNaN(y)) { 41236 continue; 41237 } 41238 41239 symbolProxyShape.x = x - size[0] / 2; 41240 symbolProxyShape.y = y - size[1] / 2; 41241 symbolProxyShape.width = size[0]; 41242 symbolProxyShape.height = size[1]; 41243 41244 symbolProxy.buildPath(path, symbolProxyShape, true); 41245 } 41246 }, 41247 41248 afterBrush: function (ctx) { 41249 var shape = this.shape; 41250 var points = shape.points; 41251 var size = shape.size; 41252 var canBoost = size[0] < BOOST_SIZE_THRESHOLD; 41253 41254 if (!canBoost) { 41255 return; 41256 } 41257 41258 this.setTransform(ctx); 41259 // PENDING If style or other canvas status changed? 41260 for (var i = 0; i < points.length;) { 41261 var x = points[i++]; 41262 var y = points[i++]; 41263 if (isNaN(x) || isNaN(y)) { 41264 continue; 41265 } 41266 // fillRect is faster than building a rect path and draw. 41267 // And it support light globalCompositeOperation. 41268 ctx.fillRect( 41269 x - size[0] / 2, y - size[1] / 2, 41270 size[0], size[1] 41271 ); 41272 } 41273 41274 this.restoreTransform(ctx); 41275 }, 41276 41277 findDataIndex: function (x, y) { 41278 // TODO ??? 41279 // Consider transform 41280 41281 var shape = this.shape; 41282 var points = shape.points; 41283 var size = shape.size; 41284 41285 var w = Math.max(size[0], 4); 41286 var h = Math.max(size[1], 4); 41287 41288 // Not consider transform 41289 // Treat each element as a rect 41290 // top down traverse 41291 for (var idx = points.length / 2 - 1; idx >= 0; idx--) { 41292 var i = idx * 2; 41293 var x0 = points[i] - w / 2; 41294 var y0 = points[i + 1] - h / 2; 41295 if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) { 41296 return idx; 41297 } 41298 } 41299 41300 return -1; 41301 } 41302 });
41303 41304 function LargeSymbolDraw() { 41305 this.group = new Group(); 41306 } 41307 41308 var largeSymbolProto = LargeSymbolDraw.prototype; 41309 41310 largeSymbolProto.isPersistent = function () { 41311 return !this._incremental; 41312 }; 41313 41314 /** 41315 * Update symbols draw by new data 41316 * @param {module:echarts/data/List} data 41317 */ 41318 largeSymbolProto.updateData = function (data) { 41319 this.group.removeAll(); 41320 var symbolEl = new LargeSymbolPath({ 41321 rectHover: true, 41322 cursor: 'default' 41323 }); 41324 41325 symbolEl.setShape({ 41326 points: data.getLayout('symbolPoints') 41327 }); 41328 this._setCommon(symbolEl, data); 41329 this.group.add(symbolEl); 41330 41331 this._incremental = null; 41332 }; 41333 41334 largeSymbolProto.updateLayout = function (data) { 41335 if (this._incremental) { 41336 return; 41337 } 41338 41339 var points = data.getLayout('symbolPoints'); 41340 this.group.eachChild(function (child) { 41341 if (child.startIndex != null) { 41342 var len = (child.endIndex - child.startIndex) * 2; 41343 var byteOffset = child.startIndex * 4 * 2; 41344 points = new Float32Array(points.buffer, byteOffset, len); 41345 } 41346 child.setShape('points', points); 41347 }); 41348 }; 41349 41350 largeSymbolProto.incrementalPrepareUpdate = function (data) { 41351 this.group.removeAll(); 41352 41353 this._clearIncremental(); 41354 // Only use incremental displayables when data amount is larger than 2 million. 41355 // PENDING Incremental data? 41356 if (data.count() > 2e6) { 41357 if (!this._incremental) { 41358 this._incremental = new IncrementalDisplayble({ 41359 silent: true 41360 }); 41361 } 41362 this.group.add(this._incremental); 41363 } 41364 else { 41365 this._incremental = null; 41366 } 41367 }; 41368 41369 largeSymbolProto.incrementalUpdate = function (taskParams, data) { 41370 var symbolEl; 41371 if (this._incremental) { 41372 symbolEl = new LargeSymbolPath(); 41373 this._incremental.addDisplayable(symbolEl, true); 41374 } 41375 else { 41376 symbolEl = new LargeSymbolPath({ 41377 rectHover: true, 41378 cursor: 'default', 41379 startIndex: taskParams.start, 41380 endIndex: taskParams.end 41381 }); 41382 symbolEl.incremental = true; 41383 this.group.add(symbolEl); 41384 } 41385 41386 symbolEl.setShape({ 41387 points: data.getLayout('symbolPoints') 41388 }); 41389 this._setCommon(symbolEl, data, !!this._incremental); 41390 }; 41391 41392 largeSymbolProto._setCommon = function (symbolEl, data, isIncremental) { 41393 var hostModel = data.hostModel; 41394 41395 // TODO 41396 // if (data.hasItemVisual.symbolSize) { 41397 // // TODO typed array? 41398 // symbolEl.setShape('sizes', data.mapArray( 41399 // function (idx) { 41400 // var size = data.getItemVisual(idx, 'symbolSize'); 41401 // return (size instanceof Array) ? size : [size, size]; 41402 // } 41403 // )); 41404 // } 41405 // else { 41406 var size = data.getVisual('symbolSize'); 41407 symbolEl.setShape('size', (size instanceof Array) ? size : [size, size]); 41408 // } 41409 41410 // Create symbolProxy to build path for each data 41411 symbolEl.symbolProxy = createSymbol( 41412 data.getVisual('symbol'), 0, 0, 0, 0 41413 ); 41414 // Use symbolProxy setColor method 41415 symbolEl.setColor = symbolEl.symbolProxy.setColor; 41416 41417 var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD; 41418 symbolEl.useStyle( 41419 // Draw shadow when doing fillRect is extremely slow. 41420 hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']) 41421 ); 41422 41423 var visualColor = data.getVisual('color'); 41424 if (visualColor) { 41425 symbolEl.setColor(visualColor); 41426 } 41427 41428 if (!isIncremental) { 41429 // Enable tooltip 41430 // PENDING May have performance issue when path is extremely large 41431 symbolEl.seriesIndex = hostModel.seriesIndex; 41432 symbolEl.on('mousemove', function (e) { 41433 symbolEl.dataIndex = null; 41434 var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY); 41435 if (dataIndex >= 0) { 41436 // Provide dataIndex for tooltip 41437 symbolEl.dataIndex = dataIndex + (symbolEl.startIndex || 0); 41438 } 41439 }); 41440 } 41441 }; 41442 41443 largeSymbolProto.remove = function () { 41444 this._clearIncremental(); 41445 this._incremental = null; 41446 this.group.removeAll(); 41447 }; 41448 41449 largeSymbolProto._clearIncremental = function () { 41450 var incremental = this._incremental; 41451 if (incremental) { 41452 incremental.clearDisplaybles(); 41453 } 41454 }; 41455 41456 /* 41457* Licensed to the Apache Software Foundation (ASF) under one 41458* or more contributor license agreements. See the NOTICE file 41459* distributed with this work for additional information 41460* regarding copyright ownership. The ASF licenses this file 41461* to you under the Apache License, Version 2.0 (the 41462* "License");
41462 you may not use this file except in compliance 41463* with the License. You may obtain a copy of the License at 41464* 41465* http://www.apache.org/licenses/LICENSE-2.0 41466* 41467* Unless required by applicable law or agreed to in writing, 41468* software distributed under the License is distributed on an 41469* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41470* KIND, either express or implied. See the License for the 41471* specific language governing permissions and limitations 41472* under the License. 41473*/ 41474 41475 extendChartView({ 41476 41477 type: 'scatter', 41478 41479 render: function (seriesModel, ecModel, api) { 41480 var data = seriesModel.getData(); 41481 41482 var symbolDraw = this._updateSymbolDraw(data, seriesModel); 41483 symbolDraw.updateData(data); 41484 41485 this._finished = true; 41486 }, 41487 41488 incrementalPrepareRender: function (seriesModel, ecModel, api) { 41489 var data = seriesModel.getData(); 41490 var symbolDraw = this._updateSymbolDraw(data, seriesModel); 41491 41492 symbolDraw.incrementalPrepareUpdate(data); 41493 41494 this._finished = false; 41495 }, 41496 41497 incrementalRender: function (taskParams, seriesModel, ecModel) { 41498 this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData()); 41499 41500 this._finished = taskParams.end === seriesModel.getData().count(); 41501 }, 41502 41503 updateTransform: function (seriesModel, ecModel, api) { 41504 var data = seriesModel.getData(); 41505 // Must mark group dirty and make sure the incremental layer will be cleared 41506 // PENDING 41507 this.group.dirty(); 41508 41509 if (!this._finished || data.count() > 1e4 || !this._symbolDraw.isPersistent()) { 41510 return { 41511 update: true 41512 }; 41513 } 41514 else { 41515 var res = pointsLayout().reset(seriesModel); 41516 if (res.progress) { 41517 res.progress({ start: 0, end: data.count() }, data); 41518 } 41519 41520 this._symbolDraw.updateLayout(data); 41521 } 41522 }, 41523 41524 _updateSymbolDraw: function (data, seriesModel) { 41525 var symbolDraw = this._symbolDraw; 41526 var pipelineContext = seriesModel.pipelineContext; 41527 var isLargeDraw = pipelineContext.large; 41528 41529 if (!symbolDraw || isLargeDraw !== this._isLargeDraw) { 41530 symbolDraw && symbolDraw.remove(); 41531 symbolDraw = this._symbolDraw = isLargeDraw 41532 ? new LargeSymbolDraw() 41533 : new SymbolDraw(); 41534 this._isLargeDraw = isLargeDraw; 41535 this.group.removeAll(); 41536 } 41537 41538 this.group.add(symbolDraw.group); 41539 41540 return symbolDraw; 41541 }, 41542 41543 remove: function (ecModel, api) { 41544 this._symbolDraw && this._symbolDraw.remove(true); 41545 this._symbolDraw = null; 41546 }, 41547 41548 dispose: function () {} 41549 }); 41550 41551 /* 41552* Licensed to the Apache Software Foundation (ASF) under one 41553* or more contributor license agreements. See the NOTICE file 41554* distributed with this work for additional information 41555* regarding copyright ownership. The ASF licenses this file 41556* to you under the Apache License, Version 2.0 (the 41557* "License"); you may not use this file except in compliance 41558* with the License. You may obtain a copy of the License at 41559* 41560* http://www.apache.org/licenses/LICENSE-2.0 41561* 41562* Unless required by applicable law or agreed to in writing, 41563* software distributed under the License is distributed on an 41564* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41565* KIND, either express or implied. See the License for the 41566* specific language governing permissions and limitations 41567* under the License. 41568*/ 41569 41570// import * as zrUtil from 'zrender/src/core/util'; 41571 41572// In case developer forget to include grid component 41573 registerVisual(visualSymbol('scatter', 'circle')); 41574 registerLayout(pointsLayout('scatter')); 41575 41576// echarts.registerProcessor(function (ecModel, api) { 41577// ecModel.eachSeriesByType('scatter', function (seriesModel) { 41578// var data = seriesModel.getData();
41579// var coordSys = seriesModel.coordinateSystem; 41580// if (coordSys.type !== 'geo') { 41581// return; 41582// } 41583// var startPt = coordSys.pointToData([0, 0]); 41584// var endPt = coordSys.pointToData([api.getWidth(), api.getHeight()]); 41585 41586// var dims = zrUtil.map(coordSys.dimensions, function (dim) { 41587// return data.mapDimension(dim); 41588// }); 41589// var range = {}; 41590// range[dims[0]] = [Math.min(startPt[0], endPt[0]), Math.max(startPt[0], endPt[0])]; 41591// range[dims[1]] = [Math.min(startPt[1], endPt[1]), Math.max(startPt[1], endPt[1])]; 41592 41593// data.selectRange(range); 41594// }); 41595// }); 41596 41597 /* 41598* Licensed to the Apache Software Foundation (ASF) under one 41599* or more contributor license agreements. See the NOTICE file 41600* distributed with this work for additional information 41601* regarding copyright ownership. The ASF licenses this file 41602* to you under the Apache License, Version 2.0 (the 41603* "License"); you may not use this file except in compliance 41604* with the License. You may obtain a copy of the License at 41605* 41606* http://www.apache.org/licenses/LICENSE-2.0 41607* 41608* Unless required by applicable law or agreed to in writing, 41609* software distributed under the License is distributed on an 41610* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41611* KIND, either express or implied. See the License for the 41612* specific language governing permissions and limitations 41613* under the License. 41614*/ 41615 41616 function IndicatorAxis(dim, scale, radiusExtent) { 41617 Axis.call(this, dim, scale, radiusExtent); 41618 41619 /** 41620 * Axis type 41621 * - 'category' 41622 * - 'value' 41623 * - 'time' 41624 * - 'log' 41625 * @type {string} 41626 */ 41627 this.type = 'value'; 41628 41629 this.angle = 0; 41630 41631 /** 41632 * Indicator name 41633 * @type {string} 41634 */ 41635 this.name = ''; 41636 /** 41637 * @type {module:echarts/model/Model} 41638 */ 41639 this.model; 41640 } 41641 41642 inherits(IndicatorAxis, Axis); 41643 41644 /* 41645* Licensed to the Apache Software Foundation (ASF) under one 41646* or more contributor license agreements. See the NOTICE file 41647* distributed with this work for additional information 41648* regarding copyright ownership. The ASF licenses this file 41649* to you under the Apache License, Version 2.0 (the 41650* "License"); you may not use this file except in compliance 41651* with the License. You may obtain a copy of the License at 41652* 41653* http://www.apache.org/licenses/LICENSE-2.0 41654* 41655* Unless required by applicable law or agreed to in writing, 41656* software distributed under the License is distributed on an 41657* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41658* KIND, either express or implied. See the License for the 41659* specific language governing permissions and limitations 41660* under the License. 41661*/ 41662 41663// TODO clockwise 41664 41665 function Radar(radarModel, ecModel, api) { 41666 41667 this._model = radarModel; 41668 /** 41669 * Radar dimensions 41670 * @type {Array.<string>} 41671 */ 41672 this.dimensions = []; 41673 41674 this._indicatorAxes = map(radarModel.getIndicatorModels(), function (indicatorModel, idx) { 41675 var dim = 'indicator_' + idx; 41676 var indicatorAxis = new IndicatorAxis(dim, new IntervalScale()); 41677 indicatorAxis.name = indicatorModel.get('name'); 41678 // Inject model and axis 41679 indicatorAxis.model = indicatorModel; 41680 indicatorModel.axis = indicatorAxis; 41681 this.dimensions.push(dim); 41682 return indicatorAxis; 41683 }, this); 41684 41685 this.resize(radarModel, api); 41686 41687 /** 41688 * @type {number} 41689 * @readOnly 41690 */ 41691 this.cx; 41692 /** 41693 * @type {number} 41694 * @readOnly 41695 */ 41696 this.cy; 41697 /** 41698 * @type {number} 41699 * @readOnly 41700 */ 41701 this.r; 41702 /** 41703 * @type {number} 41704 * @readOnly 41705 */ 41706 this.startAngle; 41707 } 41708 41709 Radar.prototype.getIndicatorAxes = function () { 41710 return this._indicatorAxes; 41711 }; 41712 41713 Radar.prototype.dataToPoint = function (value, indicatorIndex) {
41714 var indicatorAxis = this._indicatorAxes[indicatorIndex]; 41715 41716 return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex); 41717 }; 41718 41719 Radar.prototype.coordToPoint = function (coord, indicatorIndex) { 41720 var indicatorAxis = this._indicatorAxes[indicatorIndex]; 41721 var angle = indicatorAxis.angle; 41722 var x = this.cx + coord * Math.cos(angle); 41723 var y = this.cy - coord * Math.sin(angle); 41724 return [x, y]; 41725 }; 41726 41727 Radar.prototype.pointToData = function (pt) { 41728 var dx = pt[0] - this.cx; 41729 var dy = pt[1] - this.cy; 41730 var radius = Math.sqrt(dx * dx + dy * dy); 41731 dx /= radius; 41732 dy /= radius; 41733 41734 var radian = Math.atan2(-dy, dx); 41735 41736 // Find the closest angle 41737 // FIXME index can calculated directly 41738 var minRadianDiff = Infinity; 41739 var closestAxis; 41740 var closestAxisIdx = -1; 41741 for (var i = 0; i < this._indicatorAxes.length; i++) { 41742 var indicatorAxis = this._indicatorAxes[i]; 41743 var diff = Math.abs(radian - indicatorAxis.angle); 41744 if (diff < minRadianDiff) { 41745 closestAxis = indicatorAxis; 41746 closestAxisIdx = i; 41747 minRadianDiff = diff; 41748 } 41749 } 41750 41751 return [closestAxisIdx, +(closestAxis && closestAxis.coodToData(radius))]; 41752 }; 41753 41754 Radar.prototype.resize = function (radarModel, api) { 41755 var center = radarModel.get('center'); 41756 var viewWidth = api.getWidth(); 41757 var viewHeight = api.getHeight(); 41758 var viewSize = Math.min(viewWidth, viewHeight) / 2; 41759 this.cx = parsePercent$1(center[0], viewWidth); 41760 this.cy = parsePercent$1(center[1], viewHeight); 41761 41762 this.startAngle = radarModel.get('startAngle') * Math.PI / 180; 41763 41764 this.r = parsePercent$1(radarModel.get('radius'), viewSize); 41765 41766 each$1(this._indicatorAxes, function (indicatorAxis, idx) { 41767 indicatorAxis.setExtent(0, this.r); 41768 var angle = (this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length); 41769 // Normalize to [-PI, PI] 41770 angle = Math.atan2(Math.sin(angle), Math.cos(angle)); 41771 indicatorAxis.angle = angle; 41772 }, this); 41773 }; 41774 41775 Radar.prototype.update = function (ecModel, api) { 41776 var indicatorAxes = this._indicatorAxes; 41777 var radarModel = this._model; 41778 each$1(indicatorAxes, function (indicatorAxis) { 41779 indicatorAxis.scale.setExtent(Infinity, -Infinity); 41780 }); 41781 ecModel.eachSeriesByType('radar', function (radarSeries, idx) { 41782 if (radarSeries.get('coordinateSystem') !== 'radar' 41783 || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel 41784 ) { 41785 return; 41786 } 41787 var data = radarSeries.getData(); 41788 each$1(indicatorAxes, function (indicatorAxis) { 41789 indicatorAxis.scale.unionExtentFromData(data, data.mapDimension(indicatorAxis.dim)); 41790 }); 41791 }, this); 41792 41793 var splitNumber = radarModel.get('splitNumber'); 41794 41795 function increaseInterval(interval) { 41796 var exp10 = Math.pow(10, Math.floor(Math.log(interval) / Math.LN10)); 41797 // Increase interval 41798 var f = interval / exp10; 41799 if (f === 2) { 41800 f = 5; 41801 } 41802 else { // f is 2 or 5 41803 f *= 2; 41804 } 41805 return f * exp10; 41806 } 41807 // Force all the axis fixing the maxSplitNumber. 41808 each$1(indicatorAxes, function (indicatorAxis, idx) { 41809 var rawExtent = getScaleExtent(indicatorAxis.scale, indicatorAxis.model); 41810 niceScaleExtent(indicatorAxis.scale, indicatorAxis.model); 41811 41812 var axisModel = indicatorAxis.model; 41813 var scale = indicatorAxis.scale; 41814 var fixedMin = axisModel.getMin(); 41815 var fixedMax = axisModel.getMax(); 41816 var interval = scale.getInterval(); 41817 41818 if (fixedMin != null && fixedMax != null) { 41819 // User set min, max, divide to get new interval 41820 scale.setExtent(+fixedMin, +fixedMax); 41821 scale.setInterval( 41822 (fixedMax - fixedMin) / splitNumber 41823 ); 41824 } 41825 else if (fixedMin != null) { 41826 var max; 41827 // User set min, expand extent on the other side 41828 do { 41829 max = fixedMin + interval * splitNumber; 41830 scale.setExtent(+fixedMin, max); 41831 // Interval must been set after extent 41832 // FIXME 41833 scale.setInterval(interval); 41834 41835 interval = increaseInterval(interval); 41836 } while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])); 41837 } 41838 else if (fixedMax != null) { 41839 var min; 41840 // User set min, expand extent on the other side 41841 do { 41842 min = fixedMax - interval * splitNumber; 41843 scale.setExtent(min, +fixedMax); 41844 scale.setInterval(interval); 41845 interval = increaseInterval(interval); 41846 } while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])); 41847 } 41848 else { 41849 var nicedSplitNumber = scale.getTicks().length - 1; 41850 if (nicedSplitNumber > splitNumber) { 41851 interval = increaseInterval(interval); 41852 } 41853 // PENDING 41854 var center = Math.round((rawExtent[0] + rawExtent[1]) / 2 / interval) * interval; 41855 var halfSplitNumber = Math.round(splitNumber / 2); 41856 scale.setExtent( 41857 round$1(center - halfSplitNumber * interval), 41858 round$1(center + (splitNumber - halfSplitNumber) * interval) 41859 ); 41860 scale.setInterval(interval); 41861 } 41862 }); 41863 }; 41864 41865 /** 41866 * Radar dimensions is based on the data 41867 * @type {Array} 41868 */ 41869 Radar.dimensions = []; 41870 41871 Radar.create = function (ecModel, api) { 41872 var radarList = []; 41873 ecModel.eachComponent('radar', function (radarModel) { 41874 var radar = new Radar(radarModel, ecModel, api); 41875 radarList.push(radar); 41876 radarModel.coordinateSystem = radar; 41877 }); 41878 ecModel.eachSeriesByType('radar', function (radarSeries) { 41879 if (radarSeries.get('coordinateSystem') === 'radar') { 41880 // Inject coordinate system 41881 radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0]; 41882 } 41883 }); 41884 return radarList; 41885 }; 41886 41887 CoordinateSystemManager.register('radar', Radar); 41888 41889 /* 41890* Licensed to the Apache Software Foundation (ASF) under one 41891* or more contributor license agreements. See the NOTICE file 41892* distributed with this work for additional information 41893* regarding copyright ownership. The ASF licenses this file 41894* to you under the Apache License, Version 2.0 (the 41895* "License");
41895 you may not use this file except in compliance 41896* with the License. You may obtain a copy of the License at 41897* 41898* http://www.apache.org/licenses/LICENSE-2.0 41899* 41900* Unless required by applicable law or agreed to in writing, 41901* software distributed under the License is distributed on an 41902* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 41903* KIND, either express or implied. See the License for the 41904* specific language governing permissions and limitations 41905* under the License. 41906*/ 41907 41908 var valueAxisDefault = axisDefault.valueAxis; 41909 41910 function defaultsShow(opt, show) { 41911 return defaults({ 41912 show: show 41913 }, opt); 41914 } 41915 41916 var RadarModel = extendComponentModel({ 41917 41918 type: 'radar', 41919 41920 optionUpdated: function () { 41921 var boundaryGap = this.get('boundaryGap'); 41922 var splitNumber = this.get('splitNumber'); 41923 var scale = this.get('scale'); 41924 var axisLine = this.get('axisLine'); 41925 var axisTick = this.get('axisTick'); 41926 var axisLabel = this.get('axisLabel'); 41927 var nameTextStyle = this.get('name'); 41928 var showName = this.get('name.show'); 41929 var nameFormatter = this.get('name.formatter'); 41930 var nameGap = this.get('nameGap'); 41931 var triggerEvent = this.get('triggerEvent'); 41932 41933 var indicatorModels = map(this.get('indicator') || [], function (indicatorOpt) { 41934 // PENDING 41935 if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) { 41936 indicatorOpt.min = 0; 41937 } 41938 else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) { 41939 indicatorOpt.max = 0; 41940 } 41941 var iNameTextStyle = nameTextStyle; 41942 if(indicatorOpt.color != null) { 41943 iNameTextStyle = defaults({color: indicatorOpt.color}, nameTextStyle); 41944 } 41945 // Use same configuration 41946 indicatorOpt = merge(clone(indicatorOpt), { 41947 boundaryGap: boundaryGap, 41948 splitNumber: splitNumber, 41949 scale: scale, 41950 axisLine: axisLine, 41951 axisTick: axisTick, 41952 axisLabel: axisLabel, 41953 // Competitable with 2 and use text 41954 name: indicatorOpt.text, 41955 nameLocation: 'end', 41956 nameGap: nameGap, 41957 // min: 0, 41958 nameTextStyle: iNameTextStyle, 41959 triggerEvent: triggerEvent 41960 }, false); 41961 if (!showName) { 41962 indicatorOpt.name = ''; 41963 } 41964 if (typeof nameFormatter === 'string') { 41965 var indName = indicatorOpt.name; 41966 indicatorOpt.name = nameFormatter.replace('{value}', indName != null ? indName : ''); 41967 } 41968 else if (typeof nameFormatter === 'function') { 41969 indicatorOpt.name = nameFormatter( 41970 indicatorOpt.name, indicatorOpt 41971 ); 41972 } 41973 var model = extend( 41974 new Model(indicatorOpt, null, this.ecModel), 41975 axisModelCommonMixin 41976 ); 41977 41978 // For triggerEvent. 41979 model.mainType = 'radar'; 41980 model.componentIndex = this.componentIndex; 41981 41982 return model; 41983 }, this); 41984 41985 this.getIndicatorModels = function () { 41986 return indicatorModels; 41987 }; 41988 }, 41989 41990 defaultOption: { 41991 41992 zlevel: 0, 41993 41994 z: 0, 41995 41996 center: ['50%', '50%'], 41997 41998 radius: '75%', 41999 42000 startAngle: 90, 42001 42002 name: { 42003 show: true 42004 // formatter: null 42005 // textStyle: {} 42006 }, 42007 42008 boundaryGap: [0, 0], 42009 42010 splitNumber: 5, 42011 42012 nameGap: 15, 42013 42014 scale: false, 42015 42016 // Polygon or circle 42017 shape: 'polygon', 42018
42019 axisLine: merge( 42020 { 42021 lineStyle: { 42022 color: '#bbb' 42023 } 42024 }, 42025 valueAxisDefault.axisLine 42026 ), 42027 axisLabel: defaultsShow(valueAxisDefault.axisLabel, false), 42028 axisTick: defaultsShow(valueAxisDefault.axisTick, false), 42029 splitLine: defaultsShow(valueAxisDefault.splitLine, true), 42030 splitArea: defaultsShow(valueAxisDefault.splitArea, true), 42031 42032 // {text, min, max} 42033 indicator: [] 42034 } 42035 }); 42036 42037 /* 42038* Licensed to the Apache Software Foundation (ASF) under one 42039* or more contributor license agreements. See the NOTICE file 42040* distributed with this work for additional information 42041* regarding copyright ownership. The ASF licenses this file 42042* to you under the Apache License, Version 2.0 (the 42043* "License"); you may not use this file except in compliance 42044* with the License. You may obtain a copy of the License at 42045* 42046* http://www.apache.org/licenses/LICENSE-2.0 42047* 42048* Unless required by applicable law or agreed to in writing, 42049* software distributed under the License is distributed on an 42050* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42051* KIND, either express or implied. See the License for the 42052* specific language governing permissions and limitations 42053* under the License. 42054*/ 42055 42056 var axisBuilderAttrs$1 = [ 42057 'axisLine', 'axisTickLabel', 'axisName' 42058 ]; 42059 42060 extendComponentView({ 42061 42062 type: 'radar', 42063 42064 render: function (radarModel, ecModel, api) { 42065 var group = this.group; 42066 group.removeAll(); 42067 42068 this._buildAxes(radarModel); 42069 this._buildSplitLineAndArea(radarModel); 42070 }, 42071 42072 _buildAxes: function (radarModel) { 42073 var radar = radarModel.coordinateSystem; 42074 var indicatorAxes = radar.getIndicatorAxes(); 42075 var axisBuilders = map(indicatorAxes, function (indicatorAxis) { 42076 var axisBuilder = new AxisBuilder(indicatorAxis.model, { 42077 position: [radar.cx, radar.cy], 42078 rotation: indicatorAxis.angle, 42079 labelDirection: -1, 42080 tickDirection: -1, 42081 nameDirection: 1 42082 }); 42083 return axisBuilder; 42084 }); 42085 42086 each$1(axisBuilders, function (axisBuilder) { 42087 each$1(axisBuilderAttrs$1, axisBuilder.add, axisBuilder); 42088 this.group.add(axisBuilder.getGroup()); 42089 }, this); 42090 }, 42091 42092 _buildSplitLineAndArea: function (radarModel) { 42093 var radar = radarModel.coordinateSystem; 42094 var indicatorAxes = radar.getIndicatorAxes(); 42095 if (!indicatorAxes.length) { 42096 return; 42097 } 42098 var shape = radarModel.get('shape'); 42099 var splitLineModel = radarModel.getModel('splitLine'); 42100 var splitAreaModel = radarModel.getModel('splitArea'); 42101 var lineStyleModel = splitLineModel.getModel('lineStyle'); 42102 var areaStyleModel = splitAreaModel.getModel('areaStyle'); 42103 42104 var showSplitLine = splitLineModel.get('show'); 42105 var showSplitArea = splitAreaModel.get('show'); 42106 var splitLineColors = lineStyleModel.get('color'); 42107 var splitAreaColors = areaStyleModel.get('color'); 42108 42109 splitLineColors = isArray(splitLineColors) ? splitLineColors : [splitLineColors]; 42110 splitAreaColors = isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors]; 42111 42112 var splitLines = []; 42113 var splitAreas = []; 42114 42115 function getColorIndex(areaOrLine, areaOrLineColorList, idx) { 42116 var colorIndex = idx % areaOrLineColorList.length; 42117 areaOrLine[colorIndex] = areaOrLine[colorIndex] || []; 42118 return colorIndex; 42119 } 42120 42121 if (shape === 'circle') { 42122 var ticksRadius = indicatorAxes[0].getTicksCoords(); 42123 var cx = radar.cx; 42124 var cy = radar.cy; 42125 for (var i = 0; i < ticksRadius.length; i++) { 42126 if (showSplitLine) { 42127 var colorIndex = getColorIndex(splitLines, splitLineColors, i); 42128 splitLines[colorIndex].push(new Circle({ 42129 shape: { 42130 cx: cx, 42131 cy: cy, 42132 r: ticksRadius[i].coord 42133 } 42134 })); 42135 } 42136 if (showSplitArea && i < ticksRadius.length - 1) { 42137 var colorIndex = getColorIndex(splitAreas, splitAreaColors, i); 42138 splitAreas[colorIndex].push(new Ring({ 42139 shape: { 42140 cx: cx, 42141 cy: cy, 42142 r0: ticksRadius[i].coord, 42143 r: ticksRadius[i + 1].coord 42144 } 42145 })); 42146 } 42147 } 42148 } 42149 // Polyyon 42150 else { 42151 var realSplitNumber; 42152 var axesTicksPoints = map(indicatorAxes, function (indicatorAxis, idx) { 42153 var ticksCoords = indicatorAxis.getTicksCoords(); 42154 realSplitNumber = realSplitNumber == null 42155 ? ticksCoords.length - 1 42156 : Math.min(ticksCoords.length - 1, realSplitNumber); 42157 return map(ticksCoords, function (tickCoord) { 42158 return radar.coordToPoint(tickCoord.coord, idx); 42159 }); 42160 }); 42161 42162 var prevPoints = []; 42163 for (var i = 0; i <= realSplitNumber; i++) { 42164 var points = []; 42165 for (var j = 0; j < indicatorAxes.length; j++) { 42166 points.push(axesTicksPoints[j][i]); 42167 } 42168 // Close 42169 if (points[0]) { 42170 points.push(points[0].slice()); 42171 } 42172 else { 42173 if (__DEV__) { 42174 console.error('Can\'t draw value axis ' + i); 42175 } 42176 } 42177 42178 if (showSplitLine) { 42179 var colorIndex = getColorIndex(splitLines, splitLineColors, i); 42180 splitLines[colorIndex].push(new Polyline({ 42181 shape: { 42182 points: points 42183 } 42184 })); 42185 } 42186 if (showSplitArea && prevPoints) { 42187 var colorIndex = getColorIndex(splitAreas, splitAreaColors, i - 1); 42188 splitAreas[colorIndex].push(new Polygon({ 42189 shape: { 42190 points: points.concat(prevPoints) 42191 } 42192 })); 42193 } 42194 prevPoints = points.slice().reverse(); 42195 } 42196 } 42197 42198 var lineStyle = lineStyleModel.getLineStyle(); 42199 var areaStyle = areaStyleModel.getAreaStyle(); 42200 // Add splitArea before splitLine 42201 each$1(splitAreas, function (splitAreas, idx) { 42202 this.group.add(mergePath( 42203 splitAreas, { 42204 style: defaults({
42205 stroke: 'none', 42206 fill: splitAreaColors[idx % splitAreaColors.length] 42207 }, areaStyle), 42208 silent: true 42209 } 42210 )); 42211 }, this); 42212 42213 each$1(splitLines, function (splitLines, idx) { 42214 this.group.add(mergePath( 42215 splitLines, { 42216 style: defaults({ 42217 fill: 'none', 42218 stroke: splitLineColors[idx % splitLineColors.length] 42219 }, lineStyle), 42220 silent: true 42221 } 42222 )); 42223 }, this); 42224 42225 } 42226 }); 42227 42228 /* 42229* Licensed to the Apache Software Foundation (ASF) under one 42230* or more contributor license agreements. See the NOTICE file 42231* distributed with this work for additional information 42232* regarding copyright ownership. The ASF licenses this file 42233* to you under the Apache License, Version 2.0 (the 42234* "License"); you may not use this file except in compliance 42235* with the License. You may obtain a copy of the License at 42236* 42237* http://www.apache.org/licenses/LICENSE-2.0 42238* 42239* Unless required by applicable law or agreed to in writing, 42240* software distributed under the License is distributed on an 42241* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42242* KIND, either express or implied. See the License for the 42243* specific language governing permissions and limitations 42244* under the License. 42245*/ 42246 42247 /* 42248* Licensed to the Apache Software Foundation (ASF) under one 42249* or more contributor license agreements. See the NOTICE file 42250* distributed with this work for additional information 42251* regarding copyright ownership. The ASF licenses this file 42252* to you under the Apache License, Version 2.0 (the 42253* "License"); you may not use this file except in compliance 42254* with the License. You may obtain a copy of the License at 42255* 42256* http://www.apache.org/licenses/LICENSE-2.0 42257* 42258* Unless required by applicable law or agreed to in writing, 42259* software distributed under the License is distributed on an 42260* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42261* KIND, either express or implied. See the License for the 42262* specific language governing permissions and limitations 42263* under the License. 42264*/ 42265 42266 var RadarSeries = SeriesModel.extend({ 42267 42268 type: 'series.radar', 42269 42270 dependencies: ['radar'], 42271 42272 42273 // Overwrite 42274 init: function (option) { 42275 RadarSeries.superApply(this, 'init', arguments); 42276 42277 // Enable legend selection for each data item 42278 // Use a function instead of direct access because data reference may changed 42279 this.legendDataProvider = function () { 42280 return this.getRawData(); 42281 }; 42282 }, 42283 42284 getInitialData: function (option, ecModel) { 42285 return createListSimply(this, { 42286 generateCoord: 'indicator_', 42287 generateCoordCount: Infinity 42288 }); 42289 }, 42290 42291 formatTooltip: function (dataIndex) { 42292 var data = this.getData(); 42293 var coordSys = this.coordinateSystem; 42294 var indicatorAxes = coordSys.getIndicatorAxes(); 42295 var name = this.getData().getName(dataIndex); 42296 return encodeHTML(name === '' ? this.name : name) + '<br/>' 42297 + map(indicatorAxes, function (axis, idx) { 42298 var val = data.get(data.mapDimension(axis.dim), dataIndex); 42299 return encodeHTML(axis.name + ' : ' + val); 42300 }).join('<br />'); 42301 }, 42302 42303 defaultOption: { 42304 zlevel: 0, 42305 z: 2, 42306 coordinateSystem: 'radar', 42307 legendHoverLink: true, 42308 radarIndex: 0, 42309 lineStyle: { 42310 width: 2, 42311 type: 'solid' 42312 }, 42313 label: { 42314 position: 'top' 42315 }, 42316 // areaStyle: { 42317 // }, 42318 // itemStyle: {} 42319 symbol: 'emptyCircle', 42320 symbolSize: 4 42321 // symbolRotate: null 42322 } 42323 });
42324 42325 /* 42326* Licensed to the Apache Software Foundation (ASF) under one 42327* or more contributor license agreements. See the NOTICE file 42328* distributed with this work for additional information 42329* regarding copyright ownership. The ASF licenses this file 42330* to you under the Apache License, Version 2.0 (the 42331* "License"); you may not use this file except in compliance 42332* with the License. You may obtain a copy of the License at 42333* 42334* http://www.apache.org/licenses/LICENSE-2.0 42335* 42336* Unless required by applicable law or agreed to in writing, 42337* software distributed under the License is distributed on an 42338* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42339* KIND, either express or implied. See the License for the 42340* specific language governing permissions and limitations 42341* under the License. 42342*/ 42343 42344 function normalizeSymbolSize(symbolSize) { 42345 if (!isArray(symbolSize)) { 42346 symbolSize = [+symbolSize, +symbolSize]; 42347 } 42348 return symbolSize; 42349 } 42350 42351 extendChartView({ 42352 42353 type: 'radar', 42354 42355 render: function (seriesModel, ecModel, api) { 42356 var polar = seriesModel.coordinateSystem; 42357 var group = this.group; 42358 42359 var data = seriesModel.getData(); 42360 var oldData = this._data; 42361 42362 function createSymbol$$1(data, idx) { 42363 var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; 42364 var color = data.getItemVisual(idx, 'color'); 42365 if (symbolType === 'none') { 42366 return; 42367 } 42368 var symbolSize = normalizeSymbolSize( 42369 data.getItemVisual(idx, 'symbolSize') 42370 ); 42371 var symbolPath = createSymbol( 42372 symbolType, -1, -1, 2, 2, color 42373 ); 42374 symbolPath.attr({ 42375 style: { 42376 strokeNoScale: true 42377 }, 42378 z2: 100, 42379 scale: [symbolSize[0] / 2, symbolSize[1] / 2] 42380 }); 42381 return symbolPath; 42382 } 42383 42384 function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) { 42385 // Simply rerender all 42386 symbolGroup.removeAll(); 42387 for (var i = 0; i < newPoints.length - 1; i++) { 42388 var symbolPath = createSymbol$$1(data, idx); 42389 if (symbolPath) { 42390 symbolPath.__dimIdx = i; 42391 if (oldPoints[i]) { 42392 symbolPath.attr('position', oldPoints[i]); 42393 graphic[isInit ? 'initProps' : 'updateProps']( 42394 symbolPath, { 42395 position: newPoints[i] 42396 }, seriesModel, idx 42397 ); 42398 } 42399 else { 42400 symbolPath.attr('position', newPoints[i]); 42401 } 42402 symbolGroup.add(symbolPath); 42403 } 42404 } 42405 } 42406 42407 function getInitialPoints(points) { 42408 return map(points, function (pt) { 42409 return [polar.cx, polar.cy]; 42410 }); 42411 } 42412 data.diff(oldData) 42413 .add(function (idx) { 42414 var points = data.getItemLayout(idx); 42415 if (!points) { 42416 return; 42417 } 42418 var polygon = new Polygon(); 42419 var polyline = new Polyline(); 42420 var target = { 42421 shape: { 42422 points: points 42423 } 42424 }; 42425 polygon.shape.points = getInitialPoints(points); 42426 polyline.shape.points = getInitialPoints(points); 42427 initProps(polygon, target, seriesModel, idx); 42428 initProps(polyline, target, seriesModel, idx); 42429 42430 var itemGroup = new Group(); 42431 var symbolGroup = new Group(); 42432 itemGroup.add(polyline); 42433 itemGroup.add(polygon); 42434 itemGroup.add(symbolGroup); 42435 42436 updateSymbols( 42437 polyline.shape.points, points, symbolGroup, data, idx, true 42438 ); 42439 42440 data.setItemGraphicEl(idx, itemGroup); 42441 }) 42442 .update(function (newIdx, oldIdx) { 42443 var itemGroup = oldData.getItemGraphicEl(oldIdx); 42444 var polyline = itemGroup.childAt(0); 42445 var polygon = itemGroup.childAt(1); 42446 var symbolGroup = itemGroup.childAt(2); 42447 var target = { 42448 shape: { 42449 points: data.getItemLayout(newIdx) 42450 } 42451 }; 42452 if (!target.shape.points) { 42453 return; 42454 } 42455 updateSymbols( 42456 polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false 42457 ); 42458 42459 updateProps(polyline, target, seriesModel); 42460 updateProps(polygon, target, seriesModel); 42461 42462 data.setItemGraphicEl(newIdx, itemGroup); 42463 }) 42464 .remove(function (idx) { 42465 group.remove(oldData.getItemGraphicEl(idx)); 42466 }) 42467 .execute(); 42468 42469 data.eachItemGraphicEl(function (itemGroup, idx) { 42470 var itemModel = data.getItemModel(idx); 42471 var polyline = itemGroup.childAt(0); 42472 var polygon = itemGroup.childAt(1); 42473 var symbolGroup = itemGroup.childAt(2); 42474 var color = data.getItemVisual(idx, 'color'); 42475 42476 group.add(itemGroup); 42477 42478 polyline.useStyle( 42479 defaults( 42480 itemModel.getModel('lineStyle').getLineStyle(), 42481 { 42482 fill: 'none', 42483 stroke: color 42484 } 42485 ) 42486 ); 42487 polyline.hoverStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle(); 42488 42489 var areaStyleModel = itemModel.getModel('areaStyle'); 42490 var hoverAreaStyleModel = itemModel.getModel('emphasis.areaStyle'); 42491 var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty(); 42492 var hoverPolygonIgnore = hoverAreaStyleModel.isEmpty() && hoverAreaStyleModel.parentModel.isEmpty(); 42493 42494 hoverPolygonIgnore = hoverPolygonIgnore && polygonIgnore; 42495 polygon.ignore = polygonIgnore; 42496 42497 polygon.useStyle( 42498 defaults( 42499 areaStyleModel.getAreaStyle(), 42500 { 42501 fill: color, 42502 opacity: 0.7 42503 } 42504 ) 42505 ); 42506 polygon.hoverStyle = hoverAreaStyleModel.getAreaStyle(); 42507 42508 var itemStyle = itemModel.getModel('itemStyle').getItemStyle(['color']); 42509 var itemHoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); 42510 var labelModel = itemModel.getModel('label'); 42511 var labelHoverModel = itemModel.getModel('emphasis.label'); 42512 symbolGroup.eachChild(function (symbolPath) { 42513 symbolPath.setStyle(itemStyle); 42514 symbolPath.hoverStyle = clone(itemHoverStyle); 42515 42516 setLabelStyle( 42517 symbolPath.style, symbolPath.hoverStyle, labelModel, labelHoverModel, 42518 { 42519 labelFetcher: data.hostModel,
42520 labelDataIndex: idx, 42521 labelDimIndex: symbolPath.__dimIdx, 42522 defaultText: data.get(data.dimensions[symbolPath.__dimIdx], idx), 42523 autoColor: color, 42524 isRectText: true 42525 } 42526 ); 42527 }); 42528 42529 function onEmphasis() { 42530 polygon.attr('ignore', hoverPolygonIgnore); 42531 } 42532 42533 function onNormal() { 42534 polygon.attr('ignore', polygonIgnore); 42535 } 42536 42537 itemGroup.off('mouseover').off('mouseout').off('normal').off('emphasis'); 42538 itemGroup.on('emphasis', onEmphasis) 42539 .on('mouseover', onEmphasis) 42540 .on('normal', onNormal) 42541 .on('mouseout', onNormal); 42542 42543 setHoverStyle(itemGroup); 42544 }); 42545 42546 this._data = data; 42547 }, 42548 42549 remove: function () { 42550 this.group.removeAll(); 42551 this._data = null; 42552 }, 42553 42554 dispose: function () {} 42555 }); 42556 42557 /* 42558* Licensed to the Apache Software Foundation (ASF) under one 42559* or more contributor license agreements. See the NOTICE file 42560* distributed with this work for additional information 42561* regarding copyright ownership. The ASF licenses this file 42562* to you under the Apache License, Version 2.0 (the 42563* "License"); you may not use this file except in compliance 42564* with the License. You may obtain a copy of the License at 42565* 42566* http://www.apache.org/licenses/LICENSE-2.0 42567* 42568* Unless required by applicable law or agreed to in writing, 42569* software distributed under the License is distributed on an 42570* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42571* KIND, either express or implied. See the License for the 42572* specific language governing permissions and limitations 42573* under the License. 42574*/ 42575 42576 42577 var radarLayout = function (ecModel) { 42578 ecModel.eachSeriesByType('radar', function (seriesModel) { 42579 var data = seriesModel.getData(); 42580 var points = []; 42581 var coordSys = seriesModel.coordinateSystem; 42582 if (!coordSys) { 42583 return; 42584 } 42585 42586 function pointsConverter(val, idx) { 42587 points[idx] = points[idx] || []; 42588 points[idx][i] = coordSys.dataToPoint(val, i); 42589 } 42590 var axes = coordSys.getIndicatorAxes(); 42591 for (var i = 0; i < axes.length; i++) { 42592 data.each(data.mapDimension(axes[i].dim), pointsConverter); 42593 } 42594 42595 data.each(function (idx) { 42596 // Close polygon 42597 points[idx][0] && points[idx].push(points[idx][0].slice()); 42598 data.setItemLayout(idx, points[idx]); 42599 }); 42600 }); 42601 }; 42602 42603 /* 42604* Licensed to the Apache Software Foundation (ASF) under one 42605* or more contributor license agreements. See the NOTICE file 42606* distributed with this work for additional information 42607* regarding copyright ownership. The ASF licenses this file 42608* to you under the Apache License, Version 2.0 (the 42609* "License"); you may not use this file except in compliance 42610* with the License. You may obtain a copy of the License at 42611* 42612* http://www.apache.org/licenses/LICENSE-2.0 42613* 42614* Unless required by applicable law or agreed to in writing, 42615* software distributed under the License is distributed on an 42616* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42617* KIND, either express or implied. See the License for the 42618* specific language governing permissions and limitations 42619* under the License. 42620*/ 42621 42622// Backward compat for radar chart in 2 42623 var backwardCompat$1 = function (option) { 42624 var polarOptArr = option.polar; 42625 if (polarOptArr) { 42626 if (!isArray(polarOptArr)) { 42627 polarOptArr = [polarOptArr]; 42628 } 42629 var polarNotRadar = []; 42630 each$1(polarOptArr, function (polarOpt, idx) { 42631 if (polarOpt.indicator) { 42632 if (polarOpt.type && !polarOpt.shape) { 42633 polarOpt.shape = polarOpt.type; 42634 } 42635 option.radar = option.radar || []; 42636 if (!isArray(option.radar)) { 42637 option.radar = [option.radar]; 42638 } 42639 option.radar.push(polarOpt); 42640 } 42641 else { 42642 polarNotRadar.push(polarOpt); 42643 } 42644 }); 42645 option.polar = polarNotRadar; 42646 } 42647 each$1(option.series, function (seriesOpt) { 42648 if (seriesOpt && seriesOpt.type === 'radar' && seriesOpt.polarIndex) { 42649 seriesOpt.radarIndex = seriesOpt.polarIndex; 42650 } 42651 }); 42652 }; 42653 42654 /* 42655* Licensed to the Apache Software Foundation (ASF) under one 42656* or more contributor license agreements. See the NOTICE file 42657* distributed with this work for additional information 42658* regarding copyright ownership. The ASF licenses this file 42659* to you under the Apache License, Version 2.0 (the 42660* "License");
42660 you may not use this file except in compliance 42661* with the License. You may obtain a copy of the License at 42662* 42663* http://www.apache.org/licenses/LICENSE-2.0 42664* 42665* Unless required by applicable law or agreed to in writing, 42666* software distributed under the License is distributed on an 42667* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42668* KIND, either express or implied. See the License for the 42669* specific language governing permissions and limitations 42670* under the License. 42671*/ 42672 42673 42674// Must use radar component 42675 registerVisual(dataColor('radar')); 42676 registerVisual(visualSymbol('radar', 'circle')); 42677 registerLayout(radarLayout); 42678 registerProcessor(dataFilter('radar')); 42679 registerPreprocessor(backwardCompat$1); 42680 42681 /* 42682* Licensed to the Apache Software Foundation (ASF) under one 42683* or more contributor license agreements. See the NOTICE file 42684* distributed with this work for additional information 42685* regarding copyright ownership. The ASF licenses this file 42686* to you under the Apache License, Version 2.0 (the 42687* "License"); you may not use this file except in compliance 42688* with the License. You may obtain a copy of the License at 42689* 42690* http://www.apache.org/licenses/LICENSE-2.0 42691* 42692* Unless required by applicable law or agreed to in writing, 42693* software distributed under the License is distributed on an 42694* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 42695* KIND, either express or implied. See the License for the 42696* specific language governing permissions and limitations 42697* under the License. 42698*/ 42699 42700 /** 42701 * Simple view coordinate system 42702 * Mapping given x, y to transformd view x, y 42703 */ 42704 42705 var v2ApplyTransform$1 = applyTransform; 42706 42707// Dummy transform node 42708 function TransformDummy() { 42709 Transformable.call(this); 42710 } 42711 mixin(TransformDummy, Transformable); 42712 42713 function View(name) { 42714 /** 42715 * @type {string} 42716 */ 42717 this.name = name; 42718 42719 /** 42720 * @type {Object} 42721 */ 42722 this.zoomLimit; 42723 42724 Transformable.call(this); 42725 42726 this._roamTransformable = new TransformDummy(); 42727 42728 this._rawTransformable = new TransformDummy(); 42729 42730 this._center; 42731 this._zoom; 42732 } 42733 42734 View.prototype = { 42735 42736 constructor: View, 42737 42738 type: 'view', 42739 42740 /** 42741 * @param {Array.<string>} 42742 * @readOnly 42743 */ 42744 dimensions: ['x', 'y'], 42745 42746 /** 42747 * Set bounding rect 42748 * @param {number} x 42749 * @param {number} y 42750 * @param {number} width 42751 * @param {number} height 42752 */ 42753 42754 // PENDING to getRect 42755 setBoundingRect: function (x, y, width, height) { 42756 this._rect = new BoundingRect(x, y, width, height); 42757 return this._rect; 42758 }, 42759 42760 /** 42761 * @return {module:zrender/core/BoundingRect} 42762 */ 42763 // PENDING to getRect 42764 getBoundingRect: function () { 42765 return this._rect; 42766 }, 42767 42768 /** 42769 * @param {number} x 42770 * @param {number} y 42771 * @param {number} width 42772 * @param {number} height 42773 */ 42774 setViewRect: function (x, y, width, height) { 42775 this.transformTo(x, y, width, height); 42776 this._viewRect = new BoundingRect(x, y, width, height); 42777 }, 42778 42779 /** 42780 * Transformed to particular position and size 42781 * @param {number} x 42782 * @param {number} y 42783 * @param {number} width 42784 * @param {number} height 42785 */ 42786 transformTo: function (x, y, width, height) { 42787 var rect = this.getBoundingRect(); 42788 var rawTransform = this._rawTransformable; 42789 42790 rawTransform.transform = rect.calculateTransform( 42791 new BoundingRect(x, y, width, height) 42792 ); 42793 42794 rawTransform.decomposeTransform(); 42795 42796 this._updateTransform(); 42797 }, 42798 42799 /** 42800 * Set center of view 42801 * @param {Array.<number>} [centerCoord] 42802 */ 42803 setCenter: function (centerCoord) { 42804 if (!centerCoord) { 42805 return; 42806 } 42807 this._center = centerCoord; 42808 42809 this._updateCenterAndZoom(); 42810 }, 42811 42812 /** 42813 * @param {number} zoom 42814 */ 42815 setZoom: function (zoom) { 42816 zoom = zoom || 1; 42817 42818 var zoomLimit = this.zoomLimit; 42819 if (zoomLimit) { 42820 if (zoomLimit.max != null) { 42821 zoom = Math.min(zoomLimit.max, zoom); 42822 } 42823 if (zoomLimit.min != null) { 42824 zoom = Math.max(zoomLimit.min, zoom); 42825 } 42826 } 42827 this._zoom = zoom; 42828 42829 this._updateCenterAndZoom(); 42830 }, 42831 42832 /** 42833 * Get default center without roam 42834 */ 42835 getDefaultCenter: function () { 42836 // Rect before any transform
42837 var rawRect = this.getBoundingRect(); 42838 var cx = rawRect.x + rawRect.width / 2; 42839 var cy = rawRect.y + rawRect.height / 2; 42840 42841 return [cx, cy]; 42842 }, 42843 42844 getCenter: function () { 42845 return this._center || this.getDefaultCenter(); 42846 }, 42847 42848 getZoom: function () { 42849 return this._zoom || 1; 42850 }, 42851 42852 /** 42853 * @return {Array.<number} 42854 */ 42855 getRoamTransform: function () { 42856 return this._roamTransformable.getLocalTransform(); 42857 }, 42858 42859 /** 42860 * Remove roam 42861 */ 42862 42863 _updateCenterAndZoom: function () { 42864 // Must update after view transform updated 42865 var rawTransformMatrix = this._rawTransformable.getLocalTransform(); 42866 var roamTransform = this._roamTransformable; 42867 var defaultCenter = this.getDefaultCenter(); 42868 var center = this.getCenter(); 42869 var zoom = this.getZoom(); 42870 42871 center = applyTransform([], center, rawTransformMatrix); 42872 defaultCenter = applyTransform([], defaultCenter, rawTransformMatrix); 42873 42874 roamTransform.origin = center; 42875 roamTransform.position = [ 42876 defaultCenter[0] - center[0], 42877 defaultCenter[1] - center[1] 42878 ]; 42879 roamTransform.scale = [zoom, zoom]; 42880 42881 this._updateTransform(); 42882 }, 42883 42884 /** 42885 * Update transform from roam and mapLocation 42886 * @private 42887 */ 42888 _updateTransform: function () { 42889 var roamTransformable = this._roamTransformable; 42890 var rawTransformable = this._rawTransformable; 42891 42892 rawTransformable.parent = roamTransformable; 42893 roamTransformable.updateTransform(); 42894 rawTransformable.updateTransform(); 42895 42896 copy$1(this.transform || (this.transform = []), rawTransformable.transform || create$1()); 42897 42898 this._rawTransform = rawTransformable.getLocalTransform(); 42899 42900 this.invTransform = this.invTransform || []; 42901 invert(this.invTransform, this.transform); 42902 42903 this.decomposeTransform(); 42904 }, 42905 42906 /** 42907 * @return {module:zrender/core/BoundingRect} 42908 */ 42909 getViewRect: function () { 42910 return this._viewRect; 42911 }, 42912 42913 /** 42914 * Get view rect after roam transform 42915 * @return {module:zrender/core/BoundingRect} 42916 */ 42917 getViewRectAfterRoam: function () { 42918 var rect = this.getBoundingRect().clone(); 42919 rect.applyTransform(this.transform); 42920 return rect; 42921 }, 42922 42923 /** 42924 * Convert a single (lon, lat) data item to (x, y) point. 42925 * @param {Array.<number>} data 42926 * @param {boolean} noRoam 42927 * @param {Array.<number>} [out] 42928 * @return {Array.<number>} 42929 */ 42930 dataToPoint: function (data, noRoam, out) { 42931 var transform = noRoam ? this._rawTransform : this.transform; 42932 out = out || []; 42933 return transform 42934 ? v2ApplyTransform$1(out, data, transform) 42935 : copy(out, data); 42936 }, 42937 42938 /** 42939 * Convert a (x, y) point to (lon, lat) data 42940 * @param {Array.<number>} point 42941 * @return {Array.<number>} 42942 */ 42943 pointToData: function (point) { 42944 var invTransform = this.invTransform; 42945 return invTransform 42946 ? v2ApplyTransform$1([], point, invTransform) 42947 : [point[0], point[1]]; 42948 }, 42949 42950 /** 42951 * @implements 42952 * see {module:echarts/CoodinateSystem} 42953 */ 42954 convertToPixel: curry(doConvert$1, 'dataToPoint'), 42955 42956 /** 42957 * @implements 42958 * see {module:echarts/CoodinateSystem} 42959 */ 42960 convertFromPixel: curry(doConvert$1, 'pointToData'), 42961 42962 /** 42963 * @implements 42964 * see {module:echarts/CoodinateSystem} 42965 */ 42966 containPoint: function (point) { 42967 return this.getViewRectAfterRoam().contain(point[0], point[1]); 42968 } 42969 42970 /** 42971 * @return {number} 42972 */ 42973 // getScalarScale: function () { 42974 // // Use determinant square root of transform to mutiply scalar 42975 // var m = this.transform; 42976 // var det = Math.sqrt(Math.abs(m[0] * m[3] - m[2] * m[1])); 42977 // return det; 42978 // } 42979 }; 42980 42981 mixin(View, Transformable); 42982 42983 function doConvert$1(methodName, ecModel, finder, value) { 42984 var seriesModel = finder.seriesModel; 42985 var coordSys = seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph. 42986 return coordSys === this ? coordSys[methodName](value) : null; 42987 } 42988 42989 /* 42990* Licensed to the Apache Software Foundation (ASF) under one 42991* or more contributor license agreements. See the NOTICE file 42992* distributed with this work for additional information 42993* regarding copyright ownership. The ASF licenses this file 42994* to you under the Apache License, Version 2.0 (the 42995* "License");
42995 you may not use this file except in compliance 42996* with the License. You may obtain a copy of the License at 42997* 42998* http://www.apache.org/licenses/LICENSE-2.0 42999* 43000* Unless required by applicable law or agreed to in writing, 43001* software distributed under the License is distributed on an 43002* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43003* KIND, either express or implied. See the License for the 43004* specific language governing permissions and limitations 43005* under the License. 43006*/ 43007 43008// Fix for Ã¥Ââ海诸å²⺠43009 43010 var geoCoord = [126, 25]; 43011 43012 var points$1 = [ 43013 [[0,3.5],[7,11.2],[15,11.9],[30,7],[42,0.7],[52,0.7], 43014 [56,7.7],[59,0.7],[64,0.7],[64,0],[5,0],[0,3.5]], 43015 [[13,16.1],[19,14.7],[16,21.7],[11,23.1],[13,16.1]], 43016 [[12,32.2],[14,38.5],[15,38.5],[13,32.2],[12,32.2]], 43017 [[16,47.6],[12,53.2],[13,53.2],[18,47.6],[16,47.6]], 43018 [[6,64.4],[8,70],[9,70],[8,64.4],[6,64.4]], 43019 [[23,82.6],[29,79.8],[30,79.8],[25,82.6],[23,82.6]], 43020 [[37,70.7],[43,62.3],[44,62.3],[39,70.7],[37,70.7]], 43021 [[48,51.1],[51,45.5],[53,45.5],[50,51.1],[48,51.1]], 43022 [[51,35],[51,28.7],[53,28.7],[53,35],[51,35]], 43023 [[52,22.4],[55,17.5],[56,17.5],[53,22.4],[52,22.4]], 43024 [[58,12.6],[62,7],[63,7],[60,12.6],[58,12.6]], 43025 [[0,3.5],[0,93.1],[64,93.1],[64,0],[63,0],[63,92.4], 43026 [1,92.4],[1,3.5],[0,3.5]] 43027 ]; 43028 43029 for (var i$1 = 0; i$1 < points$1.length; i$1++) { 43030 for (var k = 0; k < points$1[i$1].length; k++) { 43031 points$1[i$1][k][0] /= 10.5; 43032 points$1[i$1][k][1] /= -10.5 / 0.75; 43033 43034 points$1[i$1][k][0] += geoCoord[0]; 43035 points$1[i$1][k][1] += geoCoord[1]; 43036 } 43037 } 43038 43039 var fixNanhai = function (geo) { 43040 if (geo.map === 'china') { 43041 geo.regions.push(new Region( 43042 'Ã¥Ââ海诸å²âº', 43043 map(points$1, function (exterior) { 43044 return { 43045 type: 'polygon', 43046 exterior: exterior 43047 }; 43048 }), geoCoord 43049 )); 43050 } 43051 }; 43052 43053 /* 43054* Licensed to the Apache Software Foundation (ASF) under one 43055* or more contributor license agreements. See the NOTICE file 43056* distributed with this work for additional information 43057* regarding copyright ownership. The ASF licenses this file 43058* to you under the Apache License, Version 2.0 (the 43059* "License"); you may not use this file except in compliance 43060* with the License. You may obtain a copy of the License at 43061* 43062* http://www.apache.org/licenses/LICENSE-2.0 43063* 43064* Unless required by applicable law or agreed to in writing, 43065* software distributed under the License is distributed on an 43066* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43067* KIND, either express or implied. See the License for the 43068* specific language governing permissions and limitations 43069* under the License. 43070*/ 43071 43072 var coordsOffsetMap = { 43073 'Ã¥Ââ海诸å²âº' : [32, 80], 43074 // Ã¥â¦Â¨Ã¥âºÂ½ 43075 '广ä¸Å': [0, -10], 43076 'é¦â¢Ã¦Â¸Â¯': [10, 5], 43077 'æ¾³éâ¨': [-10, 10], 43078 //'Ã¥Åâ京': [-10, 0], 43079 '天津': [5, 5] 43080 }; 43081 43082 var fixTextCoord = function (geo) { 43083 each$1(geo.regions, function (region) { 43084 var coordFix = coordsOffsetMap[region.name]; 43085 if (coordFix) { 43086 var cp = region.center; 43087 cp[0] += coordFix[0] / 10.5; 43088 cp[1] += -coordFix[1] / (10.5 / 0.75); 43089 } 43090 }); 43091 }; 43092 43093 /* 43094* Licensed to the Apache Software Foundation (ASF) under one 43095* or more contributor license agreements. See the NOTICE file 43096* distributed with this work for additional information 43097* regarding copyright ownership. The ASF licenses this file 43098* to you under the Apache License, Version 2.0 (the 43099* "License"); you may not use this file except in compliance 43100* with the License. You may obtain a copy of the License at 43101* 43102* http://www.apache.org/licenses/LICENSE-2.0 43103* 43104* Unless required by applicable law or agreed to in writing, 43105* software distributed under the License is distributed on an 43106* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43107* KIND, either express or implied. See the License for the 43108* specific language governing permissions and limitations 43109* under the License. 43110*/ 43111 43112 var geoCoordMap = { 43113 'Russia': [100, 60], 43114 'United States': [-99, 38], 43115 'United States of America': [-99, 38] 43116 }; 43117 43118 var fixGeoCoord = function (geo) { 43119 each$1(geo.regions, function (region) { 43120 var geoCoord = geoCoordMap[region.name]; 43121 if (geoCoord) { 43122 var cp = region.center; 43123 cp[0] = geoCoord[0]; 43124 cp[1] = geoCoord[1]; 43125 } 43126 }); 43127 }; 43128 43129 /* 43130* Licensed to the Apache Software Foundation (ASF) under one 43131* or more contributor license agreements. See the NOTICE file 43132* distributed with this work for additional information 43133* regarding copyright ownership. The ASF licenses this file 43134* to you under the Apache License, Version 2.0 (the 43135* "License");
43135 you may not use this file except in compliance 43136* with the License. You may obtain a copy of the License at 43137* 43138* http://www.apache.org/licenses/LICENSE-2.0 43139* 43140* Unless required by applicable law or agreed to in writing, 43141* software distributed under the License is distributed on an 43142* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43143* KIND, either express or implied. See the License for the 43144* specific language governing permissions and limitations 43145* under the License. 43146*/ 43147 43148// Fix for éââé±¼å²⺠43149 43150// var Region = require('../Region'); 43151// var zrUtil = require('zrender/src/core/util'); 43152 43153// var geoCoord = [126, 25]; 43154 43155 var points$2 = [ 43156 [ 43157 [123.45165252685547, 25.73527164402261], 43158 [123.49731445312499, 25.73527164402261], 43159 [123.49731445312499, 25.750734064600884], 43160 [123.45165252685547, 25.750734064600884], 43161 [123.45165252685547, 25.73527164402261] 43162 ] 43163 ]; 43164 43165 var fixDiaoyuIsland = function (geo) { 43166 if (geo.map === 'china') { 43167 for (var i = 0, len = geo.regions.length; i < len; ++i) { 43168 if (geo.regions[i].name === 'å°湾') { 43169 geo.regions[i].geometries.push({ 43170 type: 'polygon', 43171 exterior: points$2[0] 43172 }); 43173 } 43174 } 43175 } 43176 }; 43177 43178 /* 43179* Licensed to the Apache Software Foundation (ASF) under one 43180* or more contributor license agreements. See the NOTICE file 43181* distributed with this work for additional information 43182* regarding copyright ownership. The ASF licenses this file 43183* to you under the Apache License, Version 2.0 (the 43184* "License"); you may not use this file except in compliance 43185* with the License. You may obtain a copy of the License at 43186* 43187* http://www.apache.org/licenses/LICENSE-2.0 43188* 43189* Unless required by applicable law or agreed to in writing, 43190* software distributed under the License is distributed on an 43191* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43192* KIND, either express or implied. See the License for the 43193* specific language governing permissions and limitations 43194* under the License. 43195*/ 43196 43197// Geo fix functions 43198 var geoFixFuncs = [ 43199 fixNanhai, 43200 fixTextCoord, 43201 fixGeoCoord, 43202 fixDiaoyuIsland 43203 ]; 43204 43205 /** 43206 * [Geo description] 43207 * @param {string} name Geo name 43208 * @param {string} map Map type 43209 * @param {Object} geoJson 43210 * @param {Object} [specialAreas] 43211 * Specify the positioned areas by left, top, width, height 43212 * @param {Object.<string, string>} [nameMap] 43213 * Specify name alias 43214 */ 43215 function Geo(name, map$$1, geoJson, specialAreas, nameMap) { 43216 43217 View.call(this, name); 43218 43219 /** 43220 * Map type 43221 * @type {string} 43222 */ 43223 this.map = map$$1; 43224 43225 this._nameCoordMap = createHashMap(); 43226 43227 this.loadGeoJson(geoJson, specialAreas, nameMap); 43228 } 43229 43230 Geo.prototype = { 43231 43232 constructor: Geo, 43233 43234 type: 'geo', 43235 43236 /** 43237 * @param {Array.<string>} 43238 * @readOnly 43239 */ 43240 dimensions: ['lng', 'lat'], 43241 43242 /** 43243 * If contain given lng,lat coord 43244 * @param {Array.<number>} 43245 * @readOnly 43246 */ 43247 containCoord: function (coord) { 43248 var regions = this.regions; 43249 for (var i = 0; i < regions.length; i++) { 43250 if (regions[i].contain(coord)) { 43251 return true; 43252 } 43253 } 43254 return false; 43255 }, 43256 /** 43257 * @param {Object} geoJson 43258 * @param {Object} [specialAreas] 43259 * Specify the positioned areas by left, top, width, height 43260 * @param {Object.<string, string>} [nameMap] 43261 * Specify name alias 43262 */ 43263 loadGeoJson: function (geoJson, specialAreas, nameMap) { 43264 // https://jsperf.com/try-catch-performance-overhead 43265 try { 43266 this.regions = geoJson ? parseGeoJson$1(geoJson) : []; 43267 } 43268 catch (e) { 43269 throw 'Invalid geoJson format\n' + e.message; 43270 } 43271 specialAreas = specialAreas || {}; 43272 nameMap = nameMap || {}; 43273 var regions = this.regions; 43274 var regionsMap = createHashMap(); 43275 for (var i = 0; i < regions.length; i++) { 43276 var regionName = regions[i].name; 43277 // Try use the alias in nameMap 43278 regionName = nameMap.hasOwnProperty(regionName) ? nameMap[regionName] : regionName; 43279 regions[i].name = regionName; 43280 43281 regionsMap.set(regionName, regions[i]);
43282 // Add geoJson 43283 this.addGeoCoord(regionName, regions[i].center); 43284 43285 // Some area like Alaska in USA map needs to be tansformed 43286 // to look better 43287 var specialArea = specialAreas[regionName]; 43288 if (specialArea) { 43289 regions[i].transformTo( 43290 specialArea.left, specialArea.top, specialArea.width, specialArea.height 43291 ); 43292 } 43293 } 43294 43295 this._regionsMap = regionsMap; 43296 43297 this._rect = null; 43298 43299 each$1(geoFixFuncs, function (fixFunc) { 43300 fixFunc(this); 43301 }, this); 43302 }, 43303 43304 // Overwrite 43305 transformTo: function (x, y, width, height) { 43306 var rect = this.getBoundingRect(); 43307 43308 rect = rect.clone(); 43309 // Longitute is inverted 43310 rect.y = -rect.y - rect.height; 43311 43312 var rawTransformable = this._rawTransformable; 43313 43314 rawTransformable.transform = rect.calculateTransform( 43315 new BoundingRect(x, y, width, height) 43316 ); 43317 43318 rawTransformable.decomposeTransform(); 43319 43320 var scale = rawTransformable.scale; 43321 scale[1] = -scale[1]; 43322 43323 rawTransformable.updateTransform(); 43324 43325 this._updateTransform(); 43326 }, 43327 43328 /** 43329 * @param {string} name 43330 * @return {module:echarts/coord/geo/Region} 43331 */ 43332 getRegion: function (name) { 43333 return this._regionsMap.get(name); 43334 }, 43335 43336 getRegionByCoord: function (coord) { 43337 var regions = this.regions; 43338 for (var i = 0; i < regions.length; i++) { 43339 if (regions[i].contain(coord)) { 43340 return regions[i]; 43341 } 43342 } 43343 }, 43344 43345 /** 43346 * Add geoCoord for indexing by name 43347 * @param {string} name 43348 * @param {Array.<number>} geoCoord 43349 */ 43350 addGeoCoord: function (name, geoCoord) { 43351 this._nameCoordMap.set(name, geoCoord); 43352 }, 43353 43354 /** 43355 * Get geoCoord by name 43356 * @param {string} name 43357 * @return {Array.<number>} 43358 */ 43359 getGeoCoord: function (name) { 43360 return this._nameCoordMap.get(name); 43361 }, 43362 43363 // Overwrite 43364 getBoundingRect: function () { 43365 if (this._rect) { 43366 return this._rect; 43367 } 43368 var rect; 43369 43370 var regions = this.regions; 43371 for (var i = 0; i < regions.length; i++) { 43372 var regionRect = regions[i].getBoundingRect(); 43373 rect = rect || regionRect.clone(); 43374 rect.union(regionRect); 43375 } 43376 // FIXME Always return new ? 43377 return (this._rect = rect || new BoundingRect(0, 0, 0, 0)); 43378 }, 43379 43380 /** 43381 * @param {string|Array.<number>} data 43382 * @param {boolean} noRoam 43383 * @param {Array.<number>} [out] 43384 * @return {Array.<number>} 43385 */ 43386 dataToPoint: function (data, noRoam, out) { 43387 if (typeof data === 'string') { 43388 // Map area name to geoCoord 43389 data = this.getGeoCoord(data); 43390 } 43391 if (data) { 43392 return View.prototype.dataToPoint.call(this, data, noRoam, out); 43393 } 43394 }, 43395 43396 /** 43397 * @inheritDoc 43398 */ 43399 convertToPixel: curry(doConvert, 'dataToPoint'), 43400 43401 /** 43402 * @inheritDoc 43403 */ 43404 convertFromPixel: curry(doConvert, 'pointToData') 43405 43406 }; 43407 43408 mixin(Geo, View); 43409 43410 function doConvert(methodName, ecModel, finder, value) { 43411 var geoModel = finder.geoModel; 43412 var seriesModel = finder.seriesModel; 43413 43414 var coordSys = geoModel 43415 ? geoModel.coordinateSystem 43416 : seriesModel 43417 ? ( 43418 seriesModel.coordinateSystem // For map. 43419 || (seriesModel.getReferringComponents('geo')[0] || {}).coordinateSystem 43420 ) 43421 : null; 43422 43423 return coordSys === this ? coordSys[methodName](value) : null; 43424 } 43425 43426 /* 43427* Licensed to the Apache Software Foundation (ASF) under one 43428* or more contributor license agreements. See the NOTICE file 43429* distributed with this work for additional information 43430* regarding copyright ownership. The ASF licenses this file 43431* to you under the Apache License, Version 2.0 (the 43432* "License");
43432 you may not use this file except in compliance 43433* with the License. You may obtain a copy of the License at 43434* 43435* http://www.apache.org/licenses/LICENSE-2.0 43436* 43437* Unless required by applicable law or agreed to in writing, 43438* software distributed under the License is distributed on an 43439* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43440* KIND, either express or implied. See the License for the 43441* specific language governing permissions and limitations 43442* under the License. 43443*/ 43444 43445 /** 43446 * Resize method bound to the geo 43447 * @param {module:echarts/coord/geo/GeoModel|module:echarts/chart/map/MapModel} geoModel 43448 * @param {module:echarts/ExtensionAPI} api 43449 */ 43450 function resizeGeo(geoModel, api) { 43451 43452 var boundingCoords = geoModel.get('boundingCoords'); 43453 if (boundingCoords != null) { 43454 var leftTop = boundingCoords[0]; 43455 var rightBottom = boundingCoords[1]; 43456 if (isNaN(leftTop[0]) || isNaN(leftTop[1]) || isNaN(rightBottom[0]) || isNaN(rightBottom[1])) { 43457 if (__DEV__) { 43458 console.error('Invalid boundingCoords'); 43459 } 43460 } 43461 else { 43462 this.setBoundingRect(leftTop[0], leftTop[1], rightBottom[0] - leftTop[0], rightBottom[1] - leftTop[1]); 43463 } 43464 } 43465 43466 var rect = this.getBoundingRect(); 43467 43468 var boxLayoutOption; 43469 43470 var center = geoModel.get('layoutCenter'); 43471 var size = geoModel.get('layoutSize'); 43472 43473 var viewWidth = api.getWidth(); 43474 var viewHeight = api.getHeight(); 43475 43476 var aspectScale = geoModel.get('aspectScale') || 0.75; 43477 var aspect = rect.width / rect.height * aspectScale; 43478 43479 var useCenterAndSize = false; 43480 43481 if (center && size) { 43482 center = [ 43483 parsePercent$1(center[0], viewWidth), 43484 parsePercent$1(center[1], viewHeight) 43485 ]; 43486 size = parsePercent$1(size, Math.min(viewWidth, viewHeight)); 43487 43488 if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) { 43489 useCenterAndSize = true; 43490 } 43491 else { 43492 if (__DEV__) { 43493 console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.'); 43494 } 43495 } 43496 } 43497 43498 var viewRect; 43499 if (useCenterAndSize) { 43500 var viewRect = {}; 43501 if (aspect > 1) {
43502 // Width is same with size 43503 viewRect.width = size; 43504 viewRect.height = size / aspect; 43505 } 43506 else { 43507 viewRect.height = size; 43508 viewRect.width = size * aspect; 43509 } 43510 viewRect.y = center[1] - viewRect.height / 2; 43511 viewRect.x = center[0] - viewRect.width / 2; 43512 } 43513 else { 43514 // Use left/top/width/height 43515 boxLayoutOption = geoModel.getBoxLayoutParams(); 43516 43517 // 0.75 rate 43518 boxLayoutOption.aspect = aspect; 43519 43520 viewRect = getLayoutRect(boxLayoutOption, { 43521 width: viewWidth, 43522 height: viewHeight 43523 }); 43524 } 43525 43526 this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height); 43527 43528 this.setCenter(geoModel.get('center')); 43529 this.setZoom(geoModel.get('zoom')); 43530 } 43531 43532 /** 43533 * @param {module:echarts/coord/Geo} geo 43534 * @param {module:echarts/model/Model} model 43535 * @inner 43536 */ 43537 function setGeoCoords(geo, model) { 43538 each$1(model.get('geoCoord'), function (geoCoord, name) { 43539 geo.addGeoCoord(name, geoCoord); 43540 }); 43541 } 43542 43543 if (__DEV__) { 43544 var mapNotExistsError = function (name) { 43545 console.error('Map ' + name + ' not exists. You can download map file on http://echarts.baidu.com/download-map.html'); 43546 }; 43547 } 43548 43549 var geoCreator = { 43550 43551 // For deciding which dimensions to use when creating list data 43552 dimensions: Geo.prototype.dimensions, 43553 43554 create: function (ecModel, api) { 43555 var geoList = []; 43556 43557 // FIXME Create each time may be slow 43558 ecModel.eachComponent('geo', function (geoModel, idx) { 43559 var name = geoModel.get('map'); 43560 var mapData = getMap(name); 43561 if (__DEV__) { 43562 if (!mapData) { 43563 mapNotExistsError(name); 43564 } 43565 } 43566 var geo = new Geo( 43567 name + idx, name, 43568 mapData && mapData.geoJson, mapData && mapData.specialAreas, 43569 geoModel.get('nameMap') 43570 ); 43571 geo.zoomLimit = geoModel.get('scaleLimit'); 43572 geoList.push(geo); 43573 43574 setGeoCoords(geo, geoModel); 43575 43576 geoModel.coordinateSystem = geo; 43577 geo.model = geoModel; 43578 43579 // Inject resize method 43580 geo.resize = resizeGeo; 43581 43582 geo.resize(geoModel, api); 43583 }); 43584 43585 ecModel.eachSeries(function (seriesModel) { 43586 var coordSys = seriesModel.get('coordinateSystem'); 43587 if (coordSys === 'geo') { 43588 var geoIndex = seriesModel.get('geoIndex') || 0; 43589 seriesModel.coordinateSystem = geoList[geoIndex]; 43590 } 43591 }); 43592 43593 // If has map series 43594 var mapModelGroupBySeries = {}; 43595 43596 ecModel.eachSeriesByType('map', function (seriesModel) { 43597 if (!seriesModel.getHostGeoModel()) { 43598 var mapType = seriesModel.getMapType(); 43599 mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || [];
43600 mapModelGroupBySeries[mapType].push(seriesModel); 43601 } 43602 }); 43603 43604 each$1(mapModelGroupBySeries, function (mapSeries, mapType) { 43605 var mapData = getMap(mapType); 43606 if (__DEV__) { 43607 if (!mapData) { 43608 mapNotExistsError(mapSeries[0].get('map')); 43609 } 43610 } 43611 43612 var nameMapList = map(mapSeries, function (singleMapSeries) { 43613 return singleMapSeries.get('nameMap'); 43614 }); 43615 var geo = new Geo( 43616 mapType, mapType, 43617 mapData && mapData.geoJson, mapData && mapData.specialAreas, 43618 mergeAll(nameMapList) 43619 ); 43620 geo.zoomLimit = retrieve.apply(null, map(mapSeries, function (singleMapSeries) { 43621 return singleMapSeries.get('scaleLimit'); 43622 })); 43623 geoList.push(geo); 43624 43625 // Inject resize method 43626 geo.resize = resizeGeo; 43627 43628 geo.resize(mapSeries[0], api); 43629 43630 each$1(mapSeries, function (singleMapSeries) { 43631 singleMapSeries.coordinateSystem = geo; 43632 43633 setGeoCoords(geo, singleMapSeries); 43634 }); 43635 }); 43636 43637 return geoList; 43638 }, 43639 43640 /** 43641 * Fill given regions array 43642 * @param {Array.<Object>} originRegionArr 43643 * @param {string} mapName 43644 * @param {Object} [nameMap] 43645 * @return {Array} 43646 */ 43647 getFilledRegions: function (originRegionArr, mapName, nameMap) { 43648 // Not use the original 43649 var regionsArr = (originRegionArr || []).slice(); 43650 nameMap = nameMap || {}; 43651 43652 var map$$1 = getMap(mapName); 43653 var geoJson = map$$1 && map$$1.geoJson; 43654 if (!geoJson) { 43655 if (__DEV__) { 43656 mapNotExistsError(mapName); 43657 } 43658 return originRegionArr; 43659 } 43660 43661 var dataNameMap = createHashMap(); 43662 var features = geoJson.features; 43663 for (var i = 0; i < regionsArr.length; i++) { 43664 dataNameMap.set(regionsArr[i].name, regionsArr[i]); 43665 } 43666 43667 for (var i = 0; i < features.length; i++) { 43668 var name = features[i].properties.name; 43669 if (!dataNameMap.get(name)) { 43670 if (nameMap.hasOwnProperty(name)) { 43671 name = nameMap[name]; 43672 } 43673 regionsArr.push({ 43674 name: name 43675 }); 43676 } 43677 } 43678 return regionsArr; 43679 } 43680 }; 43681 43682 registerCoordinateSystem('geo', geoCreator); 43683 43684 /* 43685* Licensed to the Apache Software Foundation (ASF) under one 43686* or more contributor license agreements. See the NOTICE file 43687* distributed with this work for additional information 43688* regarding copyright ownership. The ASF licenses this file 43689* to you under the Apache License, Version 2.0 (the 43690* "License"); you may not use this file except in compliance 43691* with the License. You may obtain a copy of the License at 43692* 43693* http://www.apache.org/licenses/LICENSE-2.0 43694* 43695* Unless required by applicable law or agreed to in writing, 43696* software distributed under the License is distributed on an 43697* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43698* KIND, either express or implied. See the License for the 43699* specific language governing permissions and limitations 43700* under the License. 43701*/ 43702 43703 var MapSeries = SeriesModel.extend({ 43704 43705 type: 'series.map', 43706 43707 dependencies: ['geo'], 43708 43709 layoutMode: 'box', 43710 43711 /** 43712 * Only first map series of same mapType will drawMap 43713 * @type {boolean} 43714 */ 43715 needsDrawMap: false, 43716 43717 /** 43718 * Group of all map series with same mapType 43719 * @type {boolean} 43720 */ 43721 seriesGroup: [], 43722 43723 init: function (option) { 43724 43725 // this._fillOption(option, this.getMapType()); 43726 // this.option = option; 43727 43728 MapSeries.superApply(this, 'init', arguments); 43729
43730 this.updateSelectedMap(this._createSelectableList()); 43731 }, 43732 43733 getInitialData: function (option) { 43734 return createListSimply(this, ['value']); 43735 }, 43736 43737 mergeOption: function (newOption) { 43738 // this._fillOption(newOption, this.getMapType()); 43739 43740 MapSeries.superApply(this, 'mergeOption', arguments); 43741 43742 this.updateSelectedMap(this._createSelectableList()); 43743 }, 43744 43745 _createSelectableList: function () { 43746 var data = this.getRawData(); 43747 var valueDim = data.mapDimension('value'); 43748 var targetList = []; 43749 for (var i = 0, len = data.count(); i < len; i++) { 43750 targetList.push({ 43751 name: data.getName(i), 43752 value: data.get(valueDim, i), 43753 selected: retrieveRawAttr(data, i, 'selected') 43754 }); 43755 } 43756 43757 targetList = geoCreator.getFilledRegions(targetList, this.getMapType(), this.option.nameMap); 43758 43759 return targetList; 43760 }, 43761 43762 /** 43763 * If no host geo model, return null, which means using a 43764 * inner exclusive geo model. 43765 */ 43766 getHostGeoModel: function () { 43767 var geoIndex = this.option.geoIndex; 43768 return geoIndex != null 43769 ? this.dependentModels.geo[geoIndex] 43770 : null; 43771 }, 43772 43773 getMapType: function () { 43774 return (this.getHostGeoModel() || this).option.map; 43775 }, 43776 43777 _fillOption: function (option, mapName) { 43778 // Shallow clone 43779 // option = zrUtil.extend({}, option); 43780 43781 // option.data = geoCreator.getFilledRegions(option.data, mapName, option.nameMap); 43782 43783 // return option; 43784 }, 43785 43786 getRawValue: function (dataIndex) { 43787 // Use value stored in data instead because it is calculated from multiple series 43788 // FIXME Provide all value of multiple series ? 43789 var data = this.getData(); 43790 return data.get(data.mapDimension('value'), dataIndex); 43791 }, 43792 43793 /** 43794 * Get model of region 43795 * @param {string} name 43796 * @return {module:echarts/model/Model} 43797 */ 43798 getRegionModel: function (regionName) { 43799 var data = this.getData(); 43800 return data.getItemModel(data.indexOfName(regionName)); 43801 }, 43802 43803 /** 43804 * Map tooltip formatter 43805 * 43806 * @param {number} dataIndex 43807 */ 43808 formatTooltip: function (dataIndex) { 43809 // FIXME orignalData and data is a bit confusing 43810 var data = this.getData(); 43811 var formattedValue = addCommas(this.getRawValue(dataIndex)); 43812 var name = data.getName(dataIndex); 43813 43814 var seriesGroup = this.seriesGroup; 43815 var seriesNames = []; 43816 for (var i = 0; i < seriesGroup.length; i++) { 43817 var otherIndex = seriesGroup[i].originalData.indexOfName(name); 43818 var valueDim = data.mapDimension('value'); 43819 if (!isNaN(seriesGroup[i].originalData.get(valueDim, otherIndex))) { 43820 seriesNames.push( 43821 encodeHTML(seriesGroup[i].name) 43822 ); 43823 } 43824 } 43825 43826 return seriesNames.join(', ') + '<br />' 43827 + encodeHTML(name + ' : ' + formattedValue); 43828 }, 43829 43830 /** 43831 * @implement 43832 */ 43833 getTooltipPosition: function (dataIndex) { 43834 if (dataIndex != null) { 43835 var name = this.getData().getName(dataIndex); 43836 var geo = this.coordinateSystem; 43837 var region = geo.getRegion(name); 43838 43839 return region && geo.dataToPoint(region.center); 43840 } 43841 }, 43842 43843 setZoom: function (zoom) { 43844 this.option.zoom = zoom; 43845 }, 43846 43847 setCenter: function (center) { 43848 this.option.center = center; 43849 }, 43850 43851 defaultOption: { 43852 // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 43853 zlevel: 0, 43854 // äºÅ级å±âå 43855 z: 2, 43856 43857 coordinateSystem: 'geo', 43858 43859 // map should be explicitly specified since ec3. 43860 map: '', 43861 43862 // If `geoIndex` is not specified, a exclusive geo will be 43863 // created. Otherwise use the specified geo component, and 43864 // `map` and `mapType` are ignored. 43865 // geoIndex: 0, 43866 43867 // 'center' | 'left' | 'right' | 'x%' | {number} 43868 left: 'center', 43869 // 'center' | 'top' | 'bottom' | 'x%' | {number} 43870 top: 'center', 43871 // right 43872 // bottom 43873 // width: 43874 // height 43875 43876 // Aspect is width / height. Inited to be geoJson bbox aspect 43877 // This parameter is used for scale this aspect 43878 aspectScale: 0.75, 43879 43880 ///// Layout with center and size 43881 // If you wan't to put map in a fixed size box with right aspect ratio 43882 // This two properties may more conveninet 43883 // layoutCenter: [50%, 50%] 43884 // layoutSize: 100 43885 43886 43887 // æâ¢Â°Ã¥â¬Â¼Ã¥ÂËå¹¶æâ¹å¼Âï¼Åé»ËèÂ
43887®Â¤Ã¥Å Ã¥âÅï¼Åå¯éâ¬â°Ã¤Â¸ÂºÃ¯Â¼Å¡ 43888 // 'sum' | 'average' | 'max' | 'min' 43889 // mapValueCalculation: 'sum', 43890 // åŰåâºÂ¾Ã¦â¢Â°Ã¥â¬Â¼Ã¨Â®Â¡Ã§Â®âç»âæžÅå°Âæâ¢Â°Ã§Â²Â¾Ã¥ÂºÂ¦ 43891 // mapValuePrecision: 0, 43892 43893 43894 // æË¾ç¤ºåâºÂ¾Ã¤Â¾â¹Ã©Â¢Åèâ°Â²Ã¦ â¡Ã¨Â¯â ï¼Ëç³»åËâæ â¡Ã¨Â¯â çšâå°ÂÃ¥Åâ çâ¹ï¼â°Ã¯Â¼ÅÃ¥âºÂ¾Ã¤Â¾â¹Ã¥Â¼â¬Ã¥Â¯æâ¶æÅâ°Ã¦â¢Ë 43895 showLegendSymbol: true, 43896 // éâ¬â°Ã¦â¹Â©Ã¦Â¨Â¡Ã¥Â¼Âï¼Åé»Ë认åâ¦Â³Ã©âÂï¼Åå¯éâ¬â°singleï¼Åmultiple 43897 // selectedMode: false, 43898 dataRangeHoverLink: true, 43899 // æË¯å¦å¼â¬Ã¥Â¯ç¼©æâ¾åŠ漫游模张43900 // roam: false, 43901 43902 // Define left-top, right-bottom coords to control view 43903 // For example, [ [180, 90], [-180, -90] ], 43904 // higher priority than center and zoom 43905 boundingCoords: null, 43906 43907 // Default on center of map 43908 center: null, 43909 43910 zoom: 1, 43911 43912 scaleLimit: null, 43913 43914 label: { 43915 show: false, 43916 color: '#000' 43917 }, 43918 // scaleLimit: null, 43919 itemStyle: { 43920 borderWidth: 0.5, 43921 borderColor: '#444', 43922 areaColor: '#eee' 43923 }, 43924 43925 emphasis: { 43926 label: { 43927 show: true, 43928 color: 'rgb(100,0,0)' 43929 }, 43930 itemStyle: { 43931 areaColor: 'rgba(255,215,0,0.8)' 43932 } 43933 } 43934 } 43935 43936 }); 43937 43938 mixin(MapSeries, selectableMixin); 43939 43940 /* 43941* Licensed to the Apache Software Foundation (ASF) under one 43942* or more contributor license agreements. See the NOTICE file 43943* distributed with this work for additional information 43944* regarding copyright ownership. The ASF licenses this file 43945* to you under the Apache License, Version 2.0 (the 43946* "License"); you may not use this file except in compliance 43947* with the License. You may obtain a copy of the License at 43948* 43949* http://www.apache.org/licenses/LICENSE-2.0 43950* 43951* Unless required by applicable law or agreed to in writing, 43952* software distributed under the License is distributed on an 43953* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 43954* KIND, either express or implied. See the License for the 43955* specific language governing permissions and limitations 43956* under the License. 43957*/ 43958 43959 var ATTR = '\0_ec_interaction_mutex'; 43960 43961 function take(zr, resourceKey, userKey) { 43962 var store = getStore(zr); 43963 store[resourceKey] = userKey; 43964 } 43965 43966 function release(zr, resourceKey, userKey) { 43967 var store = getStore(zr); 43968 var uKey = store[resourceKey]; 43969 43970 if (uKey === userKey) { 43971 store[resourceKey] = null; 43972 } 43973 } 43974 43975 function isTaken(zr, resourceKey) { 43976 return !!getStore(zr)[resourceKey]; 43977 } 43978 43979 function getStore(zr) { 43980 return zr[ATTR] || (zr[ATTR] = {}); 43981 } 43982 43983 /** 43984 * payload: { 43985 * type: 'takeGlobalCursor', 43986 * key: 'dataZoomSelect', or 'brush', or ..., 43987 * If no userKey, release global cursor. 43988 * } 43989 */ 43990 registerAction( 43991 {type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'}, 43992 function () {} 43993 ); 43994 43995 /* 43996* Licensed to the Apache Software Foundation (ASF) under one 43997* or more contributor license agreements. See the NOTICE file 43998* distributed with this work for additional information 43999* regarding copyright ownership. The ASF licenses this file 44000* to you under the Apache License, Version 2.0 (the 44001* "License"); you may not use this file except in compliance 44002* with the License. You may obtain a copy of the License at 44003* 44004* http://www.apache.org/licenses/LICENSE-2.0 44005* 44006* Unless required by applicable law or agreed to in writing, 44007* software distributed under the License is distributed on an 44008* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44009* KIND, either express or implied. See the License for the 44010* specific language governing permissions and limitations 44011* under the License. 44012*/ 44013 44014 /** 44015 * @alias module:echarts/component/helper/RoamController 44016 * @constructor 44017 * @mixin {module:zrender/mixin/Eventful} 44018 * 44019 * @param {module:zrender/zrender~ZRender} zr 44020 */ 44021 function RoamController(zr) { 44022 44023 /** 44024 * @type {Function} 44025 */ 44026 this.pointerChecker; 44027 44028 /** 44029 * @type {module:zrender} 44030 */ 44031 this._zr = zr; 44032 44033 /** 44034 * @type {Object} 44035 */ 44036 this._opt = {}; 44037 44038 // Avoid two roamController bind the same handler 44039 var bind$$1 = bind; 44040 var mousedownHandler = bind$$1(mousedown, this); 44041 var mousemoveHandler = bind$$1(mousemove, this); 44042 var mouseupHandler = bind$$1(mouseup, this); 44043 var mousewheelHandler = bind$$1(mousewheel, this); 44044 var pinchHandler = bind$$1(pinch, this); 44045 44046 Eventful.call(this); 44047 44048 /** 44049 * @param {Function} pointerChecker 44050 * input: x, y 44051 * output: boolean 44052 */ 44053 this.setPointerChecker = function (pointerChecker) { 44054 this.pointerChecker = pointerChecker; 44055 }; 44056 44057 /** 44058 * Notice: only enable needed types. For example, if 'zoom' 44059 * is not needed, 'zoom' should not be enabled, otherwise 44060 * default mousewheel behaviour (scroll page) will be disabled. 44061 * 44062 * @param {boolean|string} [controlType=true] Specify the control type, 44063 * which can be null/undefined or true/false 44064 * or 'pan/move' or 'zoom'/'scale' 44065 * @param {Object} [opt] 44066 * @param {Object} [opt.zoomOnMouseWheel=true] 44067 * @param {Object} [opt.moveOnMouseMove=true] 44068 * @param {Object} [opt.preventDefaultMouseMove=true] When pan. 44069 */ 44070 this.enable = function (controlType, opt) { 44071 44072 // Disable previous first 44073 this.disable(); 44074 44075 this._opt = defaults(clone(opt) || {}, { 44076 zoomOnMouseWheel: true, 44077 moveOnMouseMove: true, 44078 preventDefaultMouseMove: true 44079 }); 44080 44081 if (controlType == null) { 44082 controlType = true; 44083 } 44084 44085 if (controlType === true || (controlType === 'move' || controlType === 'pan')) { 44086 zr.on('mousedown', mousedownHandler); 44087 zr.on('mousemove', mousemoveHandler); 44088 zr.on('mouseup', mouseupHandler); 44089 } 44090 if (controlType === true || (controlType === 'scale' || controlType === 'zoom')) { 44091 zr.on('mousewheel', mousewheelHandler); 44092 zr.on('pinch', pinchHandler); 44093 } 44094 }; 44095 44096 this.disable = function () { 44097 zr.off('mousedown', mousedownHandler); 44098 zr.off('mousemove', mousemoveHandler); 44099 zr.off('mouseup', mouseupHandler); 44100 zr.off('mousewheel', mousewheelHandler); 44101 zr.off('pinch', pinchHandler); 44102 }; 44103 44104 this.dispose = this.disable; 44105 44106 this.isDragging = function () { 44107 return this._dragging; 44108 }; 44109 44110 this.isPinching = function () { 44111 return this._pinching; 44112 }; 44113 } 44114 44115 mixin(RoamController, Eventful); 44116 44117 44118 function mousedown(e) { 44119 if (notLeftMouse(e) 44120 || (e.target && e.target.draggable) 44121 ) { 44122 return; 44123 } 44124 44125 var x = e.offsetX; 44126 var y = e.offsetY; 44127 44128 // Only check on mosedown, but not mousemove. 44129 // Mouse can be out of target when mouse moving. 44130 if (this.pointerChecker && this.pointerChecker(e, x, y)) { 44131 this._x = x; 44132 this._y = y; 44133 this._dragging = true; 44134 } 44135 } 44136 44137 function mousemove(e) { 44138 if (notLeftMouse(e) 44139 || !checkKeyBinding(this, 'moveOnMouseMove', e) 44140 || !this._dragging 44141 || e.gestureEvent === 'pinch' 44142 || isTaken(this._zr, 'globalPan') 44143 ) { 44144 return; 44145 } 44146 44147 var x = e.offsetX; 44148 var y = e.offsetY; 44149 44150 var oldX = this._x; 44151 var oldY = this._y; 44152 44153 var dx = x - oldX; 44154 var dy = y - oldY; 44155 44156 this._x = x; 44157 this._y = y; 44158 44159 this._opt.preventDefaultMouseMove && stop(e.event); 44160 44161 this.trigger('pan', dx, dy, oldX, oldY, x, y); 44162 } 44163 44164 function mouseup(e) { 44165 if (!notLeftMouse(e)) { 44166 this._dragging = false; 44167 } 44168 } 44169 44170 function mousewheel(e) { 44171 // wheelDelta maybe -0 in chrome mac. 44172 if (!checkKeyBinding(this, 'zoomOnMouseWheel', e) || e.wheelDelta === 0) { 44173 return; 44174 } 44175 44176 // Convenience: 44177 // Mac and VM Windows on Mac: scroll up: zoom out.
44178 // Windows: scroll up: zoom in. 44179 var zoomDelta = e.wheelDelta > 0 ? 1.1 : 1 / 1.1; 44180 zoom.call(this, e, zoomDelta, e.offsetX, e.offsetY); 44181 } 44182 44183 function pinch(e) { 44184 if (isTaken(this._zr, 'globalPan')) { 44185 return; 44186 } 44187 var zoomDelta = e.pinchScale > 1 ? 1.1 : 1 / 1.1; 44188 zoom.call(this, e, zoomDelta, e.pinchX, e.pinchY); 44189 } 44190 44191 function zoom(e, zoomDelta, zoomX, zoomY) { 44192 if (this.pointerChecker && this.pointerChecker(e, zoomX, zoomY)) { 44193 // When mouse is out of roamController rect, 44194 // default befavoius should not be be disabled, otherwise 44195 // page sliding is disabled, contrary to expectation. 44196 stop(e.event); 44197 44198 this.trigger('zoom', zoomDelta, zoomX, zoomY); 44199 } 44200 } 44201 44202 function checkKeyBinding(roamController, prop, e) { 44203 var setting = roamController._opt[prop]; 44204 return setting 44205 && (!isString(setting) || e.event[setting + 'Key']); 44206 } 44207 44208 /* 44209* Licensed to the Apache Software Foundation (ASF) under one 44210* or more contributor license agreements. See the NOTICE file 44211* distributed with this work for additional information 44212* regarding copyright ownership. The ASF licenses this file 44213* to you under the Apache License, Version 2.0 (the 44214* "License"); you may not use this file except in compliance 44215* with the License. You may obtain a copy of the License at 44216* 44217* http://www.apache.org/licenses/LICENSE-2.0 44218* 44219* Unless required by applicable law or agreed to in writing, 44220* software distributed under the License is distributed on an 44221* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44222* KIND, either express or implied. See the License for the 44223* specific language governing permissions and limitations 44224* under the License. 44225*/ 44226 44227 44228 /** 44229 * For geo and graph. 44230 * 44231 * @param {Object} controllerHost 44232 * @param {module:zrender/Element} controllerHost.target 44233 */ 44234 function updateViewOnPan(controllerHost, dx, dy) { 44235 var target = controllerHost.target; 44236 var pos = target.position; 44237 pos[0] += dx; 44238 pos[1] += dy; 44239 target.dirty(); 44240 } 44241 44242 /** 44243 * For geo and graph. 44244 * 44245 * @param {Object} controllerHost 44246 * @param {module:zrender/Element} controllerHost.target 44247 * @param {number} controllerHost.zoom 44248 * @param {number} controllerHost.zoomLimit like: {min: 1, max: 2} 44249 */ 44250 function updateViewOnZoom(controllerHost, zoomDelta, zoomX, zoomY) { 44251 var target = controllerHost.target; 44252 var zoomLimit = controllerHost.zoomLimit; 44253 var pos = target.position; 44254 var scale = target.scale; 44255 44256 var newZoom = controllerHost.zoom = controllerHost.zoom || 1; 44257 newZoom *= zoomDelta; 44258 if (zoomLimit) { 44259 var zoomMin = zoomLimit.min || 0; 44260 var zoomMax = zoomLimit.max || Infinity; 44261 newZoom = Math.max( 44262 Math.min(zoomMax, newZoom), 44263 zoomMin 44264 ); 44265 } 44266 var zoomScale = newZoom / controllerHost.zoom; 44267 controllerHost.zoom = newZoom; 44268 // Keep the mouse center when scaling 44269 pos[0] -= (zoomX - pos[0]) * (zoomScale - 1); 44270 pos[1] -= (zoomY - pos[1]) * (zoomScale - 1); 44271 scale[0] *= zoomScale; 44272 scale[1] *= zoomScale; 44273 44274 target.dirty(); 44275 } 44276 44277 /* 44278* Licensed to the Apache Software Foundation (ASF) under one 44279* or more contributor license agreements. See the NOTICE file 44280* distributed with this work for additional information 44281* regarding copyright ownership. The ASF licenses this file 44282* to you under the Apache License, Version 2.0 (the 44283* "License"); you may not use this file except in compliance 44284* with the License. You may obtain a copy of the License at 44285* 44286* http://www.apache.org/licenses/LICENSE-2.0 44287* 44288* Unless required by applicable law or agreed to in writing, 44289* software distributed under the License is distributed on an 44290* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44291* KIND, either express or implied. See the License for the 44292* specific language governing permissions and limitations 44293* under the License. 44294*/ 44295 44296 44297 var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1}; 44298 44299 /** 44300 * Avoid that: mouse click on a elements that is over geo or graph, 44301 * but roam is triggered. 44302 */ 44303 function onIrrelevantElement(e, api, targetCoordSysModel) { 44304 var model = api.getComponentByElement(e.topTarget); 44305 // If model is axisModel, it works only if it is injected with coordinateSystem. 44306 var coordSys = model && model.coordinateSystem; 44307 return model 44308 && model !== targetCoordSysModel 44309 && !IRRELEVANT_EXCLUDES[model.mainType] 44310 && (coordSys && coordSys.model !== targetCoordSysModel); 44311 } 44312 44313 /* 44314* Licensed to the Apache Software Foundation (ASF) under one 44315* or more contributor license agreements. See the NOTICE file 44316* distributed with this work for additional information 44317* regarding copyright ownership. The ASF licenses this file 44318* to you under the Apache License, Version 2.0 (the 44319* "License");
44319 you may not use this file except in compliance 44320* with the License. You may obtain a copy of the License at 44321* 44322* http://www.apache.org/licenses/LICENSE-2.0 44323* 44324* Unless required by applicable law or agreed to in writing, 44325* software distributed under the License is distributed on an 44326* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44327* KIND, either express or implied. See the License for the 44328* specific language governing permissions and limitations 44329* under the License. 44330*/ 44331 44332 function getFixedItemStyle(model, scale) { 44333 var itemStyle = model.getItemStyle(); 44334 var areaColor = model.get('areaColor'); 44335 44336 // If user want the color not to be changed when hover, 44337 // they should both set areaColor and color to be null. 44338 if (areaColor != null) { 44339 itemStyle.fill = areaColor; 44340 } 44341 44342 return itemStyle; 44343 } 44344 44345 function updateMapSelectHandler(mapDraw, mapOrGeoModel, group, api, fromView) { 44346 group.off('click'); 44347 group.off('mousedown'); 44348 44349 if (mapOrGeoModel.get('selectedMode')) { 44350 44351 group.on('mousedown', function () { 44352 mapDraw._mouseDownFlag = true; 44353 }); 44354 44355 group.on('click', function (e) { 44356 if (!mapDraw._mouseDownFlag) { 44357 return; 44358 } 44359 mapDraw._mouseDownFlag = false; 44360 44361 var el = e.target; 44362 while (!el.__regions) { 44363 el = el.parent; 44364 } 44365 if (!el) { 44366 return; 44367 } 44368 44369 var action = { 44370 type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect', 44371 batch: map(el.__regions, function (region) { 44372 return { 44373 name: region.name, 44374 from: fromView.uid 44375 }; 44376 }) 44377 }; 44378 action[mapOrGeoModel.mainType + 'Id'] = mapOrGeoModel.id; 44379 44380 api.dispatchAction(action); 44381 44382 updateMapSelected(mapOrGeoModel, group); 44383 }); 44384 } 44385 } 44386 44387 function updateMapSelected(mapOrGeoModel, group) { 44388 // FIXME 44389 group.eachChild(function (otherRegionEl) { 44390 each$1(otherRegionEl.__regions, function (region) { 44391 otherRegionEl.trigger(mapOrGeoModel.isSelected(region.name) ? 'emphasis' : 'normal'); 44392 }); 44393 }); 44394 } 44395 44396 /** 44397 * @alias module:echarts/component/helper/MapDraw 44398 * @param {module:echarts/ExtensionAPI} api 44399 * @param {boolean} updateGroup 44400 */ 44401 function MapDraw(api, updateGroup) { 44402 44403 var group = new Group(); 44404 44405 /** 44406 * @type {module:echarts/component/helper/RoamController} 44407 * @private 44408 */ 44409 this._controller = new RoamController(api.getZr()); 44410 44411 /** 44412 * @type {Object} {target, zoom, zoomLimit} 44413 * @private 44414 */ 44415 this._controllerHost = {target: updateGroup ? group : null}; 44416 44417 /** 44418 * @type {module:zrender/container/Group} 44419 * @readOnly 44420 */ 44421 this.group = group; 44422 44423 /** 44424 * @type {boolean} 44425 * @private 44426 */ 44427 this._updateGroup = updateGroup; 44428 44429 /** 44430 * This flag is used to make sure that only one among 44431 * `pan`, `zoom`, `click` can occurs, otherwise 'selected' 44432 * action may be triggered when `pan`, which is unexpected. 44433 * @type {booelan} 44434 */ 44435 this._mouseDownFlag; 44436 } 44437 44438 MapDraw.prototype = { 44439 44440 constructor: MapDraw, 44441 44442 draw: function (mapOrGeoModel, ecModel, api, fromView, payload) { 44443 44444 var isGeo = mapOrGeoModel.mainType === 'geo'; 44445 44446 // Map series has data. GEO model that controlled by map series 44447 // will be assigned with map data. Other GEO model has no data. 44448 var data = mapOrGeoModel.getData && mapOrGeoModel.getData();
44449 isGeo && ecModel.eachComponent({mainType: 'series', subType: 'map'}, function (mapSeries) { 44450 if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) { 44451 data = mapSeries.getData(); 44452 } 44453 }); 44454 44455 var geo = mapOrGeoModel.coordinateSystem; 44456 44457 var group = this.group; 44458 44459 var scale = geo.scale; 44460 var groupNewProp = { 44461 position: geo.position, 44462 scale: scale 44463 }; 44464 44465 // No animation when first draw or in action 44466 if (!group.childAt(0) || payload) { 44467 group.attr(groupNewProp); 44468 } 44469 else { 44470 updateProps(group, groupNewProp, mapOrGeoModel); 44471 } 44472 44473 group.removeAll(); 44474 44475 var itemStyleAccessPath = ['itemStyle']; 44476 var hoverItemStyleAccessPath = ['emphasis', 'itemStyle']; 44477 var labelAccessPath = ['label']; 44478 var hoverLabelAccessPath = ['emphasis', 'label']; 44479 var nameMap = createHashMap(); 44480 44481 each$1(geo.regions, function (region) { 44482 44483 // Consider in GeoJson properties.name may be duplicated, for example, 44484 // there is multiple region named "United Kindom" or "France" (so many 44485 // colonies). And it is not appropriate to merge them in geo, which 44486 // will make them share the same label and bring trouble in label 44487 // location calculation. 44488 var regionGroup = nameMap.get(region.name) 44489 || nameMap.set(region.name, new Group()); 44490 44491 var compoundPath = new CompoundPath({ 44492 shape: { 44493 paths: [] 44494 } 44495 }); 44496 regionGroup.add(compoundPath); 44497 44498 var regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel; 44499 44500 var itemStyleModel = regionModel.getModel(itemStyleAccessPath); 44501 var hoverItemStyleModel = regionModel.getModel(hoverItemStyleAccessPath); 44502 var itemStyle = getFixedItemStyle(itemStyleModel, scale); 44503 var hoverItemStyle = getFixedItemStyle(hoverItemStyleModel, scale); 44504 44505 var labelModel = regionModel.getModel(labelAccessPath); 44506 var hoverLabelModel = regionModel.getModel(hoverLabelAccessPath); 44507 44508 var dataIdx; 44509 // Use the itemStyle in data if has data 44510 if (data) { 44511 dataIdx = data.indexOfName(region.name); 44512 // Only visual color of each item will be used. It can be encoded by dataRange 44513 // But visual color of series is used in symbol drawing 44514 // 44515 // Visual color for each series is for the symbol draw 44516 var visualColor = data.getItemVisual(dataIdx, 'color', true); 44517 if (visualColor) { 44518 itemStyle.fill = visualColor; 44519 } 44520 } 44521 44522 each$1(region.geometries, function (geometry) { 44523 if (geometry.type !== 'polygon') { 44524 return; 44525 } 44526 compoundPath.shape.paths.push(new Polygon({ 44527 shape: { 44528 points: geometry.exterior 44529 } 44530 })); 44531 44532 for (var i = 0; i < (geometry.interiors ? geometry.interiors.length : 0); i++) { 44533 compoundPath.shape.paths.push(new Polygon({ 44534 shape: { 44535 points: geometry.interiors[i] 44536 } 44537 })); 44538 } 44539 }); 44540 44541 compoundPath.setStyle(itemStyle); 44542 compoundPath.style.strokeNoScale = true; 44543 compoundPath.culling = true; 44544 // Label 44545 var showLabel = labelModel.get('show'); 44546 var hoverShowLabel = hoverLabelModel.get('show'); 44547 44548 var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx)); 44549 var itemLayout = data && data.getItemLayout(dataIdx); 44550 // In the following cases label will be drawn 44551 // 1. In map series and data value is NaN 44552 // 2. In geo component 44553 // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbol
44553Layout 44554 if ( 44555 (isGeo || isDataNaN && (showLabel || hoverShowLabel)) 44556 || (itemLayout && itemLayout.showLabel) 44557 ) { 44558 var query = !isGeo ? dataIdx : region.name; 44559 var labelFetcher; 44560 44561 // Consider dataIdx not found. 44562 if (!data || dataIdx >= 0) { 44563 labelFetcher = mapOrGeoModel; 44564 } 44565 44566 var textEl = new Text({ 44567 position: region.center.slice(), 44568 scale: [1 / scale[0], 1 / scale[1]], 44569 z2: 10, 44570 silent: true 44571 }); 44572 44573 setLabelStyle( 44574 textEl.style, textEl.hoverStyle = {}, labelModel, hoverLabelModel, 44575 { 44576 labelFetcher: labelFetcher, 44577 labelDataIndex: query, 44578 defaultText: region.name, 44579 useInsideStyle: false 44580 }, 44581 { 44582 textAlign: 'center', 44583 textVerticalAlign: 'middle' 44584 } 44585 ); 44586 44587 regionGroup.add(textEl); 44588 } 44589 44590 // setItemGraphicEl, setHoverStyle after all polygons and labels 44591 // are added to the rigionGroup 44592 if (data) { 44593 data.setItemGraphicEl(dataIdx, regionGroup); 44594 } 44595 else { 44596 var regionModel = mapOrGeoModel.getRegionModel(region.name); 44597 // Package custom mouse event for geo component 44598 compoundPath.eventData = { 44599 componentType: 'geo', 44600 geoIndex: mapOrGeoModel.componentIndex, 44601 name: region.name, 44602 region: (regionModel && regionModel.option) || {} 44603 }; 44604 } 44605 44606 var groupRegions = regionGroup.__regions || (regionGroup.__regions = []); 44607 groupRegions.push(region); 44608 44609 setHoverStyle( 44610 regionGroup, 44611 hoverItemStyle, 44612 {hoverSilentOnTouch: !!mapOrGeoModel.get('selectedMode')} 44613 ); 44614 44615 group.add(regionGroup); 44616 }); 44617 44618 this._updateController(mapOrGeoModel, ecModel, api); 44619 44620 updateMapSelectHandler(this, mapOrGeoModel, group, api, fromView); 44621 44622 updateMapSelected(mapOrGeoModel, group); 44623 }, 44624 44625 remove: function () { 44626 this.group.removeAll(); 44627 this._controller.dispose(); 44628 this._controllerHost = {}; 44629 }, 44630 44631 _updateController: function (mapOrGeoModel, ecModel, api) { 44632 var geo = mapOrGeoModel.coordinateSystem; 44633 var controller = this._controller; 44634 var controllerHost = this._controllerHost; 44635 44636 controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit'); 44637 controllerHost.zoom = geo.getZoom(); 44638 44639 // roamType is will be set default true if it is null 44640 controller.enable(mapOrGeoModel.get('roam') || false); 44641 var mainType = mapOrGeoModel.mainType; 44642 44643 function makeActionBase() { 44644 var action = { 44645 type: 'geoRoam', 44646 componentType: mainType 44647 }; 44648 action[mainType + 'Id'] = mapOrGeoModel.id; 44649 return action; 44650 } 44651 44652 controller.off('pan').on('pan', function (dx, dy) { 44653 this._mouseDownFlag = false; 44654 44655 updateViewOnPan(controllerHost, dx, dy); 44656 44657 api.dispatchAction(extend(makeActionBase(), { 44658 dx: dx, 44659 dy: dy 44660 })); 44661 }, this); 44662 44663 controller.off('zoom').on('zoom', function (zoom, mouseX, mouseY) { 44664 this._mouseDownFlag = false; 44665 44666 updateViewOnZoom(controllerHost, zoom, mouseX, mouseY); 44667 44668 api.dispatchAction(extend(makeActionBase(), { 44669 zoom: zoom,
44670 originX: mouseX, 44671 originY: mouseY 44672 })); 44673 44674 if (this._updateGroup) { 44675 var group = this.group; 44676 var scale = group.scale; 44677 group.traverse(function (el) { 44678 if (el.type === 'text') { 44679 el.attr('scale', [1 / scale[0], 1 / scale[1]]); 44680 } 44681 }); 44682 } 44683 }, this); 44684 44685 controller.setPointerChecker(function (e, x, y) { 44686 return geo.getViewRectAfterRoam().contain(x, y) 44687 && !onIrrelevantElement(e, api, mapOrGeoModel); 44688 }); 44689 } 44690 }; 44691 44692 /* 44693* Licensed to the Apache Software Foundation (ASF) under one 44694* or more contributor license agreements. See the NOTICE file 44695* distributed with this work for additional information 44696* regarding copyright ownership. The ASF licenses this file 44697* to you under the Apache License, Version 2.0 (the 44698* "License"); you may not use this file except in compliance 44699* with the License. You may obtain a copy of the License at 44700* 44701* http://www.apache.org/licenses/LICENSE-2.0 44702* 44703* Unless required by applicable law or agreed to in writing, 44704* software distributed under the License is distributed on an 44705* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44706* KIND, either express or implied. See the License for the 44707* specific language governing permissions and limitations 44708* under the License. 44709*/ 44710 44711 extendChartView({ 44712 44713 type: 'map', 44714 44715 render: function (mapModel, ecModel, api, payload) { 44716 // Not render if it is an toggleSelect action from self 44717 if (payload && payload.type === 'mapToggleSelect' 44718 && payload.from === this.uid 44719 ) { 44720 return; 44721 } 44722 44723 var group = this.group; 44724 group.removeAll(); 44725 44726 if (mapModel.getHostGeoModel()) { 44727 return; 44728 } 44729 44730 // Not update map if it is an roam action from self 44731 if (!(payload && payload.type === 'geoRoam' 44732 && payload.componentType === 'series' 44733 && payload.seriesId === mapModel.id 44734 ) 44735 ) { 44736 if (mapModel.needsDrawMap) { 44737 var mapDraw = this._mapDraw || new MapDraw(api, true); 44738 group.add(mapDraw.group); 44739 44740 mapDraw.draw(mapModel, ecModel, api, this, payload); 44741 44742 this._mapDraw = mapDraw; 44743 } 44744 else { 44745 // Remove drawed map 44746 this._mapDraw && this._mapDraw.remove(); 44747 this._mapDraw = null; 44748 } 44749 } 44750 else { 44751 var mapDraw = this._mapDraw; 44752 mapDraw && group.add(mapDraw.group); 44753 } 44754 44755 mapModel.get('showLegendSymbol') && ecModel.getComponent('legend') 44756 && this._renderSymbols(mapModel, ecModel, api); 44757 }, 44758 44759 remove: function () { 44760 this._mapDraw && this._mapDraw.remove(); 44761 this._mapDraw = null; 44762 this.group.removeAll(); 44763 }, 44764 44765 dispose: function () { 44766 this._mapDraw && this._mapDraw.remove(); 44767 this._mapDraw = null; 44768 }, 44769 44770 _renderSymbols: function (mapModel, ecModel, api) { 44771 var originalData = mapModel.originalData; 44772 var group = this.group; 44773 44774 originalData.each(originalData.mapDimension('value'), function (value, idx) { 44775 if (isNaN(value)) { 44776 return; 44777 } 44778 44779 var layout = originalData.getItemLayout(idx); 44780 44781 if (!layout || !layout.point) { 44782 // Not exists in map 44783 return; 44784 } 44785 44786 var point = layout.point; 44787 var offset = layout.offset; 44788 44789 var circle = new Circle({ 44790 style: { 44791 // Because the special of map draw. 44792 // Which needs statistic of multiple series and draw on one map. 44793 // And each series also need a symbol with legend color 44794 // 44795 // Layout and visual are put one the different data 44796 fill: mapModel.getData().getVisual('color') 44797 }, 44798 shape: { 44799 cx: point[0] + offset * 9, 44800 cy: point[1], 44801 r: 3 44802 }, 44803 silent: true,
44804 // Do not overlap the first series, on which labels are displayed. 44805 z2: !offset ? 10 : 8 44806 }); 44807 44808 // First data on the same region 44809 if (!offset) { 44810 var fullData = mapModel.mainSeries.getData(); 44811 var name = originalData.getName(idx); 44812 44813 var fullIndex = fullData.indexOfName(name); 44814 44815 var itemModel = originalData.getItemModel(idx); 44816 var labelModel = itemModel.getModel('label'); 44817 var hoverLabelModel = itemModel.getModel('emphasis.label'); 44818 44819 var polygonGroups = fullData.getItemGraphicEl(fullIndex); 44820 44821 var normalText = retrieve2( 44822 mapModel.getFormattedLabel(idx, 'normal'), 44823 name 44824 ); 44825 var emphasisText = retrieve2( 44826 mapModel.getFormattedLabel(idx, 'emphasis'), 44827 normalText 44828 ); 44829 44830 var onEmphasis = function () { 44831 var hoverStyle = setTextStyle({}, hoverLabelModel, { 44832 text: hoverLabelModel.get('show') ? emphasisText : null 44833 }, {isRectText: true, useInsideStyle: false}, true); 44834 circle.style.extendFrom(hoverStyle); 44835 // Make label upper than others if overlaps. 44836 circle.__mapOriginalZ2 = circle.z2; 44837 circle.z2 += 1; 44838 }; 44839 44840 var onNormal = function () { 44841 setTextStyle(circle.style, labelModel, { 44842 text: labelModel.get('show') ? normalText : null, 44843 textPosition: labelModel.getShallow('position') || 'bottom' 44844 }, {isRectText: true, useInsideStyle: false}); 44845 44846 if (circle.__mapOriginalZ2 != null) { 44847 circle.z2 = circle.__mapOriginalZ2; 44848 circle.__mapOriginalZ2 = null; 44849 } 44850 }; 44851 44852 polygonGroups.on('mouseover', onEmphasis) 44853 .on('mouseout', onNormal) 44854 .on('emphasis', onEmphasis) 44855 .on('normal', onNormal); 44856 44857 onNormal(); 44858 } 44859 44860 group.add(circle); 44861 }); 44862 } 44863 }); 44864 44865 /* 44866* Licensed to the Apache Software Foundation (ASF) under one 44867* or more contributor license agreements. See the NOTICE file 44868* distributed with this work for additional information 44869* regarding copyright ownership. The ASF licenses this file 44870* to you under the Apache License, Version 2.0 (the 44871* "License"); you may not use this file except in compliance 44872* with the License. You may obtain a copy of the License at 44873* 44874* http://www.apache.org/licenses/LICENSE-2.0 44875* 44876* Unless required by applicable law or agreed to in writing, 44877* software distributed under the License is distributed on an 44878* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44879* KIND, either express or implied. See the License for the 44880* specific language governing permissions and limitations 44881* under the License. 44882*/ 44883 44884 /** 44885 * @param {module:echarts/coord/View} view 44886 * @param {Object} payload 44887 * @param {Object} [zoomLimit] 44888 */ 44889 function updateCenterAndZoom( 44890 view, payload, zoomLimit 44891 ) { 44892 var previousZoom = view.getZoom(); 44893 var center = view.getCenter(); 44894 var zoom = payload.zoom; 44895 44896 var point = view.dataToPoint(center); 44897 44898 if (payload.dx != null && payload.dy != null) { 44899 point[0] -= payload.dx; 44900 point[1] -= payload.dy; 44901 44902 var center = view.pointToData(point); 44903 view.setCenter(center); 44904 } 44905 if (zoom != null) { 44906 if (zoomLimit) { 44907 var zoomMin = zoomLimit.min || 0; 44908 var zoomMax = zoomLimit.max || Infinity; 44909 zoom = Math.max( 44910 Math.min(previousZoom * zoom, zoomMax), 44911 zoomMin 44912 ) / previousZoom; 44913 } 44914 44915 // Zoom on given point(originX, originY) 44916 view.scale[0] *= zoom; 44917 view.scale[1] *= zoom; 44918 var position = view.position; 44919 var fixX = (payload.originX - position[0]) * (zoom - 1); 44920 var fixY = (payload.originY - position[1]) * (zoom - 1); 44921 44922 position[0] -= fixX; 44923 position[1] -= fixY; 44924 44925 view.updateTransform(); 44926 // Get the new center 44927 var center = view.pointToData(point); 44928 view.setCenter(center); 44929 view.setZoom(zoom * previousZoom); 44930 } 44931 44932 return { 44933 center: view.getCenter(), 44934 zoom: view.getZoom() 44935 }; 44936 } 44937 44938 /* 44939* Licensed to the Apache Software Foundation (ASF) under one 44940* or more contributor license agreements. See the NOTICE file 44941* distributed with this work for additional information 44942* regarding copyright ownership. The ASF licenses this file 44943* to you under the Apache License, Version 2.0 (the 44944* "License");
44944 you may not use this file except in compliance 44945* with the License. You may obtain a copy of the License at 44946* 44947* http://www.apache.org/licenses/LICENSE-2.0 44948* 44949* Unless required by applicable law or agreed to in writing, 44950* software distributed under the License is distributed on an 44951* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 44952* KIND, either express or implied. See the License for the 44953* specific language governing permissions and limitations 44954* under the License. 44955*/ 44956 44957 /** 44958 * @payload 44959 * @property {string} [componentType=series] 44960 * @property {number} [dx] 44961 * @property {number} [dy] 44962 * @property {number} [zoom] 44963 * @property {number} [originX] 44964 * @property {number} [originY] 44965 */ 44966 registerAction({ 44967 type: 'geoRoam', 44968 event: 'geoRoam', 44969 update: 'updateTransform' 44970 }, function (payload, ecModel) { 44971 var componentType = payload.componentType || 'series'; 44972 44973 ecModel.eachComponent( 44974 { mainType: componentType, query: payload }, 44975 function (componentModel) { 44976 var geo = componentModel.coordinateSystem; 44977 if (geo.type !== 'geo') { 44978 return; 44979 } 44980 44981 var res = updateCenterAndZoom( 44982 geo, payload, componentModel.get('scaleLimit') 44983 ); 44984 44985 componentModel.setCenter 44986 && componentModel.setCenter(res.center); 44987 44988 componentModel.setZoom 44989 && componentModel.setZoom(res.zoom); 44990 44991 // All map series with same `map` use the same geo coordinate system 44992 // So the center and zoom must be in sync. Include the series not selected by legend 44993 if (componentType === 'series') { 44994 each$1(componentModel.seriesGroup, function (seriesModel) { 44995 seriesModel.setCenter(res.center); 44996 seriesModel.setZoom(res.zoom); 44997 }); 44998 } 44999 } 45000 ); 45001 }); 45002 45003 /* 45004* Licensed to the Apache Software Foundation (ASF) under one 45005* or more contributor license agreements. See the NOTICE file 45006* distributed with this work for additional information 45007* regarding copyright ownership. The ASF licenses this file 45008* to you under the Apache License, Version 2.0 (the 45009* "License"); you may not use this file except in compliance 45010* with the License. You may obtain a copy of the License at 45011* 45012* http://www.apache.org/licenses/LICENSE-2.0 45013* 45014* Unless required by applicable law or agreed to in writing, 45015* software distributed under the License is distributed on an 45016* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45017* KIND, either express or implied. See the License for the 45018* specific language governing permissions and limitations 45019* under the License. 45020*/ 45021 45022 var mapSymbolLayout = function (ecModel) { 45023 45024 var processedMapType = {}; 45025 45026 ecModel.eachSeriesByType('map', function (mapSeries) { 45027 var mapType = mapSeries.getMapType(); 45028 if (mapSeries.getHostGeoModel() || processedMapType[mapType]) { 45029 return; 45030 } 45031 45032 var mapSymbolOffsets = {}; 45033 45034 each$1(mapSeries.seriesGroup, function (subMapSeries) { 45035 var geo = subMapSeries.coordinateSystem; 45036 var data = subMapSeries.originalData; 45037 if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) { 45038 data.each(data.mapDimension('value'), function (value, idx) { 45039 var name = data.getName(idx); 45040 var region = geo.getRegion(name); 45041 45042 // If input series.data is [11, 22, '-'/null/undefined, 44], 45043 // it will be filled with NaN: [11, 22, NaN, 44] and NaN will 45044 // not be drawn. So here must validate if value is NaN. 45045 if (!region || isNaN(value)) { 45046 return; 45047 } 45048 45049 var offset = mapSymbolOffsets[name] || 0; 45050 45051 var point = geo.dataToPoint(region.center); 45052 45053 mapSymbolOffsets[name] = offset + 1; 45054 45055 data.setItemLayout(idx, { 45056 point: point, 45057 offset: offset 45058 }); 45059 }); 45060 } 45061 }); 45062 45063 // Show label of those region not has legendSymbol(which is offset 0) 45064 var data = mapSeries.getData(); 45065 data.each(function (idx) { 45066 var name = data.getName(idx); 45067 var layout = data.getItemLayout(idx) || {}; 45068 layout.showLabel = !mapSymbolOffsets[name]; 45069 data.setItemLayout(idx, layout); 45070 }); 45071
45072 processedMapType[mapType] = true; 45073 }); 45074 }; 45075 45076 /* 45077* Licensed to the Apache Software Foundation (ASF) under one 45078* or more contributor license agreements. See the NOTICE file 45079* distributed with this work for additional information 45080* regarding copyright ownership. The ASF licenses this file 45081* to you under the Apache License, Version 2.0 (the 45082* "License"); you may not use this file except in compliance 45083* with the License. You may obtain a copy of the License at 45084* 45085* http://www.apache.org/licenses/LICENSE-2.0 45086* 45087* Unless required by applicable law or agreed to in writing, 45088* software distributed under the License is distributed on an 45089* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45090* KIND, either express or implied. See the License for the 45091* specific language governing permissions and limitations 45092* under the License. 45093*/ 45094 45095 45096 var mapVisual = function (ecModel) { 45097 ecModel.eachSeriesByType('map', function (seriesModel) { 45098 var colorList = seriesModel.get('color'); 45099 var itemStyleModel = seriesModel.getModel('itemStyle'); 45100 45101 var areaColor = itemStyleModel.get('areaColor'); 45102 var color = itemStyleModel.get('color') 45103 || colorList[seriesModel.seriesIndex % colorList.length]; 45104 45105 seriesModel.getData().setVisual({ 45106 'areaColor': areaColor, 45107 'color': color 45108 }); 45109 }); 45110 }; 45111 45112 /* 45113* Licensed to the Apache Software Foundation (ASF) under one 45114* or more contributor license agreements. See the NOTICE file 45115* distributed with this work for additional information 45116* regarding copyright ownership. The ASF licenses this file 45117* to you under the Apache License, Version 2.0 (the 45118* "License"); you may not use this file except in compliance 45119* with the License. You may obtain a copy of the License at 45120* 45121* http://www.apache.org/licenses/LICENSE-2.0 45122* 45123* Unless required by applicable law or agreed to in writing, 45124* software distributed under the License is distributed on an 45125* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45126* KIND, either express or implied. See the License for the 45127* specific language governing permissions and limitations 45128* under the License. 45129*/ 45130 45131// FIXME Ã¥â¦Â¬Ã§â¨ï¼Ÿ 45132 /** 45133 * @param {Array.<module:echarts/data/List>} datas 45134 * @param {string} statisticType 'average' 'sum' 45135 * @inner 45136 */ 45137 function dataStatistics(datas, statisticType) { 45138 var dataNameMap = {}; 45139 45140 each$1(datas, function (data) { 45141 data.each(data.mapDimension('value'), function (value, idx) { 45142 // Add prefix to avoid conflict with Object.prototype. 45143 var mapKey = 'ec-' + data.getName(idx); 45144 dataNameMap[mapKey] = dataNameMap[mapKey] || []; 45145 if (!isNaN(value)) { 45146 dataNameMap[mapKey].push(value); 45147 } 45148 }); 45149 }); 45150 45151 return datas[0].map(datas[0].mapDimension('value'), function (value, idx) { 45152 var mapKey = 'ec-' + datas[0].getName(idx); 45153 var sum = 0; 45154 var min = Infinity; 45155 var max = -Infinity; 45156 var len = dataNameMap[mapKey].length; 45157 for (var i = 0; i < len; i++) { 45158 min = Math.min(min, dataNameMap[mapKey][i]); 45159 max = Math.max(max, dataNameMap[mapKey][i]); 45160 sum += dataNameMap[mapKey][i]; 45161 } 45162 var result; 45163 if (statisticType === 'min') { 45164 result = min; 45165 } 45166 else if (statisticType === 'max') { 45167 result = max; 45168 } 45169 else if (statisticType === 'average') { 45170 result = sum / len; 45171 } 45172 else { 45173 result = sum; 45174 } 45175 return len === 0 ? NaN : result; 45176 }); 45177 } 45178 45179 var mapDataStatistic = function (ecModel) { 45180 var seriesGroups = {}; 45181 ecModel.eachSeriesByType('map', function (seriesModel) { 45182 var hostGeoModel = seriesModel.getHostGeoModel(); 45183 var key = hostGeoModel ? 'o' + hostGeoModel.id : 'i' + seriesModel.getMapType(); 45184 (seriesGroups[key] = seriesGroups[key] || []).push(seriesM
45184odel); 45185 }); 45186 45187 each$1(seriesGroups, function (seriesList, key) { 45188 var data = dataStatistics( 45189 map(seriesList, function (seriesModel) { 45190 return seriesModel.getData(); 45191 }), 45192 seriesList[0].get('mapValueCalculation') 45193 ); 45194 45195 for (var i = 0; i < seriesList.length; i++) { 45196 seriesList[i].originalData = seriesList[i].getData(); 45197 } 45198 45199 // FIXME Put where? 45200 for (var i = 0; i < seriesList.length; i++) { 45201 seriesList[i].seriesGroup = seriesList; 45202 seriesList[i].needsDrawMap = i === 0 && !seriesList[i].getHostGeoModel(); 45203 45204 seriesList[i].setData(data.cloneShallow()); 45205 seriesList[i].mainSeries = seriesList[0]; 45206 } 45207 }); 45208 }; 45209 45210 /* 45211* Licensed to the Apache Software Foundation (ASF) under one 45212* or more contributor license agreements. See the NOTICE file 45213* distributed with this work for additional information 45214* regarding copyright ownership. The ASF licenses this file 45215* to you under the Apache License, Version 2.0 (the 45216* "License"); you may not use this file except in compliance 45217* with the License. You may obtain a copy of the License at 45218* 45219* http://www.apache.org/licenses/LICENSE-2.0 45220* 45221* Unless required by applicable law or agreed to in writing, 45222* software distributed under the License is distributed on an 45223* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45224* KIND, either express or implied. See the License for the 45225* specific language governing permissions and limitations 45226* under the License. 45227*/ 45228 45229 var backwardCompat$2 = function (option) { 45230 // Save geoCoord 45231 var mapSeries = []; 45232 each$1(option.series, function (seriesOpt) { 45233 if (seriesOpt && seriesOpt.type === 'map') { 45234 mapSeries.push(seriesOpt); 45235 seriesOpt.map = seriesOpt.map || seriesOpt.mapType; 45236 // Put x, y, width, height, x2, y2 in the top level 45237 defaults(seriesOpt, seriesOpt.mapLocation); 45238 } 45239 }); 45240 }; 45241 45242 /* 45243* Licensed to the Apache Software Foundation (ASF) under one 45244* or more contributor license agreements. See the NOTICE file 45245* distributed with this work for additional information 45246* regarding copyright ownership. The ASF licenses this file 45247* to you under the Apache License, Version 2.0 (the 45248* "License"); you may not use this file except in compliance 45249* with the License. You may obtain a copy of the License at 45250* 45251* http://www.apache.org/licenses/LICENSE-2.0 45252* 45253* Unless required by applicable law or agreed to in writing, 45254* software distributed under the License is distributed on an 45255* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45256* KIND, either express or implied. See the License for the 45257* specific language governing permissions and limitations 45258* under the License. 45259*/ 45260
45261 registerLayout(mapSymbolLayout); 45262 registerVisual(mapVisual); 45263 registerProcessor(PRIORITY.PROCESSOR.STATISTIC, mapDataStatistic); 45264 registerPreprocessor(backwardCompat$2); 45265 45266 createDataSelectAction('map', [{ 45267 type: 'mapToggleSelect', 45268 event: 'mapselectchanged', 45269 method: 'toggleSelected' 45270 }, { 45271 type: 'mapSelect', 45272 event: 'mapselected', 45273 method: 'select' 45274 }, { 45275 type: 'mapUnSelect', 45276 event: 'mapunselected', 45277 method: 'unSelect' 45278 }]); 45279 45280 /* 45281* Licensed to the Apache Software Foundation (ASF) under one 45282* or more contributor license agreements. See the NOTICE file 45283* distributed with this work for additional information 45284* regarding copyright ownership. The ASF licenses this file 45285* to you under the Apache License, Version 2.0 (the 45286* "License"); you may not use this file except in compliance 45287* with the License. You may obtain a copy of the License at 45288* 45289* http://www.apache.org/licenses/LICENSE-2.0 45290* 45291* Unless required by applicable law or agreed to in writing, 45292* software distributed under the License is distributed on an 45293* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45294* KIND, either express or implied. See the License for the 45295* specific language governing permissions and limitations 45296* under the License. 45297*/ 45298 45299 /** 45300 * Link lists and struct (graph or tree) 45301 */ 45302 45303 var each$7 = each$1; 45304 45305 var DATAS = '\0__link_datas'; 45306 var MAIN_DATA = '\0__link_mainData'; 45307 45308// Caution: 45309// In most case, either list or its shallow clones (see list.cloneShallow) 45310// is active in echarts process. So considering heap memory consumption, 45311// we do not clone tree or graph, but share them among list and its shallow clones. 45312// But in some rare case, we have to keep old list (like do animation in chart). So 45313// please take care that both the old list and the new list share the same tree/graph. 45314 45315 /** 45316 * @param {Object} opt 45317 * @param {module:echarts/data/List} opt.mainData 45318 * @param {Object} [opt.struct] For example, instance of Graph or Tree. 45319 * @param {string} [opt.structAttr] designation: list[structAttr] = struct; 45320 * @param {Object} [opt.datas] {dataType: data}, 45321 * like: {node: nodeList, edge: edgeList}. 45322 * Should contain mainData. 45323 * @param {Object} [opt.datasAttr] {dataType: attr}, 45324 * designation: struct[datasAttr[dataType]] = list; 45325 */ 45326 function linkList(opt) { 45327 var mainData = opt.mainData; 45328 var datas = opt.datas; 45329 45330 if (!datas) { 45331 datas = {main: mainData}; 45332 opt.datasAttr = {main: 'data'}; 45333 } 45334 opt.datas = opt.mainData = null; 45335 45336 linkAll(mainData, datas, opt); 45337 45338 // Porxy data original methods. 45339 each$7(datas, function (data) { 45340 each$7(mainData.TRANSFERABLE_METHODS, function (methodName) { 45341 data.wrapMethod(methodName, curry(transferInjection, opt)); 45342 }); 45343 45344 }); 45345 45346 // Beyond transfer, additional features should be added to `cloneShallow`. 45347 mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt)); 45348 45349 // Only mainData trigger change, because struct.update may trigger 45350 // another changable methods, which may bring about dead lock. 45351 each$7(mainData.CHANGABLE_METHODS, function (methodName) { 45352 mainData.wrapMethod(methodName, curry(changeInjection, opt)); 45353 }); 45354 45355 // Make sure datas contains mainData. 45356 assert$1(datas[mainData.dataType] === mainData); 45357 } 45358 45359 function transferInjection(opt, res) { 45360 if (isMainData(this)) { 45361 // Transfer datas to new main data. 45362 var datas = extend({}, this[DATAS]); 45363 datas[this.dataType] = res; 45364 linkAll(res, datas, opt); 45365 } 45366 else { 45367 // Modify the reference in main data to point newData. 45368 linkSingle(res, this.dataType, this[MAIN_DATA], opt); 45369 } 45370 return res; 45371 } 45372 45373 function changeInjection(opt, res) { 45374 opt.struct && opt.struct.update(this); 45375 return res; 45376 } 45377 45378 function cloneShallowInjection(opt, res) { 45379 // cloneShallow, which brings about some fragilities, may be inappropriate 45380 // to be exposed as an API. So for implementation simplicity we can make 45381 // the restriction that cloneShallow of not-mainData should not be invoked 45382 // outside, but only be invoked here. 45383 each$7(res[DATAS], function (data, dataType) { 45384 data !== res && linkSingle(data.cloneShallow(), dataType, res, opt); 45385 }); 45386 return res; 45387 } 45388 45389 /** 45390 * Supplement method to List. 45391 * 45392 * @public 45393 * @param {string} [dataType] If not specified, return mainData.
45394 * @return {module:echarts/data/List} 45395 */ 45396 function getLinkedData(dataType) { 45397 var mainData = this[MAIN_DATA]; 45398 return (dataType == null || mainData == null) 45399 ? mainData 45400 : mainData[DATAS][dataType]; 45401 } 45402 45403 function isMainData(data) { 45404 return data[MAIN_DATA] === data; 45405 } 45406 45407 function linkAll(mainData, datas, opt) { 45408 mainData[DATAS] = {}; 45409 each$7(datas, function (data, dataType) { 45410 linkSingle(data, dataType, mainData, opt); 45411 }); 45412 } 45413 45414 function linkSingle(data, dataType, mainData, opt) { 45415 mainData[DATAS][dataType] = data; 45416 data[MAIN_DATA] = mainData; 45417 data.dataType = dataType; 45418 45419 if (opt.struct) { 45420 data[opt.structAttr] = opt.struct; 45421 opt.struct[opt.datasAttr[dataType]] = data; 45422 } 45423 45424 // Supplement method. 45425 data.getLinkedData = getLinkedData; 45426 } 45427 45428 /* 45429* Licensed to the Apache Software Foundation (ASF) under one 45430* or more contributor license agreements. See the NOTICE file 45431* distributed with this work for additional information 45432* regarding copyright ownership. The ASF licenses this file 45433* to you under the Apache License, Version 2.0 (the 45434* "License"); you may not use this file except in compliance 45435* with the License. You may obtain a copy of the License at 45436* 45437* http://www.apache.org/licenses/LICENSE-2.0 45438* 45439* Unless required by applicable law or agreed to in writing, 45440* software distributed under the License is distributed on an 45441* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45442* KIND, either express or implied. See the License for the 45443* specific language governing permissions and limitations 45444* under the License. 45445*/ 45446 45447 /** 45448 * Tree data structure 45449 * 45450 * @module echarts/data/Tree 45451 */ 45452 45453 /** 45454 * @constructor module:echarts/data/Tree~TreeNode 45455 * @param {string} name 45456 * @param {module:echarts/data/Tree} hostTree 45457 */ 45458 var TreeNode = function (name, hostTree) { 45459 /** 45460 * @type {string} 45461 */ 45462 this.name = name || ''; 45463 45464 /** 45465 * Depth of node 45466 * 45467 * @type {number} 45468 * @readOnly 45469 */ 45470 this.depth = 0; 45471 45472 /** 45473 * Height of the subtree rooted at this node. 45474 * @type {number} 45475 * @readOnly 45476 */ 45477 this.height = 0; 45478 45479 /** 45480 * @type {module:echarts/data/Tree~TreeNode} 45481 * @readOnly 45482 */ 45483 this.parentNode = null; 45484 45485 /** 45486 * Reference to list item. 45487 * Do not persistent dataIndex outside, 45488 * besause it may be changed by list. 45489 * If dataIndex -1, 45490 * this node is logical deleted (filtered) in list. 45491 * 45492 * @type {Object} 45493 * @readOnly 45494 */ 45495 this.dataIndex = -1; 45496 45497 /** 45498 * @type {Array.<module:echarts/data/Tree~TreeNode>} 45499 * @readOnly 45500 */ 45501 this.children = []; 45502 45503 /** 45504 * @type {Array.<module:echarts/data/Tree~TreeNode>} 45505 * @pubilc 45506 */ 45507 this.viewChildren = []; 45508 45509 /** 45510 * @type {moduel:echarts/data/Tree} 45511 * @readOnly 45512 */ 45513 this.hostTree = hostTree; 45514 }; 45515 45516 TreeNode.prototype = { 45517 45518 constructor: TreeNode, 45519 45520 /** 45521 * The node is removed. 45522 * @return {boolean} is removed. 45523 */ 45524 isRemoved: function () { 45525 return this.dataIndex < 0; 45526 }, 45527 45528 /** 45529 * Travel this subtree (include this node). 45530 * Usage: 45531 * node.eachNode(function () { ... }); // preorder 45532 * node.eachNode('preorder', function () { ... }); // preorder 45533 * node.eachNode('postorder', function () { ... }); // postorder 45534 * node.eachNode( 45535 * {order: 'postorder', attr: 'viewChildren'}, 45536 * function () { ... } 45537 * ); // postorder 45538 * 45539 * @param {(Object|string)} options If string, means order. 45540 * @param {string=} options.order 'preorder' or 'postorder' 45541 * @param {string=} options.attr 'children' or 'viewChildren' 45542 * @param {Function} cb If in preorder and return false, 45543 * its subtree will not be visited. 45544 * @param {Object} [context] 45545 */ 45546 eachNode: function (options, cb, context) { 45547 if (typeof options === 'function') { 45548 context = cb; 45549 cb = options; 45550 options = null; 45551 } 45552 45553 options = options || {}; 45554 if (isString(options)) { 45555 options = {order: options}; 45556 } 45557 45558 var order = options.order || 'preorder'; 45559 var children = this[options.attr || 'children']; 45560 45561 var suppressVisitSub; 45562 order === 'preorder' && (suppressVisitSub = cb.call(context, this)); 45563 45564 for (var i = 0; !suppressVisitSub && i < children.length; i++) { 45565 children[i].eachNode(options, cb, context); 45566 } 45567 45568 order === 'postorder' && cb.call(context, this); 45569 }, 45570 45571 /** 45572 * Update depth and height of this subtree. 45573 * 45574 * @param {number} depth 45575 */ 45576 updateDepthAndHeight: function (depth) { 45577 var height = 0; 45578 this.depth = depth; 45579 for (var i = 0; i < this.children.length; i++) { 45580 var child = this.children[i]; 45581 child.updateDepthAndHeight(depth + 1); 45582 if (child.height > height) { 45583 height = child.height; 45584 } 45585 } 45586 this.height = height + 1; 45587 }, 45588 45589 /** 45590 * @param {string} id
45591 * @return {module:echarts/data/Tree~TreeNode} 45592 */ 45593 getNodeById: function (id) { 45594 if (this.getId() === id) { 45595 return this; 45596 } 45597 for (var i = 0, children = this.children, len = children.length; i < len; i++) { 45598 var res = children[i].getNodeById(id); 45599 if (res) { 45600 return res; 45601 } 45602 } 45603 }, 45604 45605 /** 45606 * @param {module:echarts/data/Tree~TreeNode} node 45607 * @return {boolean} 45608 */ 45609 contains: function (node) { 45610 if (node === this) { 45611 return true; 45612 } 45613 for (var i = 0, children = this.children, len = children.length; i < len; i++) { 45614 var res = children[i].contains(node); 45615 if (res) { 45616 return res; 45617 } 45618 } 45619 }, 45620 45621 /** 45622 * @param {boolean} includeSelf Default false. 45623 * @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root, child, grandchild, ...] 45624 */ 45625 getAncestors: function (includeSelf) { 45626 var ancestors = []; 45627 var node = includeSelf ? this : this.parentNode; 45628 while (node) { 45629 ancestors.push(node); 45630 node = node.parentNode; 45631 } 45632 ancestors.reverse(); 45633 return ancestors; 45634 }, 45635 45636 /** 45637 * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 1, 2, 3 45638 * @return {number} Value. 45639 */ 45640 getValue: function (dimension) { 45641 var data = this.hostTree.data; 45642 return data.get(data.getDimension(dimension || 'value'), this.dataIndex); 45643 }, 45644 45645 /** 45646 * @param {Object} layout 45647 * @param {boolean=} [merge=false] 45648 */ 45649 setLayout: function (layout, merge$$1) { 45650 this.dataIndex >= 0 45651 && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge$$1); 45652 }, 45653 45654 /** 45655 * @return {Object} layout 45656 */ 45657 getLayout: function () { 45658 return this.hostTree.data.getItemLayout(this.dataIndex); 45659 }, 45660 45661 /** 45662 * @param {string} [path] 45663 * @return {module:echarts/model/Model} 45664 */ 45665 getModel: function (path) { 45666 if (this.dataIndex < 0) { 45667 return; 45668 } 45669 var hostTree = this.hostTree; 45670 var itemModel = hostTree.data.getItemModel(this.dataIndex); 45671 var levelModel = this.getLevelModel(); 45672 var leavesModel; 45673 if (!levelModel && (this.children.length === 0 || (this.children.length !== 0 && this.isExpand === false))) { 45674 leavesModel = this.getLeavesModel(); 45675 } 45676 return itemModel.getModel(path, (levelModel || leavesModel || hostTree.hostModel).getModel(path)); 45677 }, 45678 45679 /** 45680 * @return {module:echarts/model/Model} 45681 */ 45682 getLevelModel: function () { 45683 return (this.hostTree.levelModels || [])[this.depth]; 45684 }, 45685 45686 /** 45687 * @return {module:echarts/model/Model} 45688 */ 45689 getLeavesModel: function () { 45690 return this.hostTree.leavesModel; 45691 }, 45692 45693 /** 45694 * @example 45695 * setItemVisual('color', color); 45696 * setItemVisual({ 45697 * 'color': color 45698 * }); 45699 */ 45700 setVisual: function (key, value) { 45701 this.dataIndex >= 0 45702 && this.hostTree.data.setItemVisual(this.dataIndex, key, value); 45703 }, 45704 45705 /** 45706 * Get item visual 45707 */ 45708 getVisual: function (key, ignoreParent) { 45709 return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent); 45710 }, 45711 45712 /** 45713 * @public 45714 * @return {number} 45715 */ 45716 getRawIndex: function () { 45717 return this.hostTree.data.getRawIndex(this.dataIndex); 45718 }, 45719 45720 /** 45721 * @public 45722 * @return {string} 45723 */ 45724 getId: function () { 45725 return this.hostTree.data.getId(this.dataIndex); 45726 }, 45727 45728 /** 45729 * if this is an ancestor of another node 45730 * 45731 * @public 45732 * @param {TreeNode} node another node 45733 * @return {boolean} if is ancestor 45734 */ 45735 isAncestorOf: function (node) { 45736 var parent = node.parentNode; 45737 while (parent) { 45738 if (parent === this) { 45739 return true; 45740 } 45741 parent = parent.parentNode; 45742 } 45743 return false; 45744 }, 45745 45746 /** 45747 * if this is an descendant of another node 45748 * 45749 * @public 45750 * @param {TreeNode} node another node 45751 * @return {boolean} if is descendant 45752 */ 45753 isDescendantOf: function (node) { 45754 return node !== this && node.isAncestorOf(this); 45755 } 45756 }; 45757 45758 /** 45759 * @constructor
45760 * @alias module:echarts/data/Tree 45761 * @param {module:echarts/model/Model} hostModel 45762 * @param {Array.<Object>} levelOptions 45763 * @param {Object} leavesOption 45764 */ 45765 function Tree(hostModel, levelOptions, leavesOption) { 45766 /** 45767 * @type {module:echarts/data/Tree~TreeNode} 45768 * @readOnly 45769 */ 45770 this.root; 45771 45772 /** 45773 * @type {module:echarts/data/List} 45774 * @readOnly 45775 */ 45776 this.data; 45777 45778 /** 45779 * Index of each item is the same as the raw index of coresponding list item. 45780 * @private 45781 * @type {Array.<module:echarts/data/Tree~TreeNode} 45782 */ 45783 this._nodes = []; 45784 45785 /** 45786 * @private 45787 * @readOnly 45788 * @type {module:echarts/model/Model} 45789 */ 45790 this.hostModel = hostModel; 45791 45792 /** 45793 * @private 45794 * @readOnly 45795 * @type {Array.<module:echarts/model/Model} 45796 */ 45797 this.levelModels = map(levelOptions || [], function (levelDefine) { 45798 return new Model(levelDefine, hostModel, hostModel.ecModel); 45799 }); 45800 45801 this.leavesModel = new Model(leavesOption || {}, hostModel, hostModel.ecModel); 45802 } 45803 45804 Tree.prototype = { 45805 45806 constructor: Tree, 45807 45808 type: 'tree', 45809 45810 /** 45811 * Travel this subtree (include this node). 45812 * Usage: 45813 * node.eachNode(function () { ... }); // preorder 45814 * node.eachNode('preorder', function () { ... }); // preorder 45815 * node.eachNode('postorder', function () { ... }); // postorder 45816 * node.eachNode( 45817 * {order: 'postorder', attr: 'viewChildren'}, 45818 * function () { ... } 45819 * ); // postorder 45820 * 45821 * @param {(Object|string)} options If string, means order. 45822 * @param {string=} options.order 'preorder' or 'postorder' 45823 * @param {string=} options.attr 'children' or 'viewChildren' 45824 * @param {Function} cb 45825 * @param {Object} [context] 45826 */ 45827 eachNode: function(options, cb, context) { 45828 this.root.eachNode(options, cb, context); 45829 }, 45830 45831 /** 45832 * @param {number} dataIndex 45833 * @return {module:echarts/data/Tree~TreeNode} 45834 */ 45835 getNodeByDataIndex: function (dataIndex) { 45836 var rawIndex = this.data.getRawIndex(dataIndex); 45837 return this._nodes[rawIndex]; 45838 }, 45839 45840 /** 45841 * @param {string} name 45842 * @return {module:echarts/data/Tree~TreeNode} 45843 */ 45844 getNodeByName: function (name) { 45845 return this.root.getNodeByName(name); 45846 }, 45847 45848 /** 45849 * Update item available by list, 45850 * when list has been performed options like 'filterSelf' or 'map'. 45851 */ 45852 update: function () { 45853 var data = this.data; 45854 var nodes = this._nodes; 45855 45856 for (var i = 0, len = nodes.length; i < len; i++) { 45857 nodes[i].dataIndex = -1; 45858 } 45859 45860 for (var i = 0, len = data.count(); i < len; i++) { 45861 nodes[data.getRawIndex(i)].dataIndex = i; 45862 } 45863 }, 45864 45865 /** 45866 * Clear all layouts 45867 */ 45868 clearLayouts: function () { 45869 this.data.clearItemLayouts(); 45870 } 45871 }; 45872 45873 /** 45874 * data node format: 45875 * { 45876 * name: ... 45877 * value: ... 45878 * children: [ 45879 * { 45880 * name: ... 45881 * value: ... 45882 * children: ... 45883 * }, 45884 * ... 45885 * ] 45886 * } 45887 * 45888 * @static 45889 * @param {Object} dataRoot Root node. 45890 * @param {module:echarts/model/Model} hostModel 45891 * @param {Object} treeOptions 45892 * @param {Array.<Object>} treeOptions.levels 45893 * @param {Array.<Object>} treeOptions.leaves 45894 * @return module:echarts/data/Tree 45895 */ 45896 Tree.createTree = function (dataRoot, hostModel, treeOptions) { 45897 45898 var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves); 45899 var listData = []; 45900 var dimMax = 1; 45901 45902 buildHierarchy(dataRoot); 45903 45904 function buildHierarchy(dataNode, parentNode) { 45905 var value = dataNode.value; 45906 dimMax = Math.max(dimMax, isArray(value) ? value.length : 1); 45907 45908 listData.push(dataNode); 45909 45910 var node = new TreeNode(dataNode.name, tree); 45911 parentNode 45912 ? addChild(node, parentNode) 45913 : (tree.root = node); 45914 45915 tree._nodes.push(node); 45916 45917 var children = dataNode.children; 45918 if (children) { 45919 for (var i = 0; i < children.length; i++) { 45920 buildHierarchy(children[i], node); 45921 } 45922 } 45923 } 45924 45925 tree.root.updateDepthAndHeight(0); 45926 45927 var dimensionsInfo = createDimensions(listData, { 45928 coordDimensions: ['value'], 45929 dimensionsCount: dimMax 45930 }); 45931 45932 var list = new List(dimensionsInfo, hostModel); 45933 list.initData(listData); 45934 45935 linkList({ 45936 mainData: list, 45937 struct: tree, 45938 structAttr: 'tree' 45939 }); 45940
45941 tree.update(); 45942 45943 return tree; 45944 }; 45945 45946 /** 45947 * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote, 45948 * so this function is not ready and not necessary to be public. 45949 * 45950 * @param {(module:echarts/data/Tree~TreeNode|Object)} child 45951 */ 45952 function addChild(child, node) { 45953 var children = node.children; 45954 if (child.parentNode === node) { 45955 return; 45956 } 45957 45958 children.push(child); 45959 child.parentNode = node; 45960 } 45961 45962 /* 45963* Licensed to the Apache Software Foundation (ASF) under one 45964* or more contributor license agreements. See the NOTICE file 45965* distributed with this work for additional information 45966* regarding copyright ownership. The ASF licenses this file 45967* to you under the Apache License, Version 2.0 (the 45968* "License"); you may not use this file except in compliance 45969* with the License. You may obtain a copy of the License at 45970* 45971* http://www.apache.org/licenses/LICENSE-2.0 45972* 45973* Unless required by applicable law or agreed to in writing, 45974* software distributed under the License is distributed on an 45975* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 45976* KIND, either express or implied. See the License for the 45977* specific language governing permissions and limitations 45978* under the License. 45979*/ 45980 45981 /** 45982 * @file Create data struct and define tree view's series model 45983 */ 45984 45985 SeriesModel.extend({ 45986 45987 type: 'series.tree', 45988 45989 layoutInfo: null, 45990 45991 // can support the position parameters 'left', 'top','right','bottom', 'width', 45992 // 'height' in the setOption() with 'merge' mode normal. 45993 layoutMode: 'box', 45994 45995 /** 45996 * Init a tree data structure from data in option series 45997 * @param {Object} option the object used to config echarts view 45998 * @return {module:echarts/data/List} storage initial data 45999 */ 46000 getInitialData: function (option) { 46001 46002 //create an virtual root 46003 var root = {name: option.name, children: option.data}; 46004 46005 var leaves = option.leaves || {}; 46006 46007 var treeOption = {}; 46008 46009 treeOption.leaves = leaves; 46010 46011 var tree = Tree.createTree(root, this, treeOption); 46012 46013 var treeDepth = 0; 46014 46015 tree.eachNode('preorder', function (node) { 46016 if (node.depth > treeDepth) { 46017 treeDepth = node.depth; 46018 } 46019 }); 46020 46021 var expandAndCollapse = option.expandAndCollapse; 46022 var expandTreeDepth = (expandAndCollapse && option.initialTreeDepth >= 0) 46023 ? option.initialTreeDepth : treeDepth; 46024 46025 tree.root.eachNode('preorder', function (node) { 46026 var item = node.hostTree.data.getRawDataItem(node.dataIndex); 46027 // add item.collapsed != null, because users can collapse node original in the series.data. 46028 node.isExpand = (item && item.collapsed != null) 46029 ? !item.collapsed 46030 : node.depth <= expandTreeDepth; 46031 }); 46032 46033 return tree.data; 46034 }, 46035 46036 /** 46037 * Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'. 46038 * @returns {string} orient 46039 */ 46040 getOrient: function () { 46041 var orient = this.get('orient'); 46042 if (orient === 'horizontal') { 46043 orient = 'LR'; 46044 } 46045 else if (orient === 'vertical') { 46046 orient = 'TB'; 46047 } 46048 return orient; 46049 }, 46050 46051 /** 46052 * @override 46053 * @param {number} dataIndex 46054 */ 46055 formatTooltip: function (dataIndex) { 46056 var tree = this.getData().tree; 46057 var realRoot = tree.root.children[0]; 46058 var node = tree.getNodeByDataIndex(dataIndex); 46059 var value = node.getValue(); 46060 var name = node.name; 46061 while (node && (node !== realRoot)) { 46062 name = node.parentNode.name + '.' + name; 46063 node = node.parentNode; 46064 } 46065 return encodeHTML(name + ( 46066 (isNaN(value) || value == null) ? '' : ' : ' + value 46067 )); 46068 }, 46069 46070 defaultOption: { 46071 zlevel: 0, 46072 z: 2, 46073 46074 // the position of the whole view 46075 left: '12%', 46076 top: '12%', 46077 right: '12%', 46078 bottom: '12%', 46079 46080 // the layout of the tree, two value can be selected, 'orthogonal' or 'radial' 46081 layout: 'orthogonal', 46082 46083 // The orient of orthoginal layout, can be setted to 'LR', 'TB', 'RL', 'BT'. 46084 // and the backward compatibility configuration 'horizontal = LR', 'vertical = TB'. 46085 orient: 'LR', 46086 46087 symbol: 'emptyCircle', 46088 46089 symbolSize: 7, 46090 46091 expandAndCollapse: true, 46092 46093 initialTreeDepth: 2, 46094 46095 lineStyle: { 46096 color: '#ccc', 46097 width: 1.5, 46098 curveness: 0.5 46099 }, 46100 46101 itemStyle: { 46102 color: 'lightsteelblue', 46103 borderColor: '#c23531', 46104 borderWidth: 1.5 46105 }, 46106 46107 label: { 46108 show: true, 46109 color: '#555' 46110 }, 46111 46112 leaves: { 46113 label: { 46114 show: true 46115 } 46116 }, 46117 46118 animationEasing: 'linear', 46119 46120 animationDuration: 700, 46121 46122 animationDurationUpdate: 1000 46123 } 46124 });
vendor: 4,095 bytes, lines 46125-46231
46125 46126 /* 46127* Licensed to the Apache Software Foundation (ASF) under one 46128* or more contributor license agreements. See the NOTICE file 46129* distributed with this work for additional information 46130* regarding copyright ownership. The ASF licenses this file 46131* to you under the Apache License, Version 2.0 (the 46132* "License"); you may not use this file except in compliance 46133* with the License. You may obtain a copy of the License at 46134* 46135* http://www.apache.org/licenses/LICENSE-2.0 46136* 46137* Unless required by applicable law or agreed to in writing, 46138* software distributed under the License is distributed on an 46139* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 46140* KIND, either express or implied. See the License for the 46141* specific language governing permissions and limitations 46142* under the License. 46143*/ 46144 46145 /* 46146* The tree layout implementation references to d3.js 46147* (https://github.com/d3/d3-hierarchy). The use of the source 46148* code of this file is also subject to the terms and consitions 46149* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>). 46150*/ 46151 46152 /** 46153 * @file The layout algorithm of node-link tree diagrams. Here we using Reingold-Tilford algorithm to drawing 46154 * the tree. 46155 * @see https://github.com/d3/d3-hierarchy 46156 */ 46157 46158 /** 46159 * Initialize all computational message for following algorithm 46160 * @param {module:echarts/data/Tree~TreeNode} root The virtual root of the tree 46161 */ 46162 function init$2(root) { 46163 root.hierNode = { 46164 defaultAncestor: null, 46165 ancestor: root, 46166 prelim: 0, 46167 modifier: 0, 46168 change: 0, 46169 shift: 0, 46170 i: 0, 46171 thread: null 46172 }; 46173 46174 var nodes = [root]; 46175 var node; 46176 var children; 46177 46178 while (node = nodes.pop()) { // jshint ignore:line 46179 children = node.children; 46180 if (node.isExpand && children.length) { 46181 var n = children.length; 46182 for (var i = n - 1; i >= 0; i--) { 46183 var child = children[i]; 46184 child.hierNode = { 46185 defaultAncestor: null, 46186 ancestor: child, 46187 prelim: 0, 46188 modifier: 0, 46189 change: 0, 46190 shift: 0, 46191 i: i, 46192 thread: null 46193 }; 46194 nodes.push(child); 46195 } 46196 } 46197 } 46198 } 46199 46200 /** 46201 * Computes a preliminary x coordinate for node. Before that, this function is 46202 * applied recursively to the children of node, as well as the function 46203 * apportion(). After spacing out the children by calling executeShifts(), the 46204 * node is placed to the midpoint of its outermost children. 46205 * @param {module:echarts/data/Tree~TreeNode} node 46206 * @param {Function} separation 46207 */ 46208 function firstWalk(node, separation) { 46209 var children = node.isExpand ? node.children : []; 46210 var siblings = node.parentNode.children; 46211 var subtreeW = node.hierNode.i ? siblings[node.hierNode.i -1] : null; 46212 if (children.length) { 46213 executeShifts(node); 46214 var midPoint = (children[0].hierNode.prelim + children[children.length - 1].hierNode.prelim) / 2; 46215 if (subtreeW) { 46216 node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW); 46217 node.hierNode.modifier = node.hierNode.prelim - midPoint; 46218 } 46219 else { 46220 node.hierNode.prelim = midPoint; 46221 } 46222 } 46223 else if (subtreeW) { 46224 node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW); 46225 } 46226 node.parentNode.hierNode.defaultAncestor = apportion(node, subtreeW, node.parentNode.hierNode.defaultAncestor || siblings[0], separation); 46227 } 46228 46229 46230 /** 46231 * Computes all real x-coordinates by summing up the modifiers recursively.
46232 * @param {module:echarts/data/Tree~TreeNode} node 46233 */ 46234 function secondWalk(node) { 46235 var nodeX = node.hierNode.prelim + node.parentNode.hierNode.modifier; 46236 node.setLayout({x: nodeX}, true); 46237 node.hierNode.modifier += node.parentNode.hierNode.modifier; 46238 } 46239 46240 46241 function separation(cb) { 46242 return arguments.length ? cb : defaultSeparation; 46243 } 46244 46245 /** 46246 * Transform the common coordinate to radial coordinate 46247 * @param {number} x 46248 * @param {number} y 46249 * @return {Object} 46250 */ 46251 function radialCoordinate(x, y) { 46252 var radialCoor = {}; 46253 x -= Math.PI / 2; 46254 radialCoor.x = y * Math.cos(x); 46255 radialCoor.y = y * Math.sin(x); 46256 return radialCoor; 46257 } 46258 46259 /** 46260 * Get the layout position of the whole view 46261 * @param {module:echarts/model/Series} seriesModel the model object of sankey series 46262 * @param {module:echarts/ExtensionAPI} api provide the API list that the developer can call 46263 * @return {module:zrender/core/BoundingRect} size of rect to draw the sankey view 46264 */ 46265 function getViewRect(seriesModel, api) { 46266 return getLayoutRect( 46267 seriesModel.getBoxLayoutParams(), { 46268 width: api.getWidth(), 46269 height: api.getHeight() 46270 } 46271 ); 46272 } 46273 46274 /** 46275 * All other shifts, applied to the smaller subtrees between w- and w+, are 46276 * performed by this function. 46277 * @param {module:echarts/data/Tree~TreeNode} node 46278 */ 46279 function executeShifts(node) { 46280 var children = node.children; 46281 var n = children.length; 46282 var shift = 0; 46283 var change = 0; 46284 while (--n >= 0) { 46285 var child = children[n]; 46286 child.hierNode.prelim += shift; 46287 child.hierNode.modifier += shift; 46288 change += child.hierNode.change; 46289 shift += child.hierNode.shift + change; 46290 } 46291 } 46292 46293 /** 46294 * The core of the algorithm. Here, a new subtree is combined with the 46295 * previous subtrees. Threads are used to traverse the inside and outside 46296 * contours of the left and right subtree up to the highest common level. 46297 * Whenever two nodes of the inside contours conflict, we compute the left 46298 * one of the greatest uncommon ancestors using the function nextAncestor() 46299 * and call moveSubtree() to shift the subtree and prepare the shifts of 46300 * smaller subtrees. Finally, we add a new thread (if necessary). 46301 * @param {module:echarts/data/Tree~TreeNode} subtreeV 46302 * @param {module:echarts/data/Tree~TreeNode} subtreeW 46303 * @param {module:echarts/data/Tree~TreeNode} ancestor 46304 * @param {Function} separation 46305 * @return {module:echarts/data/Tree~TreeNode} 46306 */ 46307 function apportion(subtreeV, subtreeW, ancestor, separation) { 46308 46309 if (subtreeW) { 46310 var nodeOutRight = subtreeV; 46311 var nodeInRight = subtreeV; 46312 var nodeOutLeft = nodeInRight.parentNode.children[0]; 46313 var nodeInLeft = subtreeW; 46314 46315 var sumOutRight = nodeOutRight.hierNode.modifier; 46316 var sumInRight = nodeInRight.hierNode.modifier; 46317 var sumOutLeft = nodeOutLeft.hierNode.modifier; 46318 var sumInLeft = nodeInLeft.hierNode.modifier; 46319 46320 while (nodeInLeft = nextRight(nodeInLeft), nodeInRight = nextLeft(nodeInRight), nodeInLeft && nodeInRight) { 46321 nodeOutRight = nextRight(nodeOutRight); 46322 nodeOutLeft = nextLeft(nodeOutLeft); 46323 nodeOutRight.hierNode.ancestor = subtreeV; 46324 var shift = nodeInLeft.hierNode.prelim + sumInLeft - nodeInRight.hierNode.prelim 46325 - sumInRight + separation(nodeInLeft, nodeInRight); 46326 if (shift > 0) { 46327 moveSubtree(nextAncestor(nodeInLeft, subtreeV, ancestor), subtreeV, shift); 46328 sumInRight += shift; 46329 sumOutRight += shift; 46330 } 46331 sumInLeft += nodeInLeft.hierNode.modifier; 46332 sumInRight += nodeInRight.hierNode.modifier; 46333 sumOutRight += nodeOutRight.hierNode.modifier; 46334 sumOutLeft += nodeOutLeft.hierNode.modifier; 46335 } 46336 if (nodeInLeft && !nextRight(nodeOutRight)) { 46337 nodeOutRight.hierNode.thread = nodeInLeft; 46338 nodeOutRight.hierNode.modifier += sumInLeft - sumOutRight; 46339 46340 } 46341 if (nodeInRight && !nextLeft(nodeOutLeft)) { 46342 nodeOutLeft.hierNode.thread = nodeInRight; 46343 nodeOutLeft.hierNode.modifier += sumInRight - sumOutLeft; 46344 ancestor = subtreeV; 46345 } 46346 } 46347 return ancestor; 46348 } 46349 46350 /** 46351 * This function is used to traverse the right contour of a subtree. 46352 * It returns the rightmost child of node or the thread of node. The function 46353 * returns null if and only if node is on the highest depth of its subtree.
46354 * @param {module:echarts/data/Tree~TreeNode} node 46355 * @return {module:echarts/data/Tree~TreeNode} 46356 */ 46357 function nextRight(node) { 46358 var children = node.children; 46359 return children.length && node.isExpand ? children[children.length - 1] : node.hierNode.thread; 46360 } 46361 46362 /** 46363 * This function is used to traverse the left contour of a subtree (or a subforest). 46364 * It returns the leftmost child of node or the thread of node. The function 46365 * returns null if and only if node is on the highest depth of its subtree. 46366 * @param {module:echarts/data/Tree~TreeNode} node 46367 * @return {module:echarts/data/Tree~TreeNode} 46368 */ 46369 function nextLeft(node) { 46370 var children = node.children; 46371 return children.length && node.isExpand ? children[0] : node.hierNode.thread; 46372 } 46373 46374 /** 46375 * If nodeInLeftââ¬â¢s ancestor is a sibling of node, returns nodeInLeftââ¬â¢s ancestor. 46376 * Otherwise, returns the specified ancestor. 46377 * @param {module:echarts/data/Tree~TreeNode} nodeInLeft 46378 * @param {module:echarts/data/Tree~TreeNode} node 46379 * @param {module:echarts/data/Tree~TreeNode} ancestor 46380 * @return {module:echarts/data/Tree~TreeNode} 46381 */ 46382 function nextAncestor(nodeInLeft, node, ancestor) { 46383 return nodeInLeft.hierNode.ancestor.parentNode === node.parentNode 46384 ? nodeInLeft.hierNode.ancestor : ancestor; 46385 } 46386 46387 /** 46388 * Shifts the current subtree rooted at wr. This is done by increasing prelim(w+) and modifier(w+) by shift. 46389 * @param {module:echarts/data/Tree~TreeNode} wl 46390 * @param {module:echarts/data/Tree~TreeNode} wr 46391 * @param {number} shift [description] 46392 */ 46393 function moveSubtree(wl, wr,shift) { 46394 var change = shift / (wr.hierNode.i - wl.hierNode.i); 46395 wr.hierNode.change -= change; 46396 wr.hierNode.shift += shift; 46397 wr.hierNode.modifier += shift; 46398 wr.hierNode.prelim += shift; 46399 wl.hierNode.change += change; 46400 } 46401 46402 function defaultSeparation(node1, node2) { 46403 return node1.parentNode === node2.parentNode ? 1 : 2; 46404 } 46405 46406 /* 46407* Licensed to the Apache Software Foundation (ASF) under one 46408* or more contributor license agreements. See the NOTICE file 46409* distributed with this work for additional information 46410* regarding copyright ownership. The ASF licenses this file 46411* to you under the Apache License, Version 2.0 (the 46412* "License"); you may not use this file except in compliance 46413* with the License. You may obtain a copy of the License at 46414* 46415* http://www.apache.org/licenses/LICENSE-2.0 46416* 46417* Unless required by applicable law or agreed to in writing, 46418* software distributed under the License is distributed on an 46419* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 46420* KIND, either express or implied. See the License for the 46421* specific language governing permissions and limitations 46422* under the License. 46423*/ 46424 46425 /** 46426 * @file This file used to draw tree view 46427 */ 46428 46429 extendChartView({ 46430 46431 type: 'tree', 46432 46433 /** 46434 * Init the chart 46435 * @override 46436 * @param {module:echarts/model/Global} ecModel 46437 * @param {module:echarts/ExtensionAPI} api 46438 */ 46439 init: function (ecModel, api) { 46440 46441 /** 46442 * @private 46443 * @type {module:echarts/data/Tree} 46444 */ 46445 this._oldTree; 46446 46447 /** 46448 * @private 46449 * @type {module:zrender/container/Group} 46450 */ 46451 this._mainGroup = new Group(); 46452 46453 this.group.add(this._mainGroup); 46454 }, 46455 46456 render: function (seriesModel, ecModel, api, payload) { 46457 46458 var data = seriesModel.getData(); 46459 46460 var layoutInfo = seriesModel.layoutInfo; 46461 46462 var group = this._mainGroup; 46463 46464 var layout = seriesModel.get('layout'); 46465 46466 if (layout === 'radial') { 46467 group.attr('position', [layoutInfo.x + layoutInfo.width / 2, layoutInfo.y + layoutInfo.height / 2]); 46468 } 46469 else { 46470 group.attr('position', [layoutInfo.x, layoutInfo.y]); 46471 } 46472 46473 var oldData = this._data; 46474 46475 var seriesScope = { 46476 expandAndCollapse: seriesModel.get('expandAndCollapse'), 46477 layout: layout, 46478 orient: seriesModel.getOrient(), 46479 curvature: seriesModel.get('lineStyle.curveness'), 46480 symbolRotate: seriesModel.get('symbolRotate'), 46481 symbolOffset: seriesModel.get('symbolOffset'), 46482 hoverAnimation: seriesModel.get('hoverAnimation'), 46483 useNameLabel: true, 46484 fadeIn: true 46485 }; 46486 46487 data.diff(oldData) 46488 .add(function (newIdx) { 46489 if (symbolNeedsDraw$1(data, newIdx)) { 46490 // create node and edge 46491 updateNode(data, newIdx, null, group, seriesModel, seriesScope); 46492 } 46493 }) 46494 .update(function (newIdx, oldIdx) { 46495 var symbolEl = oldData.getItemGraphicEl(oldIdx); 46496 if (!symbolNeedsDraw$1(data, newIdx)) { 46497 symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope); 46498 return; 46499 } 46500 // update node and edge 46501 updateNode(data, newIdx, symbolEl, group, seriesModel, seriesScope); 46502 }) 46503 .remove(function (oldIdx) { 46504 var symbolEl = oldData.getItemGraphicEl(oldIdx); 46505 // When remove a collapsed node of subtree, since the collapsed 46506 // node haven't been initialized with a symbol element, 46507 // you can't found it's symbol element through index. 46508 // so if we want to remove the symbol element we should insure 46509 // that the symbol element is not null. 46510 if (symbolEl) { 46511 removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope); 46512 } 46513 }) 46514 .execute(); 46515 46516 if (seriesScope.expandAndCollapse === true) { 46517 data.eachItemGraphicEl(function (el, dataIndex) { 46518 el.off('click').on('click', function () { 46519 api.dispatchAction({ 46520 type: 'treeExpandAndCollapse', 46521 seriesId: seriesModel.id, 46522 dataIndex: dataIndex 46523 }); 46524 }); 46525 }); 46526 } 46527 46528 this._data = data; 46529 }, 46530 46531 dispose: function () {}, 46532 46533 remove: function () { 46534 this._mainGroup.removeAll(); 46535 this._data = null; 46536 } 46537 46538 });
46539 46540 function symbolNeedsDraw$1(data, dataIndex) { 46541 var layout = data.getItemLayout(dataIndex); 46542 46543 return layout 46544 && !isNaN(layout.x) && !isNaN(layout.y) 46545 && data.getItemVisual(dataIndex, 'symbol') !== 'none'; 46546 } 46547 46548 function getTreeNodeStyle(node, itemModel, seriesScope) { 46549 seriesScope.itemModel = itemModel; 46550 seriesScope.itemStyle = itemModel.getModel('itemStyle').getItemStyle(); 46551 seriesScope.hoverItemStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); 46552 seriesScope.lineStyle = itemModel.getModel('lineStyle').getLineStyle(); 46553 seriesScope.labelModel = itemModel.getModel('label'); 46554 seriesScope.hoverLabelModel = itemModel.getModel('emphasis.label'); 46555 46556 if (node.isExpand === false && node.children.length !== 0) { 46557 seriesScope.symbolInnerColor = seriesScope.itemStyle.fill; 46558 } 46559 else { 46560 seriesScope.symbolInnerColor = '#fff'; 46561 } 46562 46563 return seriesScope; 46564 } 46565 46566 function updateNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) { 46567 var isInit = !symbolEl; 46568 var node = data.tree.getNodeByDataIndex(dataIndex); 46569 var itemModel = node.getModel(); 46570 var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope); 46571 var virtualRoot = data.tree.root; 46572 46573 var source = node.parentNode === virtualRoot ? node : node.parentNode || node; 46574 var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex); 46575 var sourceLayout = source.getLayout(); 46576 var sourceOldLayout = sourceSymbolEl 46577 ? { 46578 x: sourceSymbolEl.position[0], 46579 y: sourceSymbolEl.position[1], 46580 rawX: sourceSymbolEl.__radialOldRawX, 46581 rawY: sourceSymbolEl.__radialOldRawY 46582 } 46583 : sourceLayout; 46584 var targetLayout = node.getLayout(); 46585 46586 if (isInit) { 46587 symbolEl = new SymbolClz$1(data, dataIndex, seriesScope); 46588 symbolEl.attr('position', [sourceOldLayout.x, sourceOldLayout.y]); 46589 } 46590 else { 46591 symbolEl.updateData(data, dataIndex, seriesScope); 46592 } 46593 46594 symbolEl.__radialOldRawX = symbolEl.__radialRawX; 46595 symbolEl.__radialOldRawY = symbolEl.__radialRawY; 46596 symbolEl.__radialRawX = targetLayout.rawX; 46597 symbolEl.__radialRawY = targetLayout.rawY; 46598 46599 group.add(symbolEl); 46600 data.setItemGraphicEl(dataIndex, symbolEl); 46601 updateProps(symbolEl, { 46602 position: [targetLayout.x, targetLayout.y] 46603 }, seriesModel); 46604 46605 var symbolPath = symbolEl.getSymbolPath(); 46606 46607 if (seriesScope.layout === 'radial') { 46608 var realRoot = virtualRoot.children[0]; 46609 var rootLayout = realRoot.getLayout(); 46610 var length = realRoot.children.length; 46611 var rad; 46612 var isLeft; 46613 46614 if (targetLayout.x === rootLayout.x && node.isExpand === true) { 46615 var center = {}; 46616 center.x = (realRoot.children[0].getLayout().x + realRoot.children[length - 1].getLayout().x) / 2; 46617 center.y = (realRoot.children[0].getLayout().y + realRoot.children[length - 1].getLayout().y) / 2; 46618 rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x); 46619 if (rad < 0) { 46620 rad = Math.PI * 2 + rad; 46621 } 46622 isLeft = center.x < rootLayout.x; 46623 if (isLeft) { 46624 rad = rad - Math.PI; 46625 } 46626 } 46627 else { 46628 rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x); 46629 if (rad < 0) { 46630 rad = Math.PI * 2 + rad; 46631 } 46632 if (node.children.length === 0 || (node.children.length !== 0 && node.isExpand === false)) { 46633 isLeft = targetLayout.x < rootLayout.x; 46634 if (isLeft) { 46635 rad = rad - Math.PI; 46636 } 46637 } 46638 else { 46639 isLeft = targetLayout.x > rootLayout.x; 46640 if (!isLeft) { 46641 rad = rad - Math.PI; 46642 } 46643 } 46644 } 46645 46646 var textPosition = isLeft ? 'left' : 'right'; 46647 symbolPath.setStyle({ 46648 textPosition: textPosition, 46649 textRotation: -rad, 46650 textOrigin: 'center', 46651 verticalAlign: 'middle' 46652 }); 46653 } 46654 46655 if (node.parentNode && node.parentNode !== virtualRoot) { 46656 var edge = symbolEl.__edge; 46657 if (!edge) { 46658 edge = symbolEl.__edge = new BezierCurve({ 46659 shape: getEdgeShape(seriesScope, sourceOldLayout, sourceOldLayout), 46660 style: defaults({opacity: 0}, seriesScope.lineStyle) 46661 }); 46662 } 46663 46664 updateProps(edge, { 46665 shape: getEdgeShape(seriesScope, sourceLayout, targetLayout), 46666 style: {opacity: 1} 46667 }, seriesModel); 46668 46669 group.add(edge); 46670 } 46671 } 46672 46673 function removeNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) { 46674 var node = data.tree.getNodeByDataIndex(dataIndex); 46675 var virtualRoot = data.tree.root;
46676 var itemModel = node.getModel(); 46677 var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope); 46678 46679 var source = node.parentNode === virtualRoot ? node : node.parentNode || node; 46680 var sourceLayout; 46681 while (sourceLayout = source.getLayout(), sourceLayout == null) { 46682 source = source.parentNode === virtualRoot ? source : source.parentNode || source; 46683 } 46684 46685 updateProps(symbolEl, { 46686 position: [sourceLayout.x + 1, sourceLayout.y + 1] 46687 }, seriesModel, function () { 46688 group.remove(symbolEl); 46689 data.setItemGraphicEl(dataIndex, null); 46690 }); 46691 46692 symbolEl.fadeOut(null, {keepLabel: true}); 46693 46694 var edge = symbolEl.__edge; 46695 if (edge) { 46696 updateProps(edge, { 46697 shape: getEdgeShape(seriesScope, sourceLayout, sourceLayout), 46698 style: { 46699 opacity: 0 46700 } 46701 }, seriesModel, function () { 46702 group.remove(edge); 46703 }); 46704 } 46705 } 46706 46707 function getEdgeShape(seriesScope, sourceLayout, targetLayout) { 46708 var cpx1; 46709 var cpy1; 46710 var cpx2; 46711 var cpy2; 46712 var orient = seriesScope.orient; 46713 46714 if (seriesScope.layout === 'radial') { 46715 var x1 = sourceLayout.rawX; 46716 var y1 = sourceLayout.rawY; 46717 var x2 = targetLayout.rawX; 46718 var y2 = targetLayout.rawY; 46719 46720 var radialCoor1 = radialCoordinate(x1, y1); 46721 var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * seriesScope.curvature); 46722 var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * seriesScope.curvature); 46723 var radialCoor4 = radialCoordinate(x2, y2); 46724 46725 return { 46726 x1: radialCoor1.x, 46727 y1: radialCoor1.y, 46728 x2: radialCoor4.x, 46729 y2: radialCoor4.y, 46730 cpx1: radialCoor2.x, 46731 cpy1: radialCoor2.y, 46732 cpx2: radialCoor3.x, 46733 cpy2: radialCoor3.y 46734 }; 46735 } 46736 else { 46737 var x1 = sourceLayout.x; 46738 var y1 = sourceLayout.y; 46739 var x2 = targetLayout.x; 46740 var y2 = targetLayout.y; 46741 46742 if (orient === 'LR' || orient === 'RL') { 46743 cpx1 = x1 + (x2 - x1) * seriesScope.curvature; 46744 cpy1 = y1; 46745 cpx2 = x2 + (x1 - x2) * seriesScope.curvature; 46746 cpy2 = y2; 46747 } 46748 if (orient === 'TB' || orient === 'BT') { 46749 cpx1 = x1; 46750 cpy1 = y1 + (y2 - y1) * seriesScope.curvature; 46751 cpx2 = x2; 46752 cpy2 = y2 + (y1 - y2) * seriesScope.curvature; 46753 } 46754 return { 46755 x1: x1, 46756 y1: y1, 46757 x2: x2, 46758 y2: y2, 46759 cpx1: cpx1, 46760 cpy1: cpy1, 46761 cpx2: cpx2, 46762 cpy2: cpy2 46763 }; 46764 } 46765 } 46766 46767 /* 46768* Licensed to the Apache Software Foundation (ASF) under one 46769* or more contributor license agreements. See the NOTICE file 46770* distributed with this work for additional information 46771* regarding copyright ownership. The ASF licenses this file 46772* to you under the Apache License, Version 2.0 (the 46773* "License"); you may not use this file except in compliance 46774* with the License. You may obtain a copy of the License at 46775* 46776* http://www.apache.org/licenses/LICENSE-2.0 46777* 46778* Unless required by applicable law or agreed to in writing, 46779* software distributed under the License is distributed on an 46780* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 46781* KIND, either express or implied. See the License for the 46782* specific language governing permissions and limitations 46783* under the License. 46784*/ 46785 46786 registerAction({ 46787 type: 'treeExpandAndCollapse', 46788 event: 'treeExpandAndCollapse', 46789 update: 'update' 46790 }, function (payload, ecModel) {
vendor: 4,692 bytes, lines 46791-46919
46791 ecModel.eachComponent({mainType: 'series', subType: 'tree', query: payload}, function (seriesModel) { 46792 var dataIndex = payload.dataIndex; 46793 var tree = seriesModel.getData().tree; 46794 var node = tree.getNodeByDataIndex(dataIndex); 46795 node.isExpand = !node.isExpand; 46796 46797 }); 46798 }); 46799 46800 /* 46801* Licensed to the Apache Software Foundation (ASF) under one 46802* or more contributor license agreements. See the NOTICE file 46803* distributed with this work for additional information 46804* regarding copyright ownership. The ASF licenses this file 46805* to you under the Apache License, Version 2.0 (the 46806* "License"); you may not use this file except in compliance 46807* with the License. You may obtain a copy of the License at 46808* 46809* http://www.apache.org/licenses/LICENSE-2.0 46810* 46811* Unless required by applicable law or agreed to in writing, 46812* software distributed under the License is distributed on an 46813* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 46814* KIND, either express or implied. See the License for the 46815* specific language governing permissions and limitations 46816* under the License. 46817*/ 46818 46819 46820 /** 46821 * Traverse the tree from bottom to top and do something 46822 * @param {module:echarts/data/Tree~TreeNode} root The real root of the tree 46823 * @param {Function} callback 46824 */ 46825 function eachAfter (root, callback, separation) { 46826 var nodes = [root]; 46827 var next = []; 46828 var node; 46829 46830 while (node = nodes.pop()) { // jshint ignore:line 46831 next.push(node); 46832 if (node.isExpand) { 46833 var children = node.children; 46834 if (children.length) { 46835 for (var i = 0; i < children.length; i++) { 46836 nodes.push(children[i]); 46837 } 46838 } 46839 } 46840 } 46841 46842 while (node = next.pop()) { // jshint ignore:line 46843 callback(node, separation); 46844 } 46845 } 46846 46847 /** 46848 * Traverse the tree from top to bottom and do something 46849 * @param {module:echarts/data/Tree~TreeNode} root The real root of the tree 46850 * @param {Function} callback 46851 */ 46852 function eachBefore (root, callback) { 46853 var nodes = [root]; 46854 var node; 46855 while (node = nodes.pop()) { // jshint ignore:line 46856 callback(node); 46857 if (node.isExpand) { 46858 var children = node.children; 46859 if (children.length) { 46860 for (var i = children.length - 1; i >= 0; i--) { 46861 nodes.push(children[i]); 46862 } 46863 } 46864 } 46865 } 46866 } 46867 46868 /* 46869* Licensed to the Apache Software Foundation (ASF) under one 46870* or more contributor license agreements. See the NOTICE file 46871* distributed with this work for additional information 46872* regarding copyright ownership. The ASF licenses this file 46873* to you under the Apache License, Version 2.0 (the 46874* "License"); you may not use this file except in compliance 46875* with the License. You may obtain a copy of the License at 46876* 46877* http://www.apache.org/licenses/LICENSE-2.0 46878* 46879* Unless required by applicable law or agreed to in writing, 46880* software distributed under the License is distributed on an 46881* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 46882* KIND, either express or implied. See the License for the 46883* specific language governing permissions and limitations 46884* under the License. 46885*/ 46886 46887 var treeLayout = function (ecModel, api) { 46888 ecModel.eachSeriesByType('tree', function (seriesModel) { 46889 commonLayout(seriesModel, api); 46890 }); 46891 }; 46892 46893 function commonLayout(seriesModel, api) { 46894 var layoutInfo = getViewRect(seriesModel, api); 46895 seriesModel.layoutInfo = layoutInfo; 46896 var layout = seriesModel.get('layout'); 46897 var width = 0; 46898 var height = 0; 46899 var separation$$1 = null; 46900 46901 if (layout === 'radial') { 46902 width = 2 * Math.PI; 46903 height = Math.min(layoutInfo.height, layoutInfo.width) / 2; 46904 separation$$1 = separation(function (node1, node2) { 46905 return (node1.parentNode === node2.parentNode ? 1 : 2) / node1.depth; 46906 }); 46907 } 46908 else { 46909 width = layoutInfo.width; 46910 height = layoutInfo.height; 46911 separation$$1 = separation(); 46912 } 46913 46914 var virtualRoot = seriesModel.getData().tree.root; 46915 var realRoot = virtualRoot.children[0]; 46916 46917 if (realRoot) { 46918 init$2(virtualRoot); 46919 eachAfter(realRoot, firstWalk, separation$$1);
vendor: 6,400 bytes, lines 46920-47080
46920 virtualRoot.hierNode.modifier = - realRoot.hierNode.prelim; 46921 eachBefore(realRoot, secondWalk); 46922 46923 var left = realRoot; 46924 var right = realRoot; 46925 var bottom = realRoot; 46926 eachBefore(realRoot, function (node) { 46927 var x = node.getLayout().x; 46928 if (x < left.getLayout().x) { 46929 left = node; 46930 } 46931 if (x > right.getLayout().x) { 46932 right = node; 46933 } 46934 if (node.depth > bottom.depth) { 46935 bottom = node; 46936 } 46937 }); 46938 46939 var delta = left === right ? 1 : separation$$1(left, right) / 2; 46940 var tx = delta - left.getLayout().x; 46941 var kx = 0; 46942 var ky = 0; 46943 var coorX = 0; 46944 var coorY = 0; 46945 if (layout === 'radial') { 46946 kx = width / (right.getLayout().x + delta + tx); 46947 // here we use (node.depth - 1), bucause the real root's depth is 1 46948 ky = height / ((bottom.depth - 1) || 1); 46949 eachBefore(realRoot, function (node) { 46950 coorX = (node.getLayout().x + tx) * kx; 46951 coorY = (node.depth - 1) * ky; 46952 var finalCoor = radialCoordinate(coorX, coorY); 46953 node.setLayout({x: finalCoor.x, y: finalCoor.y, rawX: coorX, rawY: coorY}, true); 46954 }); 46955 } 46956 else { 46957 var orient = seriesModel.getOrient(); 46958 if (orient === 'RL' || orient === 'LR') { 46959 ky = height / (right.getLayout().x + delta + tx); 46960 kx = width / ((bottom.depth - 1) || 1); 46961 eachBefore(realRoot, function (node) { 46962 coorY = (node.getLayout().x + tx) * ky; 46963 coorX = orient === 'LR' 46964 ? (node.depth - 1) * kx 46965 : width - (node.depth - 1) * kx; 46966 node.setLayout({x: coorX, y: coorY}, true); 46967 }); 46968 } 46969 else if (orient === 'TB' || orient === 'BT') { 46970 kx = width / (right.getLayout().x + delta + tx); 46971 ky = height / ((bottom.depth - 1) || 1); 46972 eachBefore(realRoot, function (node) { 46973 coorX = (node.getLayout().x + tx) * kx; 46974 coorY = orient === 'TB' 46975 ? (node.depth - 1) * ky 46976 : height - (node.depth - 1) * ky; 46977 node.setLayout({x: coorX, y: coorY}, true); 46978 }); 46979 } 46980 } 46981 } 46982 } 46983 46984 /* 46985* Licensed to the Apache Software Foundation (ASF) under one 46986* or more contributor license agreements. See the NOTICE file 46987* distributed with this work for additional information 46988* regarding copyright ownership. The ASF licenses this file 46989* to you under the Apache License, Version 2.0 (the 46990* "License"); you may not use this file except in compliance 46991* with the License. You may obtain a copy of the License at 46992* 46993* http://www.apache.org/licenses/LICENSE-2.0 46994* 46995* Unless required by applicable law or agreed to in writing, 46996* software distributed under the License is distributed on an 46997* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 46998* KIND, either express or implied. See the License for the 46999* specific language governing permissions and limitations 47000* under the License. 47001*/ 47002 47003 registerVisual(visualSymbol('tree', 'circle')); 47004 registerLayout(treeLayout); 47005 47006 /* 47007* Licensed to the Apache Software Foundation (ASF) under one 47008* or more contributor license agreements. See the NOTICE file 47009* distributed with this work for additional information 47010* regarding copyright ownership. The ASF licenses this file 47011* to you under the Apache License, Version 2.0 (the 47012* "License"); you may not use this file except in compliance 47013* with the License. You may obtain a copy of the License at 47014* 47015* http://www.apache.org/licenses/LICENSE-2.0 47016* 47017* Unless required by applicable law or agreed to in writing, 47018* software distributed under the License is distributed on an 47019* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 47020* KIND, either express or implied. See the License for the 47021* specific language governing permissions and limitations 47022* under the License. 47023*/ 47024 47025 function retrieveTargetInfo(payload, validPayloadTypes, seriesModel) { 47026 if (payload && indexOf(validPayloadTypes, payload.type) >= 0) { 47027 var root = seriesModel.getData().tree.root; 47028 var targetNode = payload.targetNode; 47029 if (targetNode && root.contains(targetNode)) { 47030 return {node: targetNode}; 47031 } 47032 47033 var targetNodeId = payload.targetNodeId; 47034 if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) { 47035 return {node: targetNode}; 47036 } 47037 } 47038 } 47039 47040// Not includes the given node at the last item. 47041 function getPathToRoot(node) { 47042 var path = []; 47043 while (node) { 47044 node = node.parentNode; 47045 node && path.push(node); 47046 } 47047 return path.reverse(); 47048 } 47049 47050 function aboveViewRoot(viewRoot, node) { 47051 var viewPath = getPathToRoot(viewRoot); 47052 return indexOf(viewPath, node) >= 0; 47053 } 47054 47055// From root to the input node (the input node will be included). 47056 function wrapTreePathInfo(node, seriesModel) { 47057 var treePathInfo = []; 47058 47059 while (node) { 47060 var nodeDataIndex = node.dataIndex; 47061 treePathInfo.push({ 47062 name: node.name, 47063 dataIndex: nodeDataIndex, 47064 value: seriesModel.getRawValue(nodeDataIndex) 47065 }); 47066 node = node.parentNode; 47067 } 47068 47069 treePathInfo.reverse(); 47070 47071 return treePathInfo; 47072 } 47073 47074 /* 47075* Licensed to the Apache Software Foundation (ASF) under one 47076* or more contributor license agreements. See the NOTICE file 47077* distributed with this work for additional information 47078* regarding copyright ownership. The ASF licenses this file 47079* to you under the Apache License, Version 2.0 (the 47080* "License");
47080 you may not use this file except in compliance 47081* with the License. You may obtain a copy of the License at 47082* 47083* http://www.apache.org/licenses/LICENSE-2.0 47084* 47085* Unless required by applicable law or agreed to in writing, 47086* software distributed under the License is distributed on an 47087* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 47088* KIND, either express or implied. See the License for the 47089* specific language governing permissions and limitations 47090* under the License. 47091*/ 47092 47093 SeriesModel.extend({ 47094 47095 type: 'series.treemap', 47096 47097 layoutMode: 'box', 47098 47099 dependencies: ['grid', 'polar'], 47100 47101 /** 47102 * @type {module:echarts/data/Tree~Node} 47103 */ 47104 _viewRoot: null, 47105 47106 defaultOption: { 47107 // Disable progressive rendering 47108 progressive: 0, 47109 hoverLayerThreshold: Infinity, 47110 // center: ['50%', '50%'], // not supported in ec3. 47111 // size: ['80%', '80%'], // deprecated, compatible with ec2. 47112 left: 'center', 47113 top: 'middle', 47114 right: null, 47115 bottom: null, 47116 width: '80%', 47117 height: '80%', 47118 sort: true, // Can be null or false or true 47119 // (order by desc default, asc not supported yet (strange effect)) 47120 clipWindow: 'origin', // Size of clipped window when zooming. 'origin' or 'fullscreen' 47121 squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio 47122 leafDepth: null, // Nodes on depth from root are regarded as leaves. 47123 // Count from zero (zero represents only view root). 47124 drillDownIcon: 'ââ¶', // Use html character temporarily because it is complicated 47125 // to align specialized icon. ââ·ââ¶âÂââÂÂââ¼âÅÅ¡ 47126 47127 zoomToNodeRatio: 0.32 * 0.32, // Be effective when using zoomToNode. Specify the proportion of the 47128 // target node area in the view area. 47129 roam: true, // true, false, 'scale' or 'zoom', 'move'. 47130 nodeClick: 'zoomToNode', // Leaf node click behaviour: 'zoomToNode', 'link', false. 47131 // If leafDepth is set and clicking a node which has children but 47132 // be on left depth, the behaviour would be changing root. Otherwise 47133 // use behavious defined above. 47134 animation: true, 47135 animationDurationUpdate: 900, 47136 animationEasing: 'quinticInOut', 47137 breadcrumb: { 47138 show: true, 47139 height: 22, 47140 left: 'center', 47141 top: 'bottom', 47142 // right 47143 // bottom
47144 emptyItemWidth: 25, // Width of empty node. 47145 itemStyle: { 47146 color: 'rgba(0,0,0,0.7)', //'#5793f3', 47147 borderColor: 'rgba(255,255,255,0.7)', 47148 borderWidth: 1, 47149 shadowColor: 'rgba(150,150,150,1)', 47150 shadowBlur: 3, 47151 shadowOffsetX: 0, 47152 shadowOffsetY: 0, 47153 textStyle: { 47154 color: '#fff' 47155 } 47156 }, 47157 emphasis: { 47158 textStyle: {} 47159 } 47160 }, 47161 label: { 47162 show: true, 47163 // Do not use textDistance, for ellipsis rect just the same as treemap node rect. 47164 distance: 0, 47165 padding: 5, 47166 position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ... 47167 // formatter: null, 47168 color: '#fff', 47169 ellipsis: true 47170 // align 47171 // verticalAlign 47172 }, 47173 upperLabel: { // Label when node is parent. 47174 show: false, 47175 position: [0, '50%'], 47176 height: 20, 47177 // formatter: null, 47178 color: '#fff', 47179 ellipsis: true, 47180 // align: null, 47181 verticalAlign: 'middle' 47182 }, 47183 itemStyle: { 47184 color: null, // Can be 'none' if not necessary. 47185 colorAlpha: null, // Can be 'none' if not necessary. 47186 colorSaturation: null, // Can be 'none' if not necessary. 47187 borderWidth: 0, 47188 gapWidth: 0, 47189 borderColor: '#fff', 47190 borderColorSaturation: null // If specified, borderColor will be ineffective, and the 47191 // border color is evaluated by color of current node and 47192 // borderColorSaturation. 47193 }, 47194 emphasis: { 47195 upperLabel: { 47196 show: true, 47197 position: [0, '50%'], 47198 color: '#fff', 47199 ellipsis: true, 47200 verticalAlign: 'middle' 47201 } 47202 }, 47203 47204 visualDimension: 0, // Can be 0, 1, 2, 3. 47205 visualMin: null, 47206 visualMax: null, 47207 47208 color: [], // + treemapSeries.color should not be modified. Please only modified 47209 // level[n].color (if necessary). 47210 // + Specify color list of each level. level[0].color would be global 47211 // color list if not specified. (see method `setDefault`). 47212 // + But set as a empty array to forbid fetch color from global palette 47213 // when using nodeModel.get('color'), otherwise nodes on deep level 47214 // will always has color palette set and are not able to inherit color 47215 // from parent node. 47216 // + TreemapSeries.color can not be set as 'none', otherwise effect 47217 // legend color fetching (see seriesColor.js). 47218 colorAlpha: null, // Array. Specify color alpha range of each level, like [0.2, 0.8] 47219 colorSaturation: null, // Array. Specify color saturation of each level, like [0.2, 0.5] 47220 colorMappingBy: 'index', // 'value' or 'index' or 'id'. 47221 visibleMin: 10, // If area less than this threshold (unit: pixel^2), node will not 47222 // be rendered. Only works when sort is 'asc' or 'desc'. 47223 childrenVisibleMin: null, // If area of a node less than this threshold (unit: pixel^2), 47224 // grandchildren will not show. 47225 // Why grandchildren? If not grandchildren but children, 47226 // some siblings show children and s
47226ome not, 47227 // the appearance may be mess and not consistent, 47228 levels: [] // Each item: { 47229 // visibleMin, itemStyle, visualDimension, label 47230 // } 47231 // data: { 47232 // value: [], 47233 // children: [], 47234 // link: 'http://xxx.xxx.xxx', 47235 // target: 'blank' or 'self' 47236 // } 47237 }, 47238 47239 /** 47240 * @override 47241 */ 47242 getInitialData: function (option, ecModel) { 47243 // Create a virtual root. 47244 var root = {name: option.name, children: option.data}; 47245 47246 completeTreeValue(root); 47247 47248 var levels = option.levels || []; 47249 47250 levels = option.levels = setDefault(levels, ecModel); 47251 47252 var treeOption = {}; 47253 47254 treeOption.levels = levels; 47255 47256 // Make sure always a new tree is created when setOption, 47257 // in TreemapView, we check whether oldTree === newTree 47258 // to choose mappings approach among old shapes and new shapes. 47259 return Tree.createTree(root, this, treeOption).data; 47260 }, 47261 47262 optionUpdated: function () { 47263 this.resetViewRoot(); 47264 }, 47265 47266 /** 47267 * @override 47268 * @param {number} dataIndex 47269 * @param {boolean} [mutipleSeries=false] 47270 */ 47271 formatTooltip: function (dataIndex) { 47272 var data = this.getData(); 47273 var value = this.getRawValue(dataIndex); 47274 var formattedValue = isArray(value) 47275 ? addCommas(value[0]) : addCommas(value); 47276 var name = data.getName(dataIndex); 47277 47278 return encodeHTML(name + ': ' + formattedValue); 47279 }, 47280 47281 /** 47282 * Add tree path to tooltip param 47283 * 47284 * @override 47285 * @param {number} dataIndex 47286 * @return {Object} 47287 */ 47288 getDataParams: function (dataIndex) { 47289 var params = SeriesModel.prototype.getDataParams.apply(this, arguments); 47290 47291 var node = this.getData().tree.getNodeByDataIndex(dataIndex); 47292 params.treePathInfo = wrapTreePathInfo(node, this); 47293 47294 return params; 47295 }, 47296 47297 /** 47298 * @public 47299 * @param {Object} layoutInfo { 47300 * x: containerGroup x 47301 * y: containerGroup y 47302 * width: containerGroup width 47303 * height: containerGroup height 47304 * } 47305 */ 47306 setLayoutInfo: function (layoutInfo) { 47307 /** 47308 * @readOnly 47309 * @type {Object} 47310 */ 47311 this.layoutInfo = this.layoutInfo || {}; 47312 extend(this.layoutInfo, layoutInfo); 47313 }, 47314 47315 /** 47316 * @param {string} id 47317 * @return {number} index 47318 */ 47319 mapIdToIndex: function (id) { 47320 // A feature is implemented: 47321 // index is monotone increasing with the sequence of 47322 // input id at the first time. 47323 // This feature can make sure that each data item and its 47324 // mapped color have the same index between data list and 47325 // color list at the beginning, which is useful for user 47326 // to adjust data-color mapping. 47327 47328 /** 47329 * @private 47330 * @type {Object} 47331 */ 47332 var idIndexMap = this._idIndexMap; 47333 47334 if (!idIndexMap) { 47335 idIndexMap = this._idIndexMap = createHashMap(); 47336 /** 47337 * @private 47338 * @type {number} 47339 */ 47340 this._idIndexMapCount = 0; 47341 } 47342 47343 var index = idIndexMap.get(id); 47344 if (index == null) { 47345 idIndexMap.set(id, index = this._idIndexMapCount++); 47346 } 47347 47348 return index; 47349 }, 47350 47351 getViewRoot: function () { 47352 return this._viewRoot; 47353 }, 47354 47355 /**
47356 * @param {module:echarts/data/Tree~Node} [viewRoot] 47357 */ 47358 resetViewRoot: function (viewRoot) { 47359 viewRoot 47360 ? (this._viewRoot = viewRoot) 47361 : (viewRoot = this._viewRoot); 47362 47363 var root = this.getRawData().tree.root; 47364 47365 if (!viewRoot 47366 || (viewRoot !== root && !root.contains(viewRoot)) 47367 ) { 47368 this._viewRoot = root; 47369 } 47370 } 47371 }); 47372 47373 /** 47374 * @param {Object} dataNode 47375 */ 47376 function completeTreeValue(dataNode) { 47377 // Postorder travel tree. 47378 // If value of none-leaf node is not set, 47379 // calculate it by suming up the value of all children. 47380 var sum = 0; 47381 47382 each$1(dataNode.children, function (child) { 47383 47384 completeTreeValue(child); 47385 47386 var childValue = child.value; 47387 isArray(childValue) && (childValue = childValue[0]); 47388 47389 sum += childValue; 47390 }); 47391 47392 var thisValue = dataNode.value; 47393 if (isArray(thisValue)) { 47394 thisValue = thisValue[0]; 47395 } 47396 47397 if (thisValue == null || isNaN(thisValue)) { 47398 thisValue = sum; 47399 } 47400 // Value should not less than 0. 47401 if (thisValue < 0) { 47402 thisValue = 0; 47403 } 47404 47405 isArray(dataNode.value) 47406 ? (dataNode.value[0] = thisValue) 47407 : (dataNode.value = thisValue); 47408 } 47409 47410 /** 47411 * set default to level configuration 47412 */ 47413 function setDefault(levels, ecModel) { 47414 var globalColorList = ecModel.get('color'); 47415 47416 if (!globalColorList) { 47417 return; 47418 } 47419 47420 levels = levels || []; 47421 var hasColorDefine; 47422 each$1(levels, function (levelDefine) { 47423 var model = new Model(levelDefine); 47424 var modelColor = model.get('color'); 47425 47426 if (model.get('itemStyle.color') 47427 || (modelColor && modelColor !== 'none') 47428 ) { 47429 hasColorDefine = true; 47430 } 47431 }); 47432 47433 if (!hasColorDefine) { 47434 var level0 = levels[0] || (levels[0] = {}); 47435 level0.color = globalColorList.slice(); 47436 } 47437 47438 return levels; 47439 } 47440 47441 /* 47442* Licensed to the Apache Software Foundation (ASF) under one 47443* or more contributor license agreements. See the NOTICE file 47444* distributed with this work for additional information 47445* regarding copyright ownership. The ASF licenses this file 47446* to you under the Apache License, Version 2.0 (the 47447* "License"); you may not use this file except in compliance 47448* with the License. You may obtain a copy of the License at 47449* 47450* http://www.apache.org/licenses/LICENSE-2.0 47451* 47452* Unless required by applicable law or agreed to in writing, 47453* software distributed under the License is distributed on an 47454* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 47455* KIND, either express or implied. See the License for the 47456* specific language governing permissions and limitations 47457* under the License. 47458*/ 47459 47460 var TEXT_PADDING = 8; 47461 var ITEM_GAP = 8; 47462 var ARRAY_LENGTH = 5; 47463 47464 function Breadcrumb(containerGroup) { 47465 /** 47466 * @private 47467 * @type {module:zrender/container/Group} 47468 */ 47469 this.group = new Group(); 47470 47471 containerGroup.add(this.group); 47472 } 47473 47474 Breadcrumb.prototype = { 47475 47476 constructor: Breadcrumb, 47477 47478 render: function (seriesModel, api, targetNode, onSelect) { 47479 var model = seriesModel.getModel('breadcrumb'); 47480 var thisGroup = this.group; 47481 47482 thisGroup.removeAll(); 47483 47484 if (!model.get('show') || !targetNode) { 47485 return; 47486 } 47487 47488 var normalStyleModel = model.getModel('itemStyle'); 47489 // var emphasisStyleModel = model.getModel('emphasis.itemStyle'); 47490 var textStyleModel = normalStyleModel.getModel('textStyle'); 47491 47492 var layoutParam = { 47493 pos: { 47494 left: model.get('left'),
47495 right: model.get('right'), 47496 top: model.get('top'), 47497 bottom: model.get('bottom') 47498 }, 47499 box: { 47500 width: api.getWidth(), 47501 height: api.getHeight() 47502 }, 47503 emptyItemWidth: model.get('emptyItemWidth'), 47504 totalWidth: 0, 47505 renderList: [] 47506 }; 47507 47508 this._prepare(targetNode, layoutParam, textStyleModel); 47509 this._renderContent(seriesModel, layoutParam, normalStyleModel, textStyleModel, onSelect); 47510 47511 positionElement(thisGroup, layoutParam.pos, layoutParam.box); 47512 }, 47513 47514 /** 47515 * Prepare render list and total width 47516 * @private 47517 */ 47518 _prepare: function (targetNode, layoutParam, textStyleModel) { 47519 for (var node = targetNode; node; node = node.parentNode) { 47520 var text = node.getModel().get('name'); 47521 var textRect = textStyleModel.getTextRect(text); 47522 var itemWidth = Math.max( 47523 textRect.width + TEXT_PADDING * 2, 47524 layoutParam.emptyItemWidth 47525 ); 47526 layoutParam.totalWidth += itemWidth + ITEM_GAP; 47527 layoutParam.renderList.push({node: node, text: text, width: itemWidth}); 47528 } 47529 }, 47530 47531 /** 47532 * @private 47533 */ 47534 _renderContent: function ( 47535 seriesModel, layoutParam, normalStyleModel, textStyleModel, onSelect 47536 ) { 47537 // Start rendering. 47538 var lastX = 0; 47539 var emptyItemWidth = layoutParam.emptyItemWidth; 47540 var height = seriesModel.get('breadcrumb.height'); 47541 var availableSize = getAvailableSize(layoutParam.pos, layoutParam.box); 47542 var totalWidth = layoutParam.totalWidth; 47543 var renderList = layoutParam.renderList; 47544 47545 for (var i = renderList.length - 1; i >= 0; i--) { 47546 var item = renderList[i]; 47547 var itemNode = item.node; 47548 var itemWidth = item.width; 47549 var text = item.text; 47550 47551 // Hdie text and shorten width if necessary. 47552 if (totalWidth > availableSize.width) { 47553 totalWidth -= itemWidth - emptyItemWidth; 47554 itemWidth = emptyItemWidth; 47555 text = null; 47556 } 47557 47558 var el = new Polygon({ 47559 shape: { 47560 points: makeItemPoints( 47561 lastX, 0, itemWidth, height, 47562 i === renderList.length - 1, i === 0 47563 ) 47564 }, 47565 style: defaults( 47566 normalStyleModel.getItemStyle(), 47567 { 47568 lineJoin: 'bevel', 47569 text: text, 47570 textFill: textStyleModel.getTextColor(), 47571 textFont: textStyleModel.getFont() 47572 } 47573 ), 47574 z: 10, 47575 onclick: curry(onSelect, itemNode) 47576 }); 47577 this.group.add(el); 47578 47579 packEventData(el, seriesModel, itemNode); 47580 47581 lastX += itemWidth + ITEM_GAP; 47582 } 47583 }, 47584 47585 /** 47586 * @override 47587 */ 47588 remove: function () { 47589 this.group.removeAll(); 47590 } 47591 }; 47592 47593 function makeItemPoints(x, y, itemWidth, itemHeight, head, tail) { 47594 var points = [ 47595 [head ? x : x - ARRAY_LENGTH, y], 47596 [x + itemWidth, y], 47597 [x + itemWidth, y + itemHeight], 47598 [head ? x : x - ARRAY_LENGTH, y + itemHeight] 47599 ]; 47600 !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]); 47601 !head && points.push([x, y + itemHeight / 2]); 47602 return points; 47603 } 47604 47605// Package custom mouse event. 47606 function packEventData(el, seriesModel, itemNode) { 47607 el.eventData = { 47608 componentType: 'series', 47609 componentSubType: 'treemap', 47610 seriesIndex: seriesModel.componentIndex, 47611 seriesName: seriesModel.name, 47612 seriesType: 'treemap', 47613 selfType: 'breadcrumb', // Distinguish with click event on treemap node. 47614 nodeData: { 47615 dataIndex: itemNode && itemNode.dataIndex, 47616 name: itemNode && itemNode.name 47617 }, 47618 treePathInfo: itemNode && wrapTreePathInfo(itemNode, seriesModel) 47619 }; 47620 } 47621 47622 /* 47623* Licensed to the Apache Software Foundation (ASF) under one 47624* or more contributor license agreements. See the NOTICE file 47625* distributed with this work for additional information 47626* regarding copyright ownership. The ASF licenses this file 47627* to you under the Apache License, Version 2.0 (the 47628* "License");
47628 you may not use this file except in compliance 47629* with the License. You may obtain a copy of the License at 47630* 47631* http://www.apache.org/licenses/LICENSE-2.0 47632* 47633* Unless required by applicable law or agreed to in writing, 47634* software distributed under the License is distributed on an 47635* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 47636* KIND, either express or implied. See the License for the 47637* specific language governing permissions and limitations 47638* under the License. 47639*/ 47640 47641 /** 47642 * @param {number} [time=500] Time in ms 47643 * @param {string} [easing='linear'] 47644 * @param {number} [delay=0] 47645 * @param {Function} [callback] 47646 * 47647 * @example 47648 * // Animate position 47649 * animation 47650 * .createWrap() 47651 * .add(el1, {position: [10, 10]}) 47652 * .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400) 47653 * .done(function () { // done }) 47654 * .start('cubicOut'); 47655 */ 47656 function createWrap() { 47657 47658 var storage = []; 47659 var elExistsMap = {}; 47660 var doneCallback; 47661 47662 return { 47663 47664 /** 47665 * Caution: a el can only be added once, otherwise 'done' 47666 * might not be called. This method checks this (by el.id), 47667 * suppresses adding and returns false when existing el found. 47668 * 47669 * @param {modele:zrender/Element} el 47670 * @param {Object} target 47671 * @param {number} [time=500] 47672 * @param {number} [delay=0] 47673 * @param {string} [easing='linear'] 47674 * @return {boolean} Whether adding succeeded. 47675 * 47676 * @example 47677 * add(el, target, time, delay, easing); 47678 * add(el, target, time, easing); 47679 * add(el, target, time); 47680 * add(el, target); 47681 */ 47682 add: function (el, target, time, delay, easing) { 47683 if (isString(delay)) { 47684 easing = delay; 47685 delay = 0; 47686 } 47687 47688 if (elExistsMap[el.id]) { 47689 return false; 47690 } 47691 elExistsMap[el.id] = 1; 47692 47693 storage.push( 47694 {el: el, target: target, time: time, delay: delay, easing: easing} 47695 ); 47696 47697 return true; 47698 }, 47699 47700 /** 47701 * Only execute when animation finished. Will not execute when any 47702 * of 'stop' or 'stopAnimation' called. 47703 * 47704 * @param {Function} callback 47705 */ 47706 done: function (callback) { 47707 doneCallback = callback; 47708 return this; 47709 }, 47710 47711 /** 47712 * Will stop exist animation firstly. 47713 */ 47714 start: function () { 47715 var count = storage.length; 47716 47717 for (var i = 0, len = storage.length; i < len; i++) { 47718 var item = storage[i]; 47719 item.el.animateTo(item.target, item.time, item.delay, item.easing, done); 47720 } 47721 47722 return this; 47723 47724 function done() { 47725 count--; 47726 if (!count) { 47727 storage.length = 0; 47728 elExistsMap = {}; 47729 doneCallback && doneCallback(); 47730 } 47731 } 47732 } 47733 }; 47734 } 47735 47736 /* 47737* Licensed to the Apache Software Foundation (ASF) under one 47738* or more contributor license agreements. See the NOTICE file 47739* distributed with this work for additional information 47740* regarding copyright ownership. The ASF licenses this file 47741* to you under the Apache License, Version 2.0 (the 47742* "License"); you may not use this file except in compliance 47743* with the License. You may obtain a copy of the License at 47744* 47745* http://www.apache.org/licenses/LICENSE-2.0 47746* 47747* Unless required by applicable law or agreed to in writing, 47748* software distributed under the License is distributed on an 47749* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 47750* KIND, either express or implied. See the License for the 47751* specific language governing permissions and limitations 47752* under the License. 47753*/ 47754 47755 var bind$1 = bind; 47756 var Group$2 = Group; 47757 var Rect$1 = Rect; 47758 var each$8 = each$1; 47759 47760 var DRAG_THRESHOLD = 3; 47761 var PATH_LABEL_NOAMAL = ['label']; 47762 var PATH_LABEL_EMPHASIS = ['emphasis', 'label']; 47763 var PATH_UPPERLABEL_NORMAL = ['upperLabel']; 47764 var PATH_UPPERLABEL_EMPHASIS = ['emphasis', 'upperLabel']; 47765 var Z_BASE = 10; // Should bigger than every z. 47766 var Z_BG = 1; 47767 var Z_CONTENT = 2; 47768
47769 var getItemStyleEmphasis = makeStyleMapper([ 47770 ['fill', 'color'], 47771 // `borderColor` and `borderWidth` has been occupied, 47772 // so use `stroke` to indicate the stroke of the rect. 47773 ['stroke', 'strokeColor'], 47774 ['lineWidth', 'strokeWidth'], 47775 ['shadowBlur'], 47776 ['shadowOffsetX'], 47777 ['shadowOffsetY'], 47778 ['shadowColor'] 47779 ]); 47780 var getItemStyleNormal = function (model) { 47781 // Normal style props should include emphasis style props. 47782 var itemStyle = getItemStyleEmphasis(model); 47783 // Clear styles set by emphasis. 47784 itemStyle.stroke = itemStyle.fill = itemStyle.lineWidth = null; 47785 return itemStyle; 47786 }; 47787 47788 extendChartView({ 47789 47790 type: 'treemap', 47791 47792 /** 47793 * @override 47794 */ 47795 init: function (o, api) { 47796 47797 /** 47798 * @private 47799 * @type {module:zrender/container/Group} 47800 */ 47801 this._containerGroup; 47802 47803 /** 47804 * @private 47805 * @type {Object.<string, Array.<module:zrender/container/Group>>} 47806 */ 47807 this._storage = createStorage(); 47808 47809 /** 47810 * @private 47811 * @type {module:echarts/data/Tree} 47812 */ 47813 this._oldTree; 47814 47815 /** 47816 * @private 47817 * @type {module:echarts/chart/treemap/Breadcrumb} 47818 */ 47819 this._breadcrumb; 47820 47821 /** 47822 * @private 47823 * @type {module:echarts/component/helper/RoamController} 47824 */ 47825 this._controller; 47826 47827 /** 47828 * 'ready', 'animating' 47829 * @private 47830 */ 47831 this._state = 'ready'; 47832 }, 47833 47834 /** 47835 * @override 47836 */ 47837 render: function (seriesModel, ecModel, api, payload) { 47838 47839 var models = ecModel.findComponents({ 47840 mainType: 'series', subType: 'treemap', query: payload 47841 }); 47842 if (indexOf(models, seriesModel) < 0) { 47843 return; 47844 } 47845 47846 this.seriesModel = seriesModel; 47847 this.api = api; 47848 this.ecModel = ecModel; 47849 47850 var types = ['treemapZoomToNode', 'treemapRootToNode']; 47851 var targetInfo = retrieveTargetInfo(payload, types, seriesModel); 47852 var payloadType = payload && payload.type; 47853 var layoutInfo = seriesModel.layoutInfo; 47854 var isInit = !this._oldTree; 47855 var thisStorage = this._storage; 47856 47857 // Mark new root when action is treemapRootToNode. 47858 var reRoot = (payloadType === 'treemapRootToNode' && targetInfo && thisStorage) 47859 ? { 47860 rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()], 47861 direction: payload.direction 47862 } 47863 : null; 47864 47865 var containerGroup = this._giveContainerGroup(layoutInfo); 47866 47867 var renderResult = this._doRender(containerGroup, seriesModel, reRoot); 47868 ( 47869 !isInit && ( 47870 !payloadType 47871 || payloadType === 'treemapZoomToNode' 47872 || payloadType === 'treemapRootToNode' 47873 ) 47874 ) 47875 ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot) 47876 : renderResult.renderFinally(); 47877 47878 this._resetController(api); 47879 47880 this._renderBreadcrumb(seriesModel, api, targetInfo); 47881 }, 47882 47883 /** 47884 * @private 47885 */ 47886 _giveContainerGroup: function (layoutInfo) { 47887 var containerGroup = this._containerGroup; 47888 if (!containerGroup) { 47889 // FIXME 47890 // åŠä¸â¬Ã¥Â±âcontainerGroupæË¯ä¸ºäºâ clipï¼Åä½â æË¯çްåŨclip功èÆÂ½Ã¥Â¹Â¶Ã¦Â²Â¡Ã¦Åâ°Ã¥Â®Å¾Ã§Å½Â°Ã£â¬â 47891 containerGroup = this._containerGroup = new Group$2(); 47892 this._initEvents(containerGroup); 47893 this.group.add(containerGroup); 47894 } 47895 containerGroup.attr('position', [layoutInfo.x, layoutInfo.y]); 47896 47897 return containerGroup; 47898 }, 47899 47900 /** 47901 * @private 47902 */ 47903 _doRender: function (containerGroup, seriesModel, reRoot) { 47904 var thisTree = seriesModel.getData().tree; 47905 var oldTree = this._oldTree; 47906 47907 // Clear last shape records. 47908 var lastsForAnimation = createStorage(); 47909 var thisStorage = createStorage(); 47910 var oldStorage = this._storage; 47911 var willInvisibleEls = []; 47912 var doRenderNode = curry( 47913 renderNode, seriesModel, 47914 thisStorage, oldStorage, reRoot, 47915 lastsForAnimation, willInvisibleEls 47916 ); 47917 47918 // Notice: when thisTree and oldTree are the same tree (see list.cloneShallow), 47919 // the oldTree is actually losted, so we can not find all of the old graphic 47920 // elements from tree. So we use this stragegy: make element storage, move 47921 // from old storage to new storage, clear old storage. 47922 47923 dualTravel( 47924 thisTree.root ? [thisTree.root] : [], 47925 (oldTree && oldTree.root) ? [oldTree.root] : [], 47926 containerGroup, 47927 thisTree === oldTree || !oldTree, 47928 0 47929 ); 47930 47931 // Process all removing. 47932 var willDeleteEls = clearStorage(oldStorage); 47933 47934 this._oldTree = thisTree; 47935 this._storage = thisStorage; 47936 47937 return { 47938 lastsForAnimation: lastsForAnimation, 47939 willDeleteEls: willDeleteEls, 47940 renderFinally: renderFinally 47941 }; 47942 47943 function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) { 47944 // When 'render' is triggered by action, 47945 // 'this' and 'old' may be the same tree, 47946 // we use rawIndex in that case. 47947 if (sameTree) { 47948 oldViewChildren = thisViewChildren; 47949 each$8(thisViewChildren, function (child, index) { 47950 !child.isRemoved() && processNode(index, index); 47951 }); 47952 } 47953 // Diff hierarchically (diff only in each subtree, but not whole). 47954 // because, consistency of view is important. 47955 else { 47956 (new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey)) 47957 .add(processNode) 47958 .update(processNode) 47959 .remove(curry(processNode, null)) 47960 .execute(); 47961 } 47962 47963 function getKey(node) { 47964 // Identify by name or raw index. 47965 return node.getId(); 47966 } 47967
47968 function processNode(newIndex, oldIndex) { 47969 var thisNode = newIndex != null ? thisViewChildren[newIndex] : null; 47970 var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null; 47971 47972 var group = doRenderNode(thisNode, oldNode, parentGroup, depth); 47973 47974 group && dualTravel( 47975 thisNode && thisNode.viewChildren || [], 47976 oldNode && oldNode.viewChildren || [], 47977 group, 47978 sameTree, 47979 depth + 1 47980 ); 47981 } 47982 } 47983 47984 function clearStorage(storage) { 47985 var willDeleteEls = createStorage(); 47986 storage && each$8(storage, function (store, storageName) { 47987 var delEls = willDeleteEls[storageName]; 47988 each$8(store, function (el) { 47989 el && (delEls.push(el), el.__tmWillDelete = 1); 47990 }); 47991 }); 47992 return willDeleteEls; 47993 } 47994 47995 function renderFinally() { 47996 each$8(willDeleteEls, function (els) { 47997 each$8(els, function (el) { 47998 el.parent && el.parent.remove(el); 47999 }); 48000 }); 48001 each$8(willInvisibleEls, function (el) { 48002 el.invisible = true; 48003 // Setting invisible is for optimizing, so no need to set dirty, 48004 // just mark as invisible. 48005 el.dirty(); 48006 }); 48007 } 48008 }, 48009 48010 /** 48011 * @private 48012 */ 48013 _doAnimation: function (containerGroup, renderResult, seriesModel, reRoot) { 48014 if (!seriesModel.get('animation')) { 48015 return; 48016 } 48017 48018 var duration = seriesModel.get('animationDurationUpdate'); 48019 var easing = seriesModel.get('animationEasing'); 48020 var animationWrap = createWrap(); 48021 48022 // Make delete animations. 48023 each$8(renderResult.willDeleteEls, function (store, storageName) { 48024 each$8(store, function (el, rawIndex) { 48025 if (el.invisible) { 48026 return; 48027 } 48028 48029 var parent = el.parent; // Always has parent, and parent is nodeGroup. 48030 var target; 48031 48032 if (reRoot && reRoot.direction === 'drillDown') { 48033 target = parent === reRoot.rootNodeGroup 48034 // This is the content element of view root. 48035 // Only `content` will enter this branch, because 48036 // `background` and `nodeGroup` will not be deleted. 48037 ? { 48038 shape: { 48039 x: 0, 48040 y: 0, 48041 width: parent.__tmNodeWidth, 48042 height: parent.__tmNodeHeight 48043 }, 48044 style: { 48045 opacity: 0 48046 } 48047 } 48048 // Others. 48049 : {style: {opacity: 0}}; 48050 } 48051 else { 48052 var targetX = 0; 48053 var targetY = 0; 48054 48055 if (!parent.__tmWillDelete) { 48056 // Let node animate to right-bottom corner, cooperating with fadeout, 48057 // which is appropriate for user understanding. 48058 // Divided by 2 for reRoot rolling up effect. 48059 targetX = parent.__tmNodeWidth / 2; 48060 targetY = parent.__tmNodeHeight / 2; 48061 } 48062 48063 target = storageName === 'nodeGroup' 48064 ? {position: [targetX, targetY], style: {opacity: 0}} 48065 : { 48066 shape: {x: targetX, y: targetY, width: 0, height: 0}, 48067 style: {opacity: 0} 48068 }; 48069 } 48070 48071 target && animationWrap.add(el, target, duration, easing); 48072 }); 48073 }); 48074 48075 // Make other animations 48076 each$8(this._storage, function (store, storageName) { 48077 each$8(store, function (el, rawIndex) { 48078 var last = renderResult.lastsForAnimation[storageName][rawIndex]; 48079 var target = {}; 48080 48081 if (!last) { 48082 return; 48083 } 48084 48085 if (storageName === 'nodeGroup') { 48086 if (last.old) { 48087 target.position = el.position.slice(); 48088 el.attr('position', last.old); 48089 } 48090 } 48091 else { 48092 if (last.old) { 48093 target.shape = extend({}, el.shape); 48094 el.setShape(last.old); 48095 } 48096 48097 if (last.fadein) { 48098 el.setStyle('opacity', 0); 48099 target.style = {opacity: 1}; 48100 } 48101 // When animation is stopped for succedent animation starting, 48102 // el.style.opacity might not be 1 48103 else if (el.style.opacity !== 1) { 48104 target.style = {opacity: 1}; 48105 } 48106 } 48107 48108 animationWrap.add(el, target, duration, easing); 48109 }); 48110 }, this); 48111 48112 this._state = 'animating'; 48113 48114 animationWrap 48115 .done(bind$1(function () { 48116 this._state = 'ready'; 48117 renderResult.renderFinally(); 48118 }, this)) 48119 .start(); 48120 }, 48121 48122 /** 48123 * @private 48124 */ 48125 _resetController: function (api) { 48126 var controller = this._controller; 48127 48128 // Init controller. 48129 if (!controller) { 48130 controller = this._controller = new RoamController(api.getZr()); 48131 controller.enable(this.seriesModel.get('roam')); 48132 controller.on('pan', bind$1(this._onPan, this)); 48133 controller.on('zoom', bind$1(this._onZoom, this)); 48134 } 48135
48136 var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight()); 48137 controller.setPointerChecker(function (e, x, y) { 48138 return rect.contain(x, y); 48139 }); 48140 }, 48141 48142 /** 48143 * @private 48144 */ 48145 _clearController: function () { 48146 var controller = this._controller; 48147 if (controller) { 48148 controller.dispose(); 48149 controller = null; 48150 } 48151 }, 48152 48153 /** 48154 * @private 48155 */ 48156 _onPan: function (dx, dy) { 48157 if (this._state !== 'animating' 48158 && (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) 48159 ) { 48160 // These param must not be cached. 48161 var root = this.seriesModel.getData().tree.root; 48162 48163 if (!root) { 48164 return; 48165 } 48166 48167 var rootLayout = root.getLayout(); 48168 48169 if (!rootLayout) { 48170 return; 48171 } 48172 48173 this.api.dispatchAction({ 48174 type: 'treemapMove', 48175 from: this.uid, 48176 seriesId: this.seriesModel.id, 48177 rootRect: { 48178 x: rootLayout.x + dx, y: rootLayout.y + dy, 48179 width: rootLayout.width, height: rootLayout.height 48180 } 48181 }); 48182 } 48183 }, 48184 48185 /** 48186 * @private 48187 */ 48188 _onZoom: function (scale, mouseX, mouseY) { 48189 if (this._state !== 'animating') { 48190 // These param must not be cached. 48191 var root = this.seriesModel.getData().tree.root; 48192 48193 if (!root) { 48194 return; 48195 } 48196 48197 var rootLayout = root.getLayout(); 48198 48199 if (!rootLayout) { 48200 return; 48201 } 48202 48203 var rect = new BoundingRect( 48204 rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height 48205 ); 48206 var layoutInfo = this.seriesModel.layoutInfo; 48207 48208 // Transform mouse coord from global to containerGroup. 48209 mouseX -= layoutInfo.x; 48210 mouseY -= layoutInfo.y; 48211 48212 // Scale root bounding rect. 48213 var m = create$1(); 48214 translate(m, m, [-mouseX, -mouseY]); 48215 scale$1(m, m, [scale, scale]); 48216 translate(m, m, [mouseX, mouseY]); 48217 48218 rect.applyTransform(m); 48219 48220 this.api.dispatchAction({ 48221 type: 'treemapRender', 48222 from: this.uid, 48223 seriesId: this.seriesModel.id, 48224 rootRect: { 48225 x: rect.x, y: rect.y, 48226 width: rect.width, height: rect.height 48227 } 48228 }); 48229 } 48230 }, 48231 48232 /** 48233 * @private 48234 */ 48235 _initEvents: function (containerGroup) { 48236 containerGroup.on('click', function (e) { 48237 if (this._state !== 'ready') { 48238 return; 48239 } 48240 48241 var nodeClick = this.seriesModel.get('nodeClick', true); 48242 48243 if (!nodeClick) { 48244 return; 48245 } 48246 48247 var targetInfo = this.findTarget(e.offsetX, e.offsetY); 48248 48249 if (!targetInfo) { 48250 return; 48251 } 48252 48253 var node = targetInfo.node; 48254 if (node.getLayout().isLeafRoot) { 48255 this._rootToNode(targetInfo); 48256 } 48257 else { 48258 if (nodeClick === 'zoomToNode') { 48259 this._zoomToNode(targetInfo); 48260 } 48261 else if (nodeClick === 'link') { 48262 var itemModel = node.hostTree.data.getItemModel(node.dataIndex); 48263 var link = itemModel.get('link', true); 48264 var linkTarget = itemModel.get('target', true) || 'blank'; 48265 link && window.open(link, linkTarget); 48266 } 48267 } 48268 48269 }, this); 48270 }, 48271 48272 /** 48273 * @private 48274 */ 48275 _renderBreadcrumb: function (seriesModel, api, targetInfo) { 48276 if (!targetInfo) { 48277 targetInfo = seriesModel.get('leafDepth', true) != null 48278 ? {node: seriesModel.getViewRoot()} 48279 // FIXME 48280 // better way? 48281 // Find breadcrumb tail on center of containerGroup. 48282 : this.findTarget(api.getWidth() / 2, api.getHeight() / 2); 48283 48284 if (!targetInfo) { 48285 targetInfo = {node: seriesModel.getData().tree.root}; 48286 } 48287 } 48288 48289 (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group))) 48290 .render(seriesModel, api, targetInfo.node, bind$1(onSelect, this)); 48291 48292 function onSelect(node) { 48293 if (this._state !== 'animating') { 48294 aboveViewRoot(seriesModel.getViewRoot(), node) 48295 ? this._rootToNode({node: node}) 48296 : this._zoomToNode({node: node}); 48297 } 48298 } 48299 }, 48300 48301 /** 48302 * @override 48303 */ 48304 remove: function () { 48305 this._clearController(); 48306 this._containerGroup && this._containerGroup.removeAll(); 48307 this._storage = createStorage(); 48308 this._state = 'ready'; 48309 this._breadcrumb && this._breadcrumb.remove(); 48310 }, 48311 48312 dispose: function () { 48313 this._clearController(); 48314 }, 48315 48316 /** 48317 * @private 48318 */ 48319 _zoomToNode: function (targetInfo) { 48320 this.api.dispatchAction({ 48321 type: 'treemapZoomToNode', 48322 from: this.uid, 48323 seriesId: this.seriesModel.id, 48324 targetNode: targetInfo.node 48325 }); 48326 }, 48327 48328 /** 48329 * @private 48330 */ 48331 _rootToNode: function (targetInfo) { 48332 this.api.dispatchAction({ 48333 type: 'treemapRootToNode', 48334 from: this.uid, 48335 seriesId: this.seriesModel.id, 48336 targetNode: targetInfo.node 48337 }); 48338 }, 48339 48340 /** 48341 * @public 48342 * @param {number} x Global coord x. 48343 * @param {number} y Global coord y. 48344 * @return {Object} info If not found, return undefined; 48345 * @return {number} info.node Target node. 48346 * @return {number} info.offsetX x refer to target node. 48347 * @return {number} info.offsetY y refer to target node. 48348 */ 48349 findTarget: function (x, y) { 48350 var targetInfo; 48351 var viewRoot = this.seriesModel.getViewRoot(); 48352 48353 viewRoot.eachNode({attr: 'viewChildren', order: 'preorder'}, function (node) { 48354 var bgEl = this._storage.background[node.getRawIndex()];
48355 // If invisible, there might be no element. 48356 if (bgEl) { 48357 var point = bgEl.transformCoordToLocal(x, y); 48358 var shape = bgEl.shape; 48359 48360 // For performance consideration, dont use 'getBoundingRect'. 48361 if (shape.x <= point[0] 48362 && point[0] <= shape.x + shape.width 48363 && shape.y <= point[1] 48364 && point[1] <= shape.y + shape.height 48365 ) { 48366 targetInfo = {node: node, offsetX: point[0], offsetY: point[1]}; 48367 } 48368 else { 48369 return false; // Suppress visit subtree. 48370 } 48371 } 48372 }, this); 48373 48374 return targetInfo; 48375 } 48376 48377 }); 48378 48379 /** 48380 * @inner 48381 */ 48382 function createStorage() { 48383 return {nodeGroup: [], background: [], content: []}; 48384 } 48385 48386 /** 48387 * @inner 48388 * @return Return undefined means do not travel further. 48389 */ 48390 function renderNode( 48391 seriesModel, thisStorage, oldStorage, reRoot, 48392 lastsForAnimation, willInvisibleEls, 48393 thisNode, oldNode, parentGroup, depth 48394 ) { 48395 // Whether under viewRoot. 48396 if (!thisNode) { 48397 // Deleting nodes will be performed finally. This method just find 48398 // element from old storage, or create new element, set them to new 48399 // storage, and set styles. 48400 return; 48401 } 48402 48403 // ------------------------------------------------------------------- 48404 // Start of closure variables available in "Procedures in renderNode". 48405 48406 var thisLayout = thisNode.getLayout(); 48407 48408 if (!thisLayout || !thisLayout.isInView) { 48409 return; 48410 } 48411 48412 var thisWidth = thisLayout.width; 48413 var thisHeight = thisLayout.height; 48414 var borderWidth = thisLayout.borderWidth; 48415 var thisInvisible = thisLayout.invisible; 48416 48417 var thisRawIndex = thisNode.getRawIndex(); 48418 var oldRawIndex = oldNode && oldNode.getRawIndex(); 48419 48420 var thisViewChildren = thisNode.viewChildren; 48421 var upperHeight = thisLayout.upperHeight; 48422 var isParent = thisViewChildren && thisViewChildren.length; 48423 var itemStyleNormalModel = thisNode.getModel('itemStyle'); 48424 var itemStyleEmphasisModel = thisNode.getModel('emphasis.itemStyle'); 48425 48426 // End of closure ariables available in "Procedures in renderNode". 48427 // ----------------------------------------------------------------- 48428 48429 // Node group 48430 var group = giveGraphic('nodeGroup', Group$2); 48431 48432 if (!group) { 48433 return; 48434 } 48435 48436 parentGroup.add(group); 48437 // x,y are not set when el is above view root. 48438 group.attr('position', [thisLayout.x || 0, thisLayout.y || 0]); 48439 group.__tmNodeWidth = thisWidth; 48440 group.__tmNodeHeight = thisHeight; 48441 48442 if (thisLayout.isAboveViewRoot) { 48443 return group; 48444 } 48445 48446 // Background 48447 var bg = giveGraphic('background', Rect$1, depth, Z_BG); 48448 bg && renderBackground(group, bg, isParent && thisLayout.upperHeight); 48449 48450 // No children, render content. 48451 if (!isParent) { 48452 var content = giveGraphic('content', Rect$1, depth, Z_CONTENT); 48453 content && renderContent(group, content); 48454 } 48455 48456 return group; 48457 48458 // ---------------------------- 48459 // | Procedures in renderNode | 48460 // ---------------------------- 48461 48462 function renderBackground(group, bg, useUpperLabel) { 48463 // For tooltip. 48464 bg.dataIndex = thisNode.dataIndex; 48465 bg.seriesIndex = seriesModel.seriesIndex; 48466 48467 bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight}); 48468 var visualBorderColor = thisNode.getVisual('borderColor', true); 48469 var emphasisBorderColor = itemStyleEmphasisModel.get('borderColor'); 48470 48471 updateStyle(bg, function () { 48472 var normalStyle = getItemStyleNormal(itemStyleNormalModel); 48473 normalStyle.fill = visualBorderColor; 48474 var emphasisStyle = getItemStyleEmphasis(itemStyleEmphasisModel); 48475 emphasisStyle.fill = emphasisBorderColor; 48476 48477 if (useUpperLabel) { 48478 var upperLabelWidth = thisWidth - 2 * borderWidth; 48479 48480 prepareText( 48481 normalStyle, emphasisStyle, visualBorderColor, upperLabelWidth, upperHeight, 48482 {x: borderWidth, y: 0, width: upperLabelWidth, height: upperHeight} 48483 ); 48484 } 48485 // For old bg. 48486 else { 48487 normalStyle.text = emphasisStyle.text = null; 48488 } 48489 48490 bg.setStyle(normalStyle); 48491 setHoverStyle(bg, emphasisStyle); 48492 }); 48493 48494 group.add(bg); 48495 } 48496 48497 function renderContent(group, content) { 48498 // For tooltip. 48499 content.dataIndex = thisNode.dataIndex; 48500 content.seriesIndex = seriesModel.seriesIndex; 48501 48502 var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0); 48503 var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0); 48504 48505 content.culling = true; 48506 content.setShape({ 48507 x: borderWidth, 48508 y: borderWidth, 48509 width: contentWidth, 48510 height: contentHeight 48511 }); 48512 48513 var visualColor = thisNode.getVisual('color', true); 48514 updateStyle(content, function () { 48515 var normalStyle = getItemStyleNormal(itemStyleNormalModel); 48516 normalStyle.fill = visualColor; 48517 var emphasisStyle = getItemStyleEmphasis(itemStyleEmphasisModel); 48518 48519 prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight); 48520 48521 content.setStyle(normalStyle); 48522 setHoverStyle(content, emphasisStyle); 48523 }); 48524 48525 group.add(content); 48526 } 48527 48528 function updateStyle(element, cb) { 48529 if (!thisInvisible) { 48530 // If invisible, do not set visual, otherwise the element will 48531 // change immediately before animation. We think it is OK to 48532 // remain its origin color when moving out of the view window. 48533 cb(); 48534 48535 if (!element.__tmWillVisible) { 48536 element.invisible = false; 48537 } 48538 } 48539 else { 48540 // Delay invisible setting utill animation finished, 48541 // avoid element vanish suddenly before animation. 48542 !element.invisible && willInvisibleEls.push(element); 48543 } 48544 } 48545 48546 function prepareText(normalStyle, emphasisStyle, visualColor, width, height, upperLabelRect) { 48547 var nodeModel = thisNode.getModel(); 48548 var text = retrieve( 48549 seriesModel.getFormattedLabel( 48550 thisNode.dataIndex, 'normal', null, null, upperLabelRect ? 'upperLabel' : 'label' 48551 ), 48552 nodeModel.get('name') 48553 ); 48554 if (!upperLabelRect && thisLayout.isLeafRoot) { 48555 var iconChar = seriesModel.get('drillDownIcon', true); 48556 text = iconChar ? iconChar + ' ' + text : text; 48557 } 48558
48559 var normalLabelModel = nodeModel.getModel( 48560 upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL 48561 ); 48562 var emphasisLabelModel = nodeModel.getModel( 48563 upperLabelRect ? PATH_UPPERLABEL_EMPHASIS : PATH_LABEL_EMPHASIS 48564 ); 48565 48566 var isShow = normalLabelModel.getShallow('show'); 48567 48568 setLabelStyle( 48569 normalStyle, emphasisStyle, normalLabelModel, emphasisLabelModel, 48570 { 48571 defaultText: isShow ? text : null, 48572 autoColor: visualColor, 48573 isRectText: true 48574 } 48575 ); 48576 48577 upperLabelRect && (normalStyle.textRect = clone(upperLabelRect)); 48578 48579 normalStyle.truncate = (isShow && normalLabelModel.get('ellipsis')) 48580 ? { 48581 outerWidth: width, 48582 outerHeight: height, 48583 minChar: 2 48584 } 48585 : null; 48586 } 48587 48588 function giveGraphic(storageName, Ctor, depth, z) { 48589 var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex]; 48590 var lasts = lastsForAnimation[storageName]; 48591 48592 if (element) { 48593 // Remove from oldStorage 48594 oldStorage[storageName][oldRawIndex] = null; 48595 prepareAnimationWhenHasOld(lasts, element, storageName); 48596 } 48597 // If invisible and no old element, do not create new element (for optimizing). 48598 else if (!thisInvisible) { 48599 element = new Ctor({z: calculateZ(depth, z)}); 48600 element.__tmDepth = depth; 48601 element.__tmStorageName = storageName; 48602 prepareAnimationWhenNoOld(lasts, element, storageName); 48603 } 48604 48605 // Set to thisStorage 48606 return (thisStorage[storageName][thisRawIndex] = element); 48607 } 48608 48609 function prepareAnimationWhenHasOld(lasts, element, storageName) { 48610 var lastCfg = lasts[thisRawIndex] = {}; 48611 lastCfg.old = storageName === 'nodeGroup' 48612 ? element.position.slice() 48613 : extend({}, element.shape); 48614 } 48615 48616 // If a element is new, we need to find the animation start point carefully, 48617 // otherwise it will looks strange when 'zoomToNode'. 48618 function prepareAnimationWhenNoOld(lasts, element, storageName) { 48619 var lastCfg = lasts[thisRawIndex] = {}; 48620 var parentNode = thisNode.parentNode; 48621 48622 if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) { 48623 var parentOldX = 0; 48624 var parentOldY = 0; 48625 48626 // New nodes appear from right-bottom corner in 'zoomToNode' animation. 48627 // For convenience, get old bounding rect from background. 48628 var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()]; 48629 if (!reRoot && parentOldBg && parentOldBg.old) { 48630 parentOldX = parentOldBg.old.width; 48631 parentOldY = parentOldBg.old.height; 48632 } 48633 48634 // When no parent old shape found, its parent is new too, 48635 // so we can just use {x:0, y:0}. 48636 lastCfg.old = storageName === 'nodeGroup' 48637 ? [0, parentOldY] 48638 : {x: parentOldX, y: parentOldY, width: 0, height: 0}; 48639 } 48640 48641 // Fade in, user can be aware that these nodes are new. 48642 lastCfg.fadein = storageName !== 'nodeGroup'; 48643 } 48644 } 48645 48646// We can not set all backgroud with the same z, Because the behaviour of 48647// drill down and roll up differ background creation sequence from tree 48648// hierarchy sequence, which cause that lowser background element overlap 48649// upper ones. So we calculate z based on depth. 48650// Moreover, we try to shrink down z interval to [0, 1] to avoid that 48651// treemap with large z overlaps other components. 48652 function calculateZ(depth, zInLevel) { 48653 var zb = depth * Z_BASE + zInLevel; 48654 return (zb - 1) / zb; 48655 } 48656 48657 /* 48658* Licensed to the Apache Software Foundation (ASF) under one 48659* or more contributor license agreements. See the NOTICE file 48660* distributed with this work for additional information 48661* regarding copyright ownership. The ASF licenses this file 48662* to you under the Apache License, Version 2.0 (the 48663* "License");
48663 you may not use this file except in compliance 48664* with the License. You may obtain a copy of the License at 48665* 48666* http://www.apache.org/licenses/LICENSE-2.0 48667* 48668* Unless required by applicable law or agreed to in writing, 48669* software distributed under the License is distributed on an 48670* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 48671* KIND, either express or implied. See the License for the 48672* specific language governing permissions and limitations 48673* under the License. 48674*/ 48675 48676 /** 48677 * @file Treemap action 48678 */ 48679 48680 var noop$1 = function () {}; 48681 48682 var actionTypes = [ 48683 'treemapZoomToNode', 48684 'treemapRender', 48685 'treemapMove' 48686 ]; 48687 48688 for (var i$2 = 0; i$2 < actionTypes.length; i$2++) { 48689 registerAction({type: actionTypes[i$2], update: 'updateView'}, noop$1); 48690 } 48691 48692 registerAction( 48693 {type: 'treemapRootToNode', update: 'updateView'}, 48694 function (payload, ecModel) { 48695 48696 ecModel.eachComponent( 48697 {mainType: 'series', subType: 'treemap', query: payload}, 48698 handleRootToNode 48699 ); 48700 48701 function handleRootToNode(model, index) { 48702 var types = ['treemapZoomToNode', 'treemapRootToNode']; 48703 var targetInfo = retrieveTargetInfo(payload, types, model); 48704 48705 if (targetInfo) { 48706 var originViewRoot = model.getViewRoot(); 48707 if (originViewRoot) { 48708 payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) 48709 ? 'rollUp' : 'drillDown'; 48710 } 48711 model.resetViewRoot(targetInfo.node); 48712 } 48713 } 48714 } 48715 ); 48716 48717 /* 48718* Licensed to the Apache Software Foundation (ASF) under one 48719* or more contributor license agreements. See the NOTICE file 48720* distributed with this work for additional information 48721* regarding copyright ownership. The ASF licenses this file 48722* to you under the Apache License, Version 2.0 (the 48723* "License"); you may not use this file except in compliance 48724* with the License. You may obtain a copy of the License at 48725* 48726* http://www.apache.org/licenses/LICENSE-2.0 48727* 48728* Unless required by applicable law or agreed to in writing, 48729* software distributed under the License is distributed on an 48730* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 48731* KIND, either express or implied. See the License for the 48732* specific language governing permissions and limitations 48733* under the License. 48734*/ 48735 48736 var each$9 = each$1; 48737 var isObject$5 = isObject$1; 48738 48739 var CATEGORY_DEFAULT_VISUAL_INDEX = -1; 48740 48741 /** 48742 * @param {Object} option 48743 * @param {string} [option.type] See visualHandlers. 48744 * @param {string} [option.mappingMethod] 'linear' or 'piecewise' or 'category' or 'fixed' 48745 * @param {Array.<number>=} [option.dataExtent] [minExtent, maxExtent], 48746 * required when mappingMethod is 'linear' 48747 * @param {Array.<Object>=} [option.pieceList] [ 48748 * {value: someValue}, 48749 * {interval: [min1, max1], visual: {...}}, 48750 * {interval: [min2, max2]} 48751 * ], 48752 * required when mappingMethod is 'piecewise'. 48753 * Visual for only each piece can be specified. 48754 * @param {Array.<string|Object>=} [option.categories] ['cate1', 'cate2'] 48755 * required when mappingMethod is 'category'. 48756 * If no option.categories, categories is set 48757 * as [0, 1, 2, ...]. 48758 * @param {boolean} [option.loop=false] Whether loop mapping when mappingMethod is 'category'. 48759 * @param {(Array|Object|*)} [option.visual] Visual data. 48760 * when mappingMethod is 'category', 48761 * visual data can be array or object 48762 * (like: {cate1: '#222', none: '#fff'}) 48763 * or primary types (which represents 48764 * defualt category visual), otherwise visual 48765 * can be array or primary (which will be 48766 * normalized to array). 48767 * 48768 */ 48769 var VisualMapping = function (option) { 48770 var mappingMethod = option.mappingMethod; 48771 var visualType = option.type; 48772 48773 /** 48774 * @readOnly 48775 * @type {Object} 48776 */ 48777 var thisOption = this.option = clone(option); 48778 48779 /** 48780 * @readOnly 48781 * @type {string} 48782 */ 48783 this.type = visualType; 48784 48785 /** 48786 * @readOnly 48787 * @type {string} 48788 */ 48789 this.mappingMethod = mappingMethod; 48790 48791 /** 48792 * @private 48793 * @type {Function} 48794 */ 48795 this._normalizeData = normalizers[mappingMethod]; 48796 48797 var visualHandler = visualHandlers[visualType]; 48798 48799 /** 48800 * @public 48801 * @type {Function} 48802 */ 48803 this.applyVisual = visualHandler.applyVisual; 48804 48805 /** 48806 * @public 48807 * @type {Function} 48808 */ 48809 this.getColorMapper = visualHandler.getColorMapper; 48810 48811 /** 48812 * @private 48813 * @type {Function} 48814 */ 48815 this._doMap = visualHandler._doMap[mappingMethod]; 48816 48817 if (mappingMethod === 'piecewise') { 48818 normalizeVisualRange(thisOption); 48819 preprocessForPiecewise(thisOption); 48820 } 48821 else if (mappingMethod === 'category') { 48822 thisOption.categories 48823 ? preprocessForSpecifiedCategory(thisOption) 48824 // categories is ordinal when thisOption.categories not specified, 48825 // which need no more preprocess except normalize visual. 48826 : normalizeVisualRange(thisOption, true); 48827 } 48828 else { // mappingMethod === 'linear' or 'fixed'
48829 assert$1(mappingMethod !== 'linear' || thisOption.dataExtent); 48830 normalizeVisualRange(thisOption); 48831 } 48832 }; 48833 48834 VisualMapping.prototype = { 48835 48836 constructor: VisualMapping, 48837 48838 mapValueToVisual: function (value) { 48839 var normalized = this._normalizeData(value); 48840 return this._doMap(normalized, value); 48841 }, 48842 48843 getNormalizer: function () { 48844 return bind(this._normalizeData, this); 48845 } 48846 }; 48847 48848 var visualHandlers = VisualMapping.visualHandlers = { 48849 48850 color: { 48851 48852 applyVisual: makeApplyVisual('color'), 48853 48854 /** 48855 * Create a mapper function 48856 * @return {Function} 48857 */ 48858 getColorMapper: function () { 48859 var thisOption = this.option; 48860 48861 return bind( 48862 thisOption.mappingMethod === 'category' 48863 ? function (value, isNormalized) { 48864 !isNormalized && (value = this._normalizeData(value)); 48865 return doMapCategory.call(this, value); 48866 } 48867 : function (value, isNormalized, out) { 48868 // If output rgb array 48869 // which will be much faster and useful in pixel manipulation 48870 var returnRGBArray = !!out; 48871 !isNormalized && (value = this._normalizeData(value)); 48872 out = fastLerp(value, thisOption.parsedVisual, out); 48873 return returnRGBArray ? out : stringify(out, 'rgba'); 48874 }, 48875 this 48876 ); 48877 }, 48878 48879 _doMap: { 48880 linear: function (normalized) { 48881 return stringify( 48882 fastLerp(normalized, this.option.parsedVisual), 48883 'rgba' 48884 ); 48885 }, 48886 category: doMapCategory, 48887 piecewise: function (normalized, value) { 48888 var result = getSpecifiedVisual.call(this, value); 48889 if (result == null) { 48890 result = stringify( 48891 fastLerp(normalized, this.option.parsedVisual), 48892 'rgba' 48893 ); 48894 } 48895 return result; 48896 }, 48897 fixed: doMapFixed 48898 } 48899 }, 48900 48901 colorHue: makePartialColorVisualHandler(function (color, value) { 48902 return modifyHSL(color, value); 48903 }), 48904 48905 colorSaturation: makePartialColorVisualHandler(function (color, value) { 48906 return modifyHSL(color, null, value); 48907 }), 48908 48909 colorLightness: makePartialColorVisualHandler(function (color, value) { 48910 return modifyHSL(color, null, null, value); 48911 }), 48912 48913 colorAlpha: makePartialColorVisualHandler(function (color, value) { 48914 return modifyAlpha(color, value); 48915 }), 48916 48917 opacity: { 48918 applyVisual: makeApplyVisual('opacity'), 48919 _doMap: makeDoMap([0, 1]) 48920 }, 48921 48922 liftZ: { 48923 applyVisual: makeApplyVisual('liftZ'), 48924 _doMap: { 48925 linear: doMapFixed, 48926 category: doMapFixed, 48927 piecewise: doMapFixed, 48928 fixed: doMapFixed 48929 } 48930 }, 48931 48932 symbol: { 48933 applyVisual: function (value, getter, setter) { 48934 var symbolCfg = this.mapValueToVisual(value); 48935 if (isString(symbolCfg)) { 48936 setter('symbol', symbolCfg); 48937 } 48938 else if (isObject$5(symbolCfg)) { 48939 for (var name in symbolCfg) { 48940 if (symbolCfg.hasOwnProperty(name)) { 48941 setter(name, symbolCfg[name]); 48942 } 48943 } 48944 } 48945 }, 48946 _doMap: { 48947 linear: doMapToArray, 48948 category: doMapCategory, 48949 piecewise: function (normalized, value) { 48950 var result = getSpecifiedVisual.call(this, value); 48951 if (result == null) { 48952 result = doMapToArray.call(this, normalized); 48953 } 48954 return result; 48955 },
vendor: 4,455 bytes, lines 48956-49093
48956 fixed: doMapFixed 48957 } 48958 }, 48959 48960 symbolSize: { 48961 applyVisual: makeApplyVisual('symbolSize'), 48962 _doMap: makeDoMap([0, 1]) 48963 } 48964 }; 48965 48966 48967 function preprocessForPiecewise(thisOption) { 48968 var pieceList = thisOption.pieceList; 48969 thisOption.hasSpecialVisual = false; 48970 48971 each$1(pieceList, function (piece, index) { 48972 piece.originIndex = index; 48973 // piece.visual is "result visual value" but not 48974 // a visual range, so it does not need to be normalized. 48975 if (piece.visual != null) { 48976 thisOption.hasSpecialVisual = true; 48977 } 48978 }); 48979 } 48980 48981 function preprocessForSpecifiedCategory(thisOption) { 48982 // Hash categories. 48983 var categories = thisOption.categories; 48984 var visual = thisOption.visual; 48985 48986 var categoryMap = thisOption.categoryMap = {}; 48987 each$9(categories, function (cate, index) { 48988 categoryMap[cate] = index; 48989 }); 48990 48991 // Process visual map input. 48992 if (!isArray(visual)) { 48993 var visualArr = []; 48994 48995 if (isObject$1(visual)) { 48996 each$9(visual, function (v, cate) { 48997 var index = categoryMap[cate]; 48998 visualArr[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v; 48999 }); 49000 } 49001 else { // Is primary type, represents default visual. 49002 visualArr[CATEGORY_DEFAULT_VISUAL_INDEX] = visual; 49003 } 49004 49005 visual = setVisualToOption(thisOption, visualArr); 49006 } 49007 49008 // Remove categories that has no visual, 49009 // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX. 49010 for (var i = categories.length - 1; i >= 0; i--) { 49011 if (visual[i] == null) { 49012 delete categoryMap[categories[i]]; 49013 categories.pop(); 49014 } 49015 } 49016 } 49017 49018 function normalizeVisualRange(thisOption, isCategory) { 49019 var visual = thisOption.visual; 49020 var visualArr = []; 49021 49022 if (isObject$1(visual)) { 49023 each$9(visual, function (v) { 49024 visualArr.push(v); 49025 }); 49026 } 49027 else if (visual != null) { 49028 visualArr.push(visual); 49029 } 49030 49031 var doNotNeedPair = {color: 1, symbol: 1}; 49032 49033 if (!isCategory 49034 && visualArr.length === 1 49035 && !doNotNeedPair.hasOwnProperty(thisOption.type) 49036 ) { 49037 // Do not care visualArr.length === 0, which is illegal. 49038 visualArr[1] = visualArr[0]; 49039 } 49040 49041 setVisualToOption(thisOption, visualArr); 49042 } 49043 49044 function makePartialColorVisualHandler(applyValue) { 49045 return { 49046 applyVisual: function (value, getter, setter) { 49047 value = this.mapValueToVisual(value); 49048 // Must not be array value 49049 setter('color', applyValue(getter('color'), value)); 49050 }, 49051 _doMap: makeDoMap([0, 1]) 49052 }; 49053 } 49054 49055 function doMapToArray(normalized) { 49056 var visual = this.option.visual; 49057 return visual[ 49058 Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true)) 49059 ] || {}; 49060 } 49061 49062 function makeApplyVisual(visualType) { 49063 return function (value, getter, setter) { 49064 setter(visualType, this.mapValueToVisual(value)); 49065 }; 49066 } 49067 49068 function doMapCategory(normalized) { 49069 var visual = this.option.visual; 49070 return visual[ 49071 (this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX) 49072 ? normalized % visual.length 49073 : normalized 49074 ]; 49075 } 49076 49077 function doMapFixed() { 49078 return this.option.visual[0]; 49079 } 49080 49081 function makeDoMap(sourceExtent) { 49082 return { 49083 linear: function (normalized) { 49084 return linearMap(normalized, sourceExtent, this.option.visual, true); 49085 }, 49086 category: doMapCategory, 49087 piecewise: function (normalized, value) { 49088 var result = getSpecifiedVisual.call(this, value); 49089 if (result == null) { 49090 result = linearMap(normalized, sourceExtent, this.option.visual, true); 49091 } 49092 return result; 49093 },
49094 fixed: doMapFixed 49095 }; 49096 } 49097 49098 function getSpecifiedVisual(value) { 49099 var thisOption = this.option; 49100 var pieceList = thisOption.pieceList; 49101 if (thisOption.hasSpecialVisual) { 49102 var pieceIndex = VisualMapping.findPieceIndex(value, pieceList); 49103 var piece = pieceList[pieceIndex]; 49104 if (piece && piece.visual) { 49105 return piece.visual[this.type]; 49106 } 49107 } 49108 } 49109 49110 function setVisualToOption(thisOption, visualArr) { 49111 thisOption.visual = visualArr; 49112 if (thisOption.type === 'color') { 49113 thisOption.parsedVisual = map(visualArr, function (item) { 49114 return parse(item); 49115 }); 49116 } 49117 return visualArr; 49118 } 49119 49120 49121 /** 49122 * Normalizers by mapping methods. 49123 */ 49124 var normalizers = { 49125 49126 linear: function (value) { 49127 return linearMap(value, this.option.dataExtent, [0, 1], true); 49128 }, 49129 49130 piecewise: function (value) { 49131 var pieceList = this.option.pieceList; 49132 var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true); 49133 if (pieceIndex != null) { 49134 return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true); 49135 } 49136 }, 49137 49138 category: function (value) { 49139 var index = this.option.categories 49140 ? this.option.categoryMap[value] 49141 : value; // ordinal 49142 return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index; 49143 }, 49144 49145 fixed: noop 49146 }; 49147 49148 49149 49150 /** 49151 * List available visual types. 49152 * 49153 * @public 49154 * @return {Array.<string>} 49155 */ 49156 VisualMapping.listVisualTypes = function () { 49157 var visualTypes = []; 49158 each$1(visualHandlers, function (handler, key) { 49159 visualTypes.push(key); 49160 }); 49161 return visualTypes; 49162 }; 49163 49164 /** 49165 * @public 49166 */ 49167 VisualMapping.addVisualHandler = function (name, handler) { 49168 visualHandlers[name] = handler; 49169 }; 49170 49171 /** 49172 * @public 49173 */ 49174 VisualMapping.isValidType = function (visualType) { 49175 return visualHandlers.hasOwnProperty(visualType); 49176 }; 49177 49178 /** 49179 * Convinent method. 49180 * Visual can be Object or Array or primary type. 49181 * 49182 * @public 49183 */ 49184 VisualMapping.eachVisual = function (visual, callback, context) { 49185 if (isObject$1(visual)) { 49186 each$1(visual, callback, context); 49187 } 49188 else { 49189 callback.call(context, visual); 49190 } 49191 }; 49192 49193 VisualMapping.mapVisual = function (visual, callback, context) { 49194 var isPrimary; 49195 var newVisual = isArray(visual) 49196 ? [] 49197 : isObject$1(visual) 49198 ? {} 49199 : (isPrimary = true, null); 49200 49201 VisualMapping.eachVisual(visual, function (v, key) { 49202 var newVal = callback.call(context, v, key); 49203 isPrimary ? (newVisual = newVal) : (newVisual[key] = newVal); 49204 }); 49205 return newVisual; 49206 }; 49207 49208 /** 49209 * @public 49210 * @param {Object} obj 49211 * @return {Object} new object containers visual values. 49212 * If no visuals, return null. 49213 */ 49214 VisualMapping.retrieveVisuals = function (obj) { 49215 var ret = {}; 49216 var hasVisual; 49217 49218 obj && each$9(visualHandlers, function (h, visualType) { 49219 if (obj.hasOwnProperty(visualType)) { 49220 ret[visualType] = obj[visualType]; 49221 hasVisual = true; 49222 } 49223 }); 49224 49225 return hasVisual ? ret : null; 49226 }; 49227 49228 /** 49229 * Give order to visual types, considering colorSaturation, colorAlpha depends on color. 49230 * 49231 * @public 49232 * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...} 49233 * IF Array, like: ['color', 'symbol', 'colorSaturation'] 49234 * @return {Array.<string>} Sorted visual types. 49235 */ 49236 VisualMapping.prepareVisualTypes = function (visualTypes) { 49237 if (isObject$5(visualTypes)) { 49238 var types = []; 49239 each$9(visualTypes, function (item, type) { 49240 types.push(type); 49241 }); 49242 visualTypes = types; 49243 } 49244 else if (isArray(visualTypes)) { 49245 visualTypes = visualTypes.slice(); 49246 } 49247 else { 49248 return []; 49249 } 49250 49251 visualTypes.sort(function (type1, type2) { 49252 // color should be front of colorSaturation, colorAlpha, ... 49253 // symbol and symbolSize do not matter. 49254 return (type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0) 49255 ? 1 : -1; 49256 }); 49257 49258 return visualTypes; 49259 }; 49260 49261 /** 49262 * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'.
49263 * Other visuals are only depends on themself. 49264 * 49265 * @public 49266 * @param {string} visualType1 49267 * @param {string} visualType2 49268 * @return {boolean} 49269 */ 49270 VisualMapping.dependsOn = function (visualType1, visualType2) { 49271 return visualType2 === 'color' 49272 ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) 49273 : visualType1 === visualType2; 49274 }; 49275 49276 /** 49277 * @param {number} value 49278 * @param {Array.<Object>} pieceList [{value: ..., interval: [min, max]}, ...] 49279 * Always from small to big. 49280 * @param {boolean} [findClosestWhenOutside=false] 49281 * @return {number} index 49282 */ 49283 VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) { 49284 var possibleI; 49285 var abs = Infinity; 49286 49287 // value has the higher priority. 49288 for (var i = 0, len = pieceList.length; i < len; i++) { 49289 var pieceValue = pieceList[i].value; 49290 if (pieceValue != null) { 49291 if (pieceValue === value 49292 // FIXME 49293 // It is supposed to compare value according to value type of dimension, 49294 // but currently value type can exactly be string or number. 49295 // Compromise for numeric-like string (like '12'), especially 49296 // in the case that visualMap.categories is ['22', '33']. 49297 || (typeof pieceValue === 'string' && pieceValue === value + '') 49298 ) { 49299 return i; 49300 } 49301 findClosestWhenOutside && updatePossible(pieceValue, i); 49302 } 49303 } 49304 49305 for (var i = 0, len = pieceList.length; i < len; i++) { 49306 var piece = pieceList[i]; 49307 var interval = piece.interval; 49308 var close = piece.close; 49309 49310 if (interval) { 49311 if (interval[0] === -Infinity) { 49312 if (littleThan(close[1], value, interval[1])) { 49313 return i; 49314 } 49315 } 49316 else if (interval[1] === Infinity) { 49317 if (littleThan(close[0], interval[0], value)) { 49318 return i; 49319 } 49320 } 49321 else if ( 49322 littleThan(close[0], interval[0], value) 49323 && littleThan(close[1], value, interval[1]) 49324 ) { 49325 return i; 49326 } 49327 findClosestWhenOutside && updatePossible(interval[0], i); 49328 findClosestWhenOutside && updatePossible(interval[1], i); 49329 } 49330 } 49331 49332 if (findClosestWhenOutside) { 49333 return value === Infinity 49334 ? pieceList.length - 1 49335 : value === -Infinity 49336 ? 0 49337 : possibleI; 49338 } 49339 49340 function updatePossible(val, index) { 49341 var newAbs = Math.abs(val - value); 49342 if (newAbs < abs) { 49343 abs = newAbs; 49344 possibleI = index; 49345 } 49346 } 49347 49348 }; 49349 49350 function littleThan(close, a, b) { 49351 return close ? a <= b : a < b; 49352 } 49353 49354 /* 49355* Licensed to the Apache Software Foundation (ASF) under one 49356* or more contributor license agreements. See the NOTICE file 49357* distributed with this work for additional information 49358* regarding copyright ownership. The ASF licenses this file 49359* to you under the Apache License, Version 2.0 (the 49360* "License"); you may not use this file except in compliance 49361* with the License. You may obtain a copy of the License at 49362* 49363* http://www.apache.org/licenses/LICENSE-2.0 49364* 49365* Unless required by applicable law or agreed to in writing, 49366* software distributed under the License is distributed on an 49367* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 49368* KIND, either express or implied. See the License for the 49369* specific language governing permissions and limitations 49370* under the License. 49371*/ 49372 49373 var isArray$2 = isArray; 49374 49375 var ITEM_STYLE_NORMAL = 'itemStyle'; 49376 49377 var treemapVisual = { 49378 seriesType: 'treemap', 49379 reset: function (seriesModel, ecModel, api, payload) { 49380 var tree = seriesModel.getData().tree; 49381 var root = tree.root; 49382 var seriesItemStyleModel = seriesModel.getModel(ITEM_STYLE_NORMAL); 49383 49384 if (root.isRemoved()) { 49385 return; 49386 } 49387 49388 var levelItemStyles = map(tree.levelModels, function (levelModel) { 49389 return levelModel ? levelModel.get(ITEM_STYLE_NORMAL) : null; 49390 }); 49391 49392 travelTree( 49393 root, // Visual should calculate from tree root but not view root. 49394 {}, 49395 levelItemStyles, 49396 seriesItemStyleModel, 49397 seriesModel.getViewRoot().getAncestors(), 49398 seriesModel 49399 ); 49400 } 49401 }; 49402 49403 function travelTree( 49404 node, designatedVisual, levelItemStyles, seriesItemStyleModel, 49405 viewRootAncestors, seriesModel 49406 ) {
49407 var nodeModel = node.getModel(); 49408 var nodeLayout = node.getLayout(); 49409 49410 // Optimize 49411 if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) { 49412 return; 49413 } 49414 49415 var nodeItemStyleModel = node.getModel(ITEM_STYLE_NORMAL); 49416 var levelItemStyle = levelItemStyles[node.depth]; 49417 var visuals = buildVisuals( 49418 nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel 49419 ); 49420 49421 // calculate border color 49422 var borderColor = nodeItemStyleModel.get('borderColor'); 49423 var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation'); 49424 var thisNodeColor; 49425 if (borderColorSaturation != null) { 49426 // For performance, do not always execute 'calculateColor'. 49427 thisNodeColor = calculateColor(visuals, node); 49428 borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor); 49429 } 49430 node.setVisual('borderColor', borderColor); 49431 49432 var viewChildren = node.viewChildren; 49433 if (!viewChildren || !viewChildren.length) { 49434 thisNodeColor = calculateColor(visuals, node); 49435 // Apply visual to this node. 49436 node.setVisual('color', thisNodeColor); 49437 } 49438 else { 49439 var mapping = buildVisualMapping( 49440 node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren 49441 ); 49442 49443 // Designate visual to children. 49444 each$1(viewChildren, function (child, index) { 49445 // If higher than viewRoot, only ancestors of viewRoot is needed to visit. 49446 if (child.depth >= viewRootAncestors.length 49447 || child === viewRootAncestors[child.depth] 49448 ) { 49449 var childVisual = mapVisual$1( 49450 nodeModel, visuals, child, index, mapping, seriesModel 49451 ); 49452 travelTree( 49453 child, childVisual, levelItemStyles, seriesItemStyleModel, 49454 viewRootAncestors, seriesModel 49455 ); 49456 } 49457 }); 49458 } 49459 } 49460 49461 function buildVisuals( 49462 nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel 49463 ) { 49464 var visuals = extend({}, designatedVisual); 49465 49466 each$1(['color', 'colorAlpha', 'colorSaturation'], function (visualName) { 49467 // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel 49468 var val = nodeItemStyleModel.get(visualName, true); // Ignore parent 49469 val == null && levelItemStyle && (val = levelItemStyle[visualName]); 49470 val == null && (val = designatedVisual[visualName]); 49471 val == null && (val = seriesItemStyleModel.get(visualName)); 49472 49473 val != null && (visuals[visualName] = val); 49474 }); 49475 49476 return visuals; 49477 } 49478 49479 function calculateColor(visuals) { 49480 var color = getValueVisualDefine(visuals, 'color'); 49481 49482 if (color) { 49483 var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha'); 49484 var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation'); 49485 if (colorSaturation) { 49486 color = modifyHSL(color, null, null, colorSaturation); 49487 } 49488 if (colorAlpha) { 49489 color = modifyAlpha(color, colorAlpha); 49490 } 49491 49492 return color; 49493 } 49494 } 49495 49496 function calculateBorderColor(borderColorSaturation, thisNodeColor) { 49497 return thisNodeColor != null 49498 ? modifyHSL(thisNodeColor, null, null, borderColorSaturation) 49499 : null; 49500 } 49501 49502 function getValueVisualDefine(visuals, name) { 49503 var value = visuals[name]; 49504 if (value != null && value !== 'none') { 49505 return value; 49506 } 49507 } 49508 49509 function buildVisualMapping( 49510 node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren 49511 ) { 49512 if (!viewChildren || !viewChildren.length) { 49513 return; 49514 } 49515 49516 var rangeVisual = getRangeVisual(nodeModel, 'color') 49517 || ( 49518 visuals.color != null 49519 && visuals.color !== 'none' 49520 && ( 49521 getRangeVisual(nodeModel, 'colorAlpha') 49522 || getRangeVisual(nodeModel, 'colorSaturation') 49523 ) 49524 ); 49525 49526 if (!rangeVisual) { 49527 return; 49528 } 49529 49530 var visualMin = nodeModel.get('visualMin');
vendor: 22,597 bytes, lines 49531-50143
49531 var visualMax = nodeModel.get('visualMax'); 49532 var dataExtent = nodeLayout.dataExtent.slice(); 49533 visualMin != null && visualMin < dataExtent[0] && (dataExtent[0] = visualMin); 49534 visualMax != null && visualMax > dataExtent[1] && (dataExtent[1] = visualMax); 49535 49536 var colorMappingBy = nodeModel.get('colorMappingBy'); 49537 var opt = { 49538 type: rangeVisual.name, 49539 dataExtent: dataExtent, 49540 visual: rangeVisual.range 49541 }; 49542 if (opt.type === 'color' 49543 && (colorMappingBy === 'index' || colorMappingBy === 'id') 49544 ) { 49545 opt.mappingMethod = 'category'; 49546 opt.loop = true; 49547 // categories is ordinal, so do not set opt.categories. 49548 } 49549 else { 49550 opt.mappingMethod = 'linear'; 49551 } 49552 49553 var mapping = new VisualMapping(opt); 49554 mapping.__drColorMappingBy = colorMappingBy; 49555 49556 return mapping; 49557 } 49558 49559// Notice: If we dont have the attribute 'colorRange', but only use 49560// attribute 'color' to represent both concepts of 'colorRange' and 'color', 49561// (It means 'colorRange' when 'color' is Array, means 'color' when not array), 49562// this problem will be encountered: 49563// If a level-1 node dont have children, and its siblings has children, 49564// and colorRange is set on level-1, then the node can not be colored. 49565// So we separate 'colorRange' and 'color' to different attributes. 49566 function getRangeVisual(nodeModel, name) { 49567 // 'colorRange', 'colorARange', 'colorSRange'. 49568 // If not exsits on this node, fetch from levels and series. 49569 var range = nodeModel.get(name); 49570 return (isArray$2(range) && range.length) ? {name: name, range: range} : null; 49571 } 49572 49573 function mapVisual$1(nodeModel, visuals, child, index, mapping, seriesModel) { 49574 var childVisuals = extend({}, visuals); 49575 49576 if (mapping) { 49577 var mappingType = mapping.type; 49578 var colorMappingBy = mappingType === 'color' && mapping.__drColorMappingBy; 49579 var value = 49580 colorMappingBy === 'index' 49581 ? index 49582 : colorMappingBy === 'id' 49583 ? seriesModel.mapIdToIndex(child.getId()) 49584 : child.getValue(nodeModel.get('visualDimension')); 49585 49586 childVisuals[mappingType] = mapping.mapValueToVisual(value); 49587 } 49588 49589 return childVisuals; 49590 } 49591 49592 /* 49593* Licensed to the Apache Software Foundation (ASF) under one 49594* or more contributor license agreements. See the NOTICE file 49595* distributed with this work for additional information 49596* regarding copyright ownership. The ASF licenses this file 49597* to you under the Apache License, Version 2.0 (the 49598* "License"); you may not use this file except in compliance 49599* with the License. You may obtain a copy of the License at 49600* 49601* http://www.apache.org/licenses/LICENSE-2.0 49602* 49603* Unless required by applicable law or agreed to in writing, 49604* software distributed under the License is distributed on an 49605* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 49606* KIND, either express or implied. See the License for the 49607* specific language governing permissions and limitations 49608* under the License. 49609*/ 49610 49611 /* 49612* The treemap layout implementation references to the treemap 49613* layout of d3.js (d3/src/layout/treemap.js in v3). The use of 49614* the source code of this file is also subject to the terms 49615* and consitions of its license (BSD-3Clause, see 49616* <echarts/src/licenses/LICENSE-d3>). 49617*/ 49618 49619 var mathMax$4 = Math.max; 49620 var mathMin$4 = Math.min; 49621 var retrieveValue = retrieve; 49622 var each$10 = each$1; 49623 49624 var PATH_BORDER_WIDTH = ['itemStyle', 'borderWidth']; 49625 var PATH_GAP_WIDTH = ['itemStyle', 'gapWidth']; 49626 var PATH_UPPER_LABEL_SHOW = ['upperLabel', 'show']; 49627 var PATH_UPPER_LABEL_HEIGHT = ['upperLabel', 'height']; 49628 49629 /** 49630 * @public 49631 */ 49632 var treemapLayout = { 49633 seriesType: 'treemap', 49634 reset: function (seriesModel, ecModel, api, payload) { 49635 // Layout result in each node: 49636 // {x, y, width, height, area, borderWidth} 49637 var ecWidth = api.getWidth(); 49638 var ecHeight = api.getHeight(); 49639 var seriesOption = seriesModel.option; 49640 49641 var layoutInfo = getLayoutRect( 49642 seriesModel.getBoxLayoutParams(), 49643 { 49644 width: api.getWidth(), 49645 height: api.getHeight() 49646 } 49647 ); 49648 49649 var size = seriesOption.size || []; // Compatible with ec2. 49650 var containerWidth = parsePercent$1( 49651 retrieveValue(layoutInfo.width, size[0]), 49652 ecWidth 49653 ); 49654 var containerHeight = parsePercent$1( 49655 retrieveValue(layoutInfo.height, size[1]), 49656 ecHeight 49657 ); 49658 49659 // Fetch payload info. 49660 var payloadType = payload && payload.type; 49661 var types = ['treemapZoomToNode', 'treemapRootToNode']; 49662 var targetInfo = retrieveTargetInfo(payload, types, seriesModel); 49663 var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove') 49664 ? payload.rootRect : null; 49665 var viewRoot = seriesModel.getViewRoot(); 49666 var viewAbovePath = getPathToRoot(viewRoot); 49667 49668 if (payloadType !== 'treemapMove') { 49669 var rootSize = payloadType === 'treemapZoomToNode' 49670 ? estimateRootSize( 49671 seriesModel, targetInfo, viewRoot, containerWidth, containerHeight 49672 ) 49673 : rootRect 49674 ? [rootRect.width, rootRect.height] 49675 : [containerWidth, containerHeight]; 49676 49677 var sort = seriesOption.sort; 49678 if (sort && sort !== 'asc' && sort !== 'desc') { 49679 sort = 'desc'; 49680 } 49681 var options = { 49682 squareRatio: seriesOption.squareRatio, 49683 sort: sort, 49684 leafDepth: seriesOption.leafDepth 49685 }; 49686 49687 // layout should be cleared because using updateView but not update. 49688 viewRoot.hostTree.clearLayouts(); 49689 49690 // TODO 49691 // optimize: if out of view clip, do not layout. 49692 // But take care that if do not render node out of view clip, 49693 // how to calculate start po 49694 49695 var viewRootLayout = { 49696 x: 0, y: 0, 49697 width: rootSize[0], height: rootSize[1], 49698 area: rootSize[0] * rootSize[1] 49699 }; 49700 viewRoot.setLayout(viewRootLayout); 49701 49702 squarify(viewRoot, options, false, 0); 49703 // Supplement layout. 49704 var viewRootLayout = viewRoot.getLayout(); 49705 each$10(viewAbovePath, function (node, index) { 49706 var childValue = (viewAbovePath[index + 1] || viewRoot).getValue(); 49707 node.setLayout(extend( 49708 {dataExtent: [childValue, childValue], borderWidth: 0, upperHeight: 0}, 49709 viewRootLayout 49710 )); 49711 }); 49712 } 49713 49714 var treeRoot = seriesModel.getData().tree.root; 49715 49716 treeRoot.setLayout( 49717 calculateRootPosition(layoutInfo, rootRect, targetInfo), 49718 true 49719 ); 49720 49721 seriesModel.setLayoutInfo(layoutInfo); 49722 49723 // FIXME 49724 // 现åŨ没æÅâ°clip功èÆÂ½Ã¯Â¼Åæšâæâ¶åÂâecé«Ë宽ãâ¬â 49725 prunning( 49726 treeRoot, 49727 // Transform to base element coordinate system. 49728 new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight), 49729 viewAbovePath, 49730 viewRoot, 49731 0 49732 ); 49733 } 49734 }; 49735 49736 /** 49737 * Layout treemap with squarify algorithm. 49738 * @see https://graphics.ethz.ch/teaching/scivis_common/Literature/squarifiedTreeMaps.pdf 49739 * The implementation references to the treemap layout of d3.js. 49740 * See the license statement at the head of this file. 49741 * 49742 * @protected 49743 * @param {module:echarts/data/Tree~TreeNode} node 49744 * @param {Object} options 49745 * @param {string} options.sort 'asc' or 'desc' 49746 * @param {number} options.squareRatio 49747 * @param {boolean} hideChildren 49748 * @param {number} depth 49749 */ 49750 function squarify(node, options, hideChildren, depth) { 49751 var width; 49752 var height; 49753 49754 if (node.isRemoved()) { 49755 return; 49756 } 49757 49758 var thisLayout = node.getLayout(); 49759 width = thisLayout.width; 49760 height = thisLayout.height; 49761 49762 // Considering border and gap 49763 var nodeModel = node.getModel(); 49764 var borderWidth = nodeModel.get(PATH_BORDER_WIDTH); 49765 var halfGapWidth = nodeModel.get(PATH_GAP_WIDTH) / 2; 49766 var upperLabelHeight = getUpperLabelHeight(nodeModel); 49767 var upperHeight = Math.max(borderWidth, upperLabelHeight); 49768 var layoutOffset = borderWidth - halfGapWidth; 49769 var layoutOffsetUpper = upperHeight - halfGapWidth; 49770 var nodeModel = node.getModel(); 49771 49772 node.setLayout({ 49773 borderWidth: borderWidth, 49774 upperHeight: upperHeight, 49775 upperLabelHeight: upperLabelHeight 49776 }, true); 49777 49778 width = mathMax$4(width - 2 * layoutOffset, 0); 49779 height = mathMax$4(height - layoutOffset - layoutOffsetUpper, 0); 49780 49781 var totalArea = width * height; 49782 var viewChildren = initChildren( 49783 node, nodeModel, totalArea, options, hideChildren, depth 49784 ); 49785 49786 if (!viewChildren.length) { 49787 return; 49788 } 49789 49790 var rect = {x: layoutOffset, y: layoutOffsetUpper, width: width, height: height}; 49791 var rowFixedLength = mathMin$4(width, height); 49792 var best = Infinity; // the best row score so far 49793 var row = []; 49794 row.area = 0; 49795 49796 for (var i = 0, len = viewChildren.length; i < len;) { 49797 var child = viewChildren[i]; 49798 49799 row.push(child); 49800 row.area += child.getLayout().area; 49801 var score = worst(row, rowFixedLength, options.squareRatio); 49802 49803 // continue with this orientation 49804 if (score <= best) { 49805 i++; 49806 best = score; 49807 } 49808 // abort, and try a different orientation 49809 else { 49810 row.area -= row.pop().getLayout().area; 49811 position(row, rowFixedLength, rect, halfGapWidth, false); 49812 rowFixedLength = mathMin$4(rect.width, rect.height); 49813 row.length = row.area = 0; 49814 best = Infinity; 49815 } 49816 } 49817 49818 if (row.length) { 49819 position(row, rowFixedLength, rect, halfGapWidth, true); 49820 } 49821 49822 if (!hideChildren) { 49823 var childrenVisibleMin = nodeModel.get('childrenVisibleMin'); 49824 if (childrenVisibleMin != null && totalArea < childrenVisibleMin) { 49825 hideChildren = true; 49826 } 49827 } 49828 49829 for (var i = 0, len = viewChildren.length; i < len; i++) { 49830 squarify(viewChildren[i], options, hideChildren, depth + 1); 49831 } 49832 } 49833 49834 /** 49835 * Set area to each child, and calculate data extent for visual coding. 49836 */ 49837 function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) { 49838 var viewChildren = node.children || []; 49839 var orderBy = options.sort; 49840 orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null); 49841 49842 var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth; 49843 49844 // leafDepth has higher priority. 49845 if (hideChildren && !overLeafDepth) { 49846 return (node.viewChildren = []); 49847 } 49848 49849 // Sort children, order by desc. 49850 viewChildren = filter(viewChildren, function (child) { 49851 return !child.isRemoved(); 49852 }); 49853 49854 sort$1(viewChildren, orderBy); 49855 49856 var info = statistic(nodeModel, viewChildren, orderBy); 49857 49858 if (info.sum === 0) { 49859 return (node.viewChildren = []); 49860 } 49861 49862 info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren); 49863 49864 if (info.sum === 0) { 49865 return (node.viewChildren = []); 49866 } 49867 49868 // Set area to each child. 49869 for (var i = 0, len = viewChildren.length; i < len; i++) { 49870 var area = viewChildren[i].getValue() / info.sum * totalArea; 49871 // Do not use setLayout({...}, true), because it is needed to clear last layout. 49872 viewChildren[i].setLayout({area: area}); 49873 } 49874 49875 if (overLeafDepth) { 49876 viewChildren.length && node.setLayout({isLeafRoot: true}, true); 49877 viewChildren.length = 0; 49878 } 49879 49880 node.viewChildren = viewChildren; 49881 node.setLayout({dataExtent: info.dataExtent}, true); 49882 49883 return viewChildren; 49884 } 49885 49886 /** 49887 * Consider 'visibleMin'. Modify viewChildren and get new sum. 49888 */ 49889 function filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) { 49890 49891 // visibleMin is not supported yet when no option.sort. 49892 if (!orderBy) { 49893 return sum; 49894 } 49895 49896 var visibleMin = nodeModel.get('visibleMin'); 49897 var len = orderedChildren.length; 49898 var deletePoint = len; 49899 49900 // Always travel from little value to big value. 49901 for (var i = len - 1; i >= 0; i--) { 49902 var value = orderedChildren[ 49903 orderBy === 'asc' ? len - i - 1 : i 49904 ].getValue(); 49905 49906 if (value / sum * totalArea < visibleMin) { 49907 deletePoint = i; 49908 sum -= value; 49909 } 49910 } 49911 49912 orderBy === 'asc' 49913 ? orderedChildren.splice(0, len - deletePoint) 49914 : orderedChildren.splice(deletePoint, len - deletePoint); 49915 49916 return sum; 49917 } 49918 49919 /** 49920 * Sort 49921 */ 49922 function sort$1(viewChildren, orderBy) { 49923 if (orderBy) { 49924 viewChildren.sort(function (a, b) { 49925 var diff = orderBy === 'asc' 49926 ? a.getValue() - b.getValue() : b.getValue() - a.getValue(); 49927 return diff === 0 49928 ? (orderBy === 'asc' 49929 ? a.dataIndex - b.dataIndex : b.dataIndex - a.dataIndex 49930 ) 49931 : diff; 49932 }); 49933 } 49934 return viewChildren; 49935 } 49936 49937 /** 49938 * Statistic 49939 */ 49940 function statistic(nodeModel, children, orderBy) { 49941 // Calculate sum. 49942 var sum = 0; 49943 for (var i = 0, len = children.length; i < len; i++) { 49944 sum += children[i].getValue(); 49945 } 49946 49947 // Statistic data extent for latter visual coding. 49948 // Notice: data extent should be calculate based on raw children 49949 // but not filtered view children, otherwise visual mapping will not 49950 // be stable when zoom (where children is filtered by visibleMin). 49951 49952 var dimension = nodeModel.get('visualDimension'); 49953 var dataExtent; 49954 49955 // The same as area dimension. 49956 if (!children || !children.length) { 49957 dataExtent = [NaN, NaN]; 49958 } 49959 else if (dimension === 'value' && orderBy) { 49960 dataExtent = [ 49961 children[children.length - 1].getValue(), 49962 children[0].getValue() 49963 ]; 49964 orderBy === 'asc' && dataExtent.reverse(); 49965 } 49966 // Other dimension. 49967 else { 49968 var dataExtent = [Infinity, -Infinity]; 49969 each$10(children, function (child) { 49970 var value = child.getValue(dimension); 49971 value < dataExtent[0] && (dataExtent[0] = value); 49972 value > dataExtent[1] && (dataExtent[1] = value); 49973 }); 49974 } 49975 49976 return {sum: sum, dataExtent: dataExtent}; 49977 } 49978 49979 /** 49980 * Computes the score for the specified row, 49981 * as the worst aspect ratio. 49982 */ 49983 function worst(row, rowFixedLength, ratio) { 49984 var areaMax = 0; 49985 var areaMin = Infinity; 49986 49987 for (var i = 0, area, len = row.length; i < len; i++) { 49988 area = row[i].getLayout().area; 49989 if (area) { 49990 area < areaMin && (areaMin = area); 49991 area > areaMax && (areaMax = area); 49992 } 49993 } 49994 49995 var squareArea = row.area * row.area; 49996 var f = rowFixedLength * rowFixedLength * ratio; 49997 49998 return squareArea 49999 ? mathMax$4( 50000 (f * areaMax) / squareArea, 50001 squareArea / (f * areaMin) 50002 ) 50003 : Infinity; 50004 } 50005 50006 /** 50007 * Positions the specified row of nodes. Modifies `rect`. 50008 */ 50009 function position(row, rowFixedLength, rect, halfGapWidth, flush) { 50010 // When rowFixedLength === rect.width, 50011 // it is horizontal subdivision, 50012 // rowFixedLength is the width of the subdivision, 50013 // rowOtherLength is the height of the subdivision, 50014 // and nodes will be positioned from left to right. 50015 50016 // wh[idx0WhenH] means: when horizontal, 50017 // wh[idx0WhenH] => wh[0] => 'width'. 50018 // xy[idx1WhenH] => xy[1] => 'y'. 50019 var idx0WhenH = rowFixedLength === rect.width ? 0 : 1; 50020 var idx1WhenH = 1 - idx0WhenH; 50021 var xy = ['x', 'y']; 50022 var wh = ['width', 'height']; 50023 50024 var last = rect[xy[idx0WhenH]]; 50025 var rowOtherLength = rowFixedLength 50026 ? row.area / rowFixedLength : 0; 50027 50028 if (flush || rowOtherLength > rect[wh[idx1WhenH]]) { 50029 rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow 50030 } 50031 for (var i = 0, rowLen = row.length; i < rowLen; i++) { 50032 var node = row[i]; 50033 var nodeLayout = {}; 50034 var step = rowOtherLength 50035 ? node.getLayout().area / rowOtherLength : 0; 50036 50037 var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax$4(rowOtherLength - 2 * halfGapWidth, 0); 50038 50039 // We use Math.max/min to avoid negative width/height when considering gap width. 50040 var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last; 50041 var modWH = (i === rowLen - 1 || remain < step) ? remain : step; 50042 var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax$4(modWH - 2 * halfGapWidth, 0); 50043 50044 nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin$4(halfGapWidth, wh1 / 2); 50045 nodeLayout[xy[idx0WhenH]] = last + mathMin$4(halfGapWidth, wh0 / 2); 50046 50047 last += modWH; 50048 node.setLayout(nodeLayout, true); 50049 } 50050 50051 rect[xy[idx1WhenH]] += rowOtherLength; 50052 rect[wh[idx1WhenH]] -= rowOtherLength; 50053 } 50054 50055// Return [containerWidth, containerHeight] as defualt. 50056 function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) { 50057 // If targetInfo.node exists, we zoom to the node, 50058 // so estimate whold width and heigth by target node. 50059 var currNode = (targetInfo || {}).node; 50060 var defaultSize = [containerWidth, containerHeight]; 50061 50062 if (!currNode || currNode === viewRoot) { 50063 return defaultSize; 50064 } 50065 50066 var parent; 50067 var viewArea = containerWidth * containerHeight; 50068 var area = viewArea * seriesModel.option.zoomToNodeRatio; 50069 50070 while (parent = currNode.parentNode) { // jshint ignore:line 50071 var sum = 0; 50072 var siblings = parent.children; 50073 50074 for (var i = 0, len = siblings.length; i < len; i++) { 50075 sum += siblings[i].getValue(); 50076 } 50077 var currNodeValue = currNode.getValue(); 50078 if (currNodeValue === 0) { 50079 return defaultSize; 50080 } 50081 area *= sum / currNodeValue; 50082 50083 // Considering border, suppose aspect ratio is 1. 50084 var parentModel = parent.getModel(); 50085 var borderWidth = parentModel.get(PATH_BORDER_WIDTH); 50086 var upperHeight = Math.max(borderWidth, getUpperLabelHeight(parentModel, borderWidth)); 50087 area += 4 * borderWidth * borderWidth 50088 + (3 * borderWidth + upperHeight) * Math.pow(area, 0.5); 50089 50090 area > MAX_SAFE_INTEGER && (area = MAX_SAFE_INTEGER); 50091 50092 currNode = parent; 50093 } 50094 50095 area < viewArea && (area = viewArea); 50096 var scale = Math.pow(area / viewArea, 0.5); 50097 50098 return [containerWidth * scale, containerHeight * scale]; 50099 } 50100 50101// Root postion base on coord of containerGroup 50102 function calculateRootPosition(layoutInfo, rootRect, targetInfo) { 50103 if (rootRect) { 50104 return {x: rootRect.x, y: rootRect.y}; 50105 } 50106 50107 var defaultPosition = {x: 0, y: 0}; 50108 if (!targetInfo) { 50109 return defaultPosition; 50110 } 50111 50112 // If targetInfo is fetched by 'retrieveTargetInfo', 50113 // old tree and new tree are the same tree, 50114 // so the node still exists and we can visit it. 50115 50116 var targetNode = targetInfo.node; 50117 var layout = targetNode.getLayout(); 50118 50119 if (!layout) { 50120 return defaultPosition; 50121 } 50122 50123 // Transform coord from local to container. 50124 var targetCenter = [layout.width / 2, layout.height / 2]; 50125 var node = targetNode; 50126 while (node) { 50127 var nodeLayout = node.getLayout(); 50128 targetCenter[0] += nodeLayout.x; 50129 targetCenter[1] += nodeLayout.y; 50130 node = node.parentNode; 50131 } 50132 50133 return { 50134 x: layoutInfo.width / 2 - targetCenter[0], 50135 y: layoutInfo.height / 2 - targetCenter[1] 50136 }; 50137 } 50138 50139// Mark nodes visible for prunning when visual coding and rendering. 50140// Prunning depends on layout and root position, so we have to do it after layout. 50141 function prunning(node, clipRect, viewAbovePath, viewRoot, depth) { 50142 var nodeLayout = node.getLayout(); 50143 var nodeInViewAbovePath = viewAbovePath[depth];
50144 var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node; 50145 50146 if ( 50147 (nodeInViewAbovePath && !isAboveViewRoot) 50148 || (depth === viewAbovePath.length && node !== viewRoot) 50149 ) { 50150 return; 50151 } 50152 50153 node.setLayout({ 50154 // isInView means: viewRoot sub tree + viewAbovePath 50155 isInView: true, 50156 // invisible only means: outside view clip so that the node can not 50157 // see but still layout for animation preparation but not render. 50158 invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout), 50159 isAboveViewRoot: isAboveViewRoot 50160 }, true); 50161 50162 // Transform to child coordinate. 50163 var childClipRect = new BoundingRect( 50164 clipRect.x - nodeLayout.x, 50165 clipRect.y - nodeLayout.y, 50166 clipRect.width, 50167 clipRect.height 50168 ); 50169 50170 each$10(node.viewChildren || [], function (child) { 50171 prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1); 50172 }); 50173 } 50174 50175 function getUpperLabelHeight(model) { 50176 return model.get(PATH_UPPER_LABEL_SHOW) ? model.get(PATH_UPPER_LABEL_HEIGHT) : 0; 50177 } 50178 50179 /* 50180* Licensed to the Apache Software Foundation (ASF) under one 50181* or more contributor license agreements. See the NOTICE file 50182* distributed with this work for additional information 50183* regarding copyright ownership. The ASF licenses this file 50184* to you under the Apache License, Version 2.0 (the 50185* "License"); you may not use this file except in compliance 50186* with the License. You may obtain a copy of the License at 50187* 50188* http://www.apache.org/licenses/LICENSE-2.0 50189* 50190* Unless required by applicable law or agreed to in writing, 50191* software distributed under the License is distributed on an 50192* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 50193* KIND, either express or implied. See the License for the 50194* specific language governing permissions and limitations 50195* under the License. 50196*/ 50197 50198 registerVisual(treemapVisual); 50199 registerLayout(treemapLayout); 50200 50201 /* 50202* Licensed to the Apache Software Foundation (ASF) under one 50203* or more contributor license agreements. See the NOTICE file 50204* distributed with this work for additional information 50205* regarding copyright ownership. The ASF licenses this file 50206* to you under the Apache License, Version 2.0 (the 50207* "License"); you may not use this file except in compliance 50208* with the License. You may obtain a copy of the License at 50209* 50210* http://www.apache.org/licenses/LICENSE-2.0 50211* 50212* Unless required by applicable law or agreed to in writing, 50213* software distributed under the License is distributed on an 50214* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 50215* KIND, either express or implied. See the License for the 50216* specific language governing permissions and limitations 50217* under the License. 50218*/ 50219 50220 /** 50221 * Graph data structure 50222 * 50223 * @module echarts/data/Graph 50224 * @author Yi Shen(https://www.github.com/pissang) 50225 */ 50226 50227// id may be function name of Object, add a prefix to avoid this problem. 50228 function generateNodeKey (id) { 50229 return '_EC_' + id; 50230 } 50231 /** 50232 * @alias module:echarts/data/Graph 50233 * @constructor 50234 * @param {boolean} directed 50235 */ 50236 var Graph = function(directed) { 50237 /** 50238 * æË¯å¦æË¯æÅâ°Ã¥ÂâÃ¥âºÂ¾ 50239 * @type {boolean} 50240 * @private 50241 */ 50242 this._directed = directed || false; 50243 50244 /** 50245 * @type {Array.<module:echarts/data/Graph.Node>} 50246 * @readOnly 50247 */ 50248 this.nodes = []; 50249 50250 /** 50251 * @type {Array.<module:echarts/data/Graph.Edge>} 50252 * @readOnly 50253 */ 50254 this.edges = []; 50255 50256 /** 50257 * @type {Object.<string, module:echarts/data/Graph.Node>} 50258 * @private 50259 */ 50260 this._nodesMap = {}; 50261 /** 50262 * @type {Object.<string, module:echarts/data/Graph.Edge>} 50263 * @private 50264 */ 50265 this._edgesMap = {}; 50266 50267 /**
50268 * @type {module:echarts/data/List} 50269 * @readOnly 50270 */ 50271 this.data; 50272 50273 /** 50274 * @type {module:echarts/data/List} 50275 * @readOnly 50276 */ 50277 this.edgeData; 50278 }; 50279 50280 var graphProto = Graph.prototype; 50281 /** 50282 * @type {string} 50283 */ 50284 graphProto.type = 'graph'; 50285 50286 /** 50287 * If is directed graph 50288 * @return {boolean} 50289 */ 50290 graphProto.isDirected = function () { 50291 return this._directed; 50292 }; 50293 50294 /** 50295 * Add a new node 50296 * @param {string} id 50297 * @param {number} [dataIndex] 50298 */ 50299 graphProto.addNode = function (id, dataIndex) { 50300 id = id || ('' + dataIndex); 50301 50302 var nodesMap = this._nodesMap; 50303 50304 if (nodesMap[generateNodeKey(id)]) { 50305 if (__DEV__) { 50306 console.error('Graph nodes have duplicate name or id'); 50307 } 50308 return; 50309 } 50310 50311 var node = new Node(id, dataIndex); 50312 node.hostGraph = this; 50313 50314 this.nodes.push(node); 50315 50316 nodesMap[generateNodeKey(id)] = node; 50317 return node; 50318 }; 50319 50320 /** 50321 * Get node by data index 50322 * @param {number} dataIndex 50323 * @return {module:echarts/data/Graph~Node} 50324 */ 50325 graphProto.getNodeByIndex = function (dataIndex) { 50326 var rawIdx = this.data.getRawIndex(dataIndex); 50327 return this.nodes[rawIdx]; 50328 }; 50329 /** 50330 * Get node by id 50331 * @param {string} id 50332 * @return {module:echarts/data/Graph.Node} 50333 */ 50334 graphProto.getNodeById = function (id) { 50335 return this._nodesMap[generateNodeKey(id)]; 50336 }; 50337 50338 /** 50339 * Add a new edge 50340 * @param {number|string|module:echarts/data/Graph.Node} n1 50341 * @param {number|string|module:echarts/data/Graph.Node} n2 50342 * @param {number} [dataIndex=-1] 50343 * @return {module:echarts/data/Graph.Edge} 50344 */ 50345 graphProto.addEdge = function (n1, n2, dataIndex) { 50346 var nodesMap = this._nodesMap; 50347 var edgesMap = this._edgesMap; 50348 50349 // PNEDING 50350 if (typeof n1 === 'number') { 50351 n1 = this.nodes[n1]; 50352 } 50353 if (typeof n2 === 'number') { 50354 n2 = this.nodes[n2]; 50355 } 50356 50357 if (!Node.isInstance(n1)) { 50358 n1 = nodesMap[generateNodeKey(n1)]; 50359 } 50360 if (!Node.isInstance(n2)) { 50361 n2 = nodesMap[generateNodeKey(n2)]; 50362 } 50363 if (!n1 || !n2) { 50364 return; 50365 } 50366 50367 var key = n1.id + '-' + n2.id; 50368 // PENDING 50369 if (edgesMap[key]) { 50370 return; 50371 } 50372 50373 var edge = new Edge(n1, n2, dataIndex); 50374 edge.hostGraph = this; 50375 50376 if (this._directed) { 50377 n1.outEdges.push(edge); 50378 n2.inEdges.push(edge); 50379 } 50380 n1.edges.push(edge); 50381 if (n1 !== n2) { 50382 n2.edges.push(edge); 50383 } 50384 50385 this.edges.push(edge); 50386 edgesMap[key] = edge; 50387 50388 return edge; 50389 }; 50390 50391 /** 50392 * Get edge by data index 50393 * @param {number} dataIndex 50394 * @return {module:echarts/data/Graph~Node} 50395 */ 50396 graphProto.getEdgeByIndex = function (dataIndex) { 50397 var rawIdx = this.edgeData.getRawIndex(dataIndex); 50398 return this.edges[rawIdx]; 50399 }; 50400 /** 50401 * Get edge by two linked nodes 50402 * @param {module:echarts/data/Graph.Node|string} n1 50403 * @param {module:echarts/data/Graph.Node|string} n2 50404 * @return {module:echarts/data/Graph.Edge} 50405 */ 50406 graphProto.getEdge = function (n1, n2) { 50407 if (Node.isInstance(n1)) { 50408 n1 = n1.id; 50409 } 50410 if (Node.isInstance(n2)) { 50411 n2 = n2.id; 50412 } 50413 50414 var edgesMap = this._edgesMap; 50415 50416 if (this._directed) { 50417 return edgesMap[n1 + '-' + n2]; 50418 } else { 50419 return edgesMap[n1 + '-' + n2] 50420 || edgesMap[n2 + '-' + n1]; 50421 } 50422 }; 50423 50424 /** 50425 * Iterate all nodes 50426 * @param {Function} cb 50427 * @param {*} [context] 50428 */ 50429 graphProto.eachNode = function (cb, context) { 50430 var nodes = this.nodes; 50431 var len = nodes.length; 50432 for (var i = 0; i < len; i++) { 50433 if (nodes[i].dataIndex >= 0) { 50434 cb.call(context, nodes[i], i); 50435 } 50436 } 50437 }; 50438 50439 /** 50440 * Iterate all edges 50441 * @param {Function} cb 50442 * @param {*} [context] 50443 */ 50444 graphProto.eachEdge = function (cb, context) { 50445 var edges = this.edges;
50446 var len = edges.length; 50447 for (var i = 0; i < len; i++) { 50448 if (edges[i].dataIndex >= 0 50449 && edges[i].node1.dataIndex >= 0 50450 && edges[i].node2.dataIndex >= 0 50451 ) { 50452 cb.call(context, edges[i], i); 50453 } 50454 } 50455 }; 50456 50457 /** 50458 * Breadth first traverse 50459 * @param {Function} cb 50460 * @param {module:echarts/data/Graph.Node} startNode 50461 * @param {string} [direction='none'] 'none'|'in'|'out' 50462 * @param {*} [context] 50463 */ 50464 graphProto.breadthFirstTraverse = function ( 50465 cb, startNode, direction, context 50466 ) { 50467 if (!Node.isInstance(startNode)) { 50468 startNode = this._nodesMap[generateNodeKey(startNode)]; 50469 } 50470 if (!startNode) { 50471 return; 50472 } 50473 50474 var edgeType = direction === 'out' 50475 ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges'); 50476 50477 for (var i = 0; i < this.nodes.length; i++) { 50478 this.nodes[i].__visited = false; 50479 } 50480 50481 if (cb.call(context, startNode, null)) { 50482 return; 50483 } 50484 50485 var queue = [startNode]; 50486 while (queue.length) { 50487 var currentNode = queue.shift(); 50488 var edges = currentNode[edgeType]; 50489 50490 for (var i = 0; i < edges.length; i++) { 50491 var e = edges[i]; 50492 var otherNode = e.node1 === currentNode 50493 ? e.node2 : e.node1; 50494 if (!otherNode.__visited) { 50495 if (cb.call(context, otherNode, currentNode)) { 50496 // Stop traversing 50497 return; 50498 } 50499 queue.push(otherNode); 50500 otherNode.__visited = true; 50501 } 50502 } 50503 } 50504 }; 50505 50506// TODO 50507// graphProto.depthFirstTraverse = function ( 50508// cb, startNode, direction, context 50509// ) { 50510 50511// }; 50512 50513// Filter update 50514 graphProto.update = function () { 50515 var data = this.data; 50516 var edgeData = this.edgeData; 50517 var nodes = this.nodes; 50518 var edges = this.edges; 50519 50520 for (var i = 0, len = nodes.length; i < len; i++) { 50521 nodes[i].dataIndex = -1; 50522 } 50523 for (var i = 0, len = data.count(); i < len; i++) { 50524 nodes[data.getRawIndex(i)].dataIndex = i; 50525 } 50526 50527 edgeData.filterSelf(function (idx) { 50528 var edge = edges[edgeData.getRawIndex(idx)]; 50529 return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0; 50530 }); 50531 50532 // Update edge 50533 for (var i = 0, len = edges.length; i < len; i++) { 50534 edges[i].dataIndex = -1; 50535 } 50536 for (var i = 0, len = edgeData.count(); i < len; i++) { 50537 edges[edgeData.getRawIndex(i)].dataIndex = i; 50538 } 50539 }; 50540 50541 /** 50542 * @return {module:echarts/data/Graph} 50543 */ 50544 graphProto.clone = function () { 50545 var graph = new Graph(this._directed); 50546 var nodes = this.nodes; 50547 var edges = this.edges; 50548 for (var i = 0; i < nodes.length; i++) { 50549 graph.addNode(nodes[i].id, nodes[i].dataIndex); 50550 } 50551 for (var i = 0; i < edges.length; i++) { 50552 var e = edges[i]; 50553 graph.addEdge(e.node1.id, e.node2.id, e.dataIndex); 50554 } 50555 return graph; 50556 }; 50557 50558 50559 /** 50560 * @alias module:echarts/data/Graph.Node 50561 */ 50562 function Node(id, dataIndex) { 50563 /** 50564 * @type {string} 50565 */ 50566 this.id = id == null ? '' : id; 50567 50568 /** 50569 * @type {Array.<module:echarts/data/Graph.Edge>} 50570 */ 50571 this.inEdges = []; 50572 /** 50573 * @type {Array.<module:echarts/data/Graph.Edge>} 50574 */ 50575 this.outEdges = []; 50576 /** 50577 * @type {Array.<module:echarts/data/Graph.Edge>} 50578 */ 50579 this.edges = []; 50580 /** 50581 * @type {module:echarts/data/Graph} 50582 */ 50583 this.hostGraph; 50584 50585 /** 50586 * @type {number} 50587 */ 50588 this.dataIndex = dataIndex == null ? -1 : dataIndex; 50589 } 50590 50591 Node.prototype = { 50592 50593 constructor: Node, 50594 50595 /** 50596 * @return {number} 50597 */ 50598 degree: function () { 50599 return this.edges.length; 50600 }, 50601 50602 /** 50603 * @return {number} 50604 */ 50605 inDegree: function () { 50606 return this.inEdges.length; 50607 }, 50608 50609 /** 50610 * @return {number} 50611 */ 50612 outDegree: function () { 50613 return this.outEdges.length; 50614 }, 50615 50616 /** 50617 * @param {string} [path] 50618 * @return {module:echarts/model/Model} 50619 */ 50620 getModel: function (path) { 50621 if (this.dataIndex < 0) { 50622 return; 50623 } 50624 var graph = this.hostGraph; 50625 var itemModel = graph.data.getItemModel(this.dataIndex); 50626 50627 return itemModel.getModel(path); 50628 } 50629 }; 50630 50631 /** 50632 * Ã¥âºÂ¾Ã¨Â¾Â¹
50633 * @alias module:echarts/data/Graph.Edge 50634 * @param {module:echarts/data/Graph.Node} n1 50635 * @param {module:echarts/data/Graph.Node} n2 50636 * @param {number} [dataIndex=-1] 50637 */ 50638 function Edge(n1, n2, dataIndex) { 50639 50640 /** 50641 * èŠâçâ¹1ï¼Åå¦âæžÅæË¯æÅâ°Ã¥ÂâÃ¥âºÂ¾Ã¥Ëâ¢Ã¤Â¸ÂºÃ¦ÂºÂèŠâçâ¹ 50642 * @type {module:echarts/data/Graph.Node} 50643 */ 50644 this.node1 = n1; 50645 50646 /** 50647 * èŠâçâ¹2ï¼Åå¦âæžÅæË¯æÅâ°Ã¥ÂâÃ¥âºÂ¾Ã¥Ëâ¢Ã¤Â¸ÂºÃ§âºÂ®Ã¦ â¡Ã¨Å âçâ¹ 50648 * @type {module:echarts/data/Graph.Node} 50649 */ 50650 this.node2 = n2; 50651 50652 this.dataIndex = dataIndex == null ? -1 : dataIndex; 50653 } 50654 50655 /** 50656 * @param {string} [path] 50657 * @return {module:echarts/model/Model} 50658 */ 50659 Edge.prototype.getModel = function (path) { 50660 if (this.dataIndex < 0) { 50661 return; 50662 } 50663 var graph = this.hostGraph; 50664 var itemModel = graph.edgeData.getItemModel(this.dataIndex); 50665 50666 return itemModel.getModel(path); 50667 }; 50668 50669 var createGraphDataProxyMixin = function (hostName, dataName) { 50670 return { 50671 /** 50672 * @param {string=} [dimension='value'] Default 'value'. can be 'a', 'b', 'c', 'd', 'e'. 50673 * @return {number} 50674 */ 50675 getValue: function (dimension) { 50676 var data = this[hostName][dataName]; 50677 return data.get(data.getDimension(dimension || 'value'), this.dataIndex); 50678 }, 50679 50680 /** 50681 * @param {Object|string} key 50682 * @param {*} [value] 50683 */ 50684 setVisual: function (key, value) { 50685 this.dataIndex >= 0 50686 && this[hostName][dataName].setItemVisual(this.dataIndex, key, value); 50687 }, 50688 50689 /** 50690 * @param {string} key 50691 * @return {boolean} 50692 */ 50693 getVisual: function (key, ignoreParent) { 50694 return this[hostName][dataName].getItemVisual(this.dataIndex, key, ignoreParent); 50695 }, 50696 50697 /** 50698 * @param {Object} layout 50699 * @return {boolean} [merge=false] 50700 */ 50701 setLayout: function (layout, merge$$1) { 50702 this.dataIndex >= 0 50703 && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge$$1); 50704 }, 50705 50706 /** 50707 * @return {Object} 50708 */ 50709 getLayout: function () { 50710 return this[hostName][dataName].getItemLayout(this.dataIndex); 50711 }, 50712 50713 /** 50714 * @return {module:zrender/Element} 50715 */ 50716 getGraphicEl: function () { 50717 return this[hostName][dataName].getItemGraphicEl(this.dataIndex); 50718 }, 50719 50720 /** 50721 * @return {number} 50722 */ 50723 getRawIndex: function () { 50724 return this[hostName][dataName].getRawIndex(this.dataIndex); 50725 } 50726 }; 50727 }; 50728 50729 mixin(Node, createGraphDataProxyMixin('hostGraph', 'data')); 50730 mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData')); 50731 50732 Graph.Node = Node; 50733 Graph.Edge = Edge; 50734 50735 enableClassCheck(Node); 50736 enableClassCheck(Edge); 50737 50738 /* 50739* Licensed to the Apache Software Foundation (ASF) under one 50740* or more contributor license agreements. See the NOTICE file 50741* distributed with this work for additional information 50742* regarding copyright ownership. The ASF licenses this file 50743* to you under the Apache License, Version 2.0 (the 50744* "License"); you may not use this file except in compliance 50745* with the License. You may obtain a copy of the License at 50746* 50747* http://www.apache.org/licenses/LICENSE-2.0 50748* 50749* Unless required by applicable law or agreed to in writing, 50750* software distributed under the License is distributed on an 50751* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 50752* KIND, either express or implied. See the License for the 50753* specific language governing permissions and limitations 50754* under the License. 50755*/ 50756
50757 var createGraphFromNodeEdge = function (nodes, edges, seriesModel, directed, beforeLink) { 50758 // ??? TODO 50759 // support dataset? 50760 var graph = new Graph(directed); 50761 for (var i = 0; i < nodes.length; i++) { 50762 graph.addNode(retrieve( 50763 // Id, name, dataIndex 50764 nodes[i].id, nodes[i].name, i 50765 ), i); 50766 } 50767 50768 var linkNameList = []; 50769 var validEdges = []; 50770 var linkCount = 0; 50771 for (var i = 0; i < edges.length; i++) { 50772 var link = edges[i]; 50773 var source = link.source; 50774 var target = link.target; 50775 // addEdge may fail when source or target not exists 50776 if (graph.addEdge(source, target, linkCount)) { 50777 validEdges.push(link); 50778 linkNameList.push(retrieve(link.id, source + ' > ' + target)); 50779 linkCount++; 50780 } 50781 } 50782 50783 var coordSys = seriesModel.get('coordinateSystem'); 50784 var nodeData; 50785 if (coordSys === 'cartesian2d' || coordSys === 'polar') { 50786 nodeData = createListFromArray(nodes, seriesModel); 50787 } 50788 else { 50789 var coordSysCtor = CoordinateSystemManager.get(coordSys); 50790 var coordDimensions = (coordSysCtor && coordSysCtor.type !== 'view') 50791 ? (coordSysCtor.dimensions || []) : []; 50792 // FIXME: Some geo do not need `value` dimenson, whereas `calendar` needs 50793 // `value` dimension, but graph need `value` dimension. It's better to 50794 // uniform this behavior. 50795 if (indexOf(coordDimensions, 'value') < 0) { 50796 coordDimensions.concat(['value']); 50797 } 50798 50799 var dimensionNames = createDimensions(nodes, { 50800 coordDimensions: coordDimensions 50801 }); 50802 nodeData = new List(dimensionNames, seriesModel); 50803 nodeData.initData(nodes); 50804 } 50805 50806 var edgeData = new List(['value'], seriesModel); 50807 edgeData.initData(validEdges, linkNameList); 50808 50809 beforeLink && beforeLink(nodeData, edgeData); 50810 50811 linkList({ 50812 mainData: nodeData, 50813 struct: graph, 50814 structAttr: 'graph', 50815 datas: {node: nodeData, edge: edgeData}, 50816 datasAttr: {node: 'data', edge: 'edgeData'} 50817 }); 50818 50819 // Update dataIndex of nodes and edges because invalid edge may be removed 50820 graph.update(); 50821 50822 return graph; 50823 }; 50824 50825 /* 50826* Licensed to the Apache Software Foundation (ASF) under one 50827* or more contributor license agreements. See the NOTICE file 50828* distributed with this work for additional information 50829* regarding copyright ownership. The ASF licenses this file 50830* to you under the Apache License, Version 2.0 (the 50831* "License"); you may not use this file except in compliance 50832* with the License. You may obtain a copy of the License at 50833* 50834* http://www.apache.org/licenses/LICENSE-2.0 50835* 50836* Unless required by applicable law or agreed to in writing, 50837* software distributed under the License is distributed on an 50838* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 50839* KIND, either express or implied. See the License for the 50840* specific language governing permissions and limitations 50841* under the License. 50842*/ 50843 50844 var GraphSeries = extendSeriesModel({ 50845 50846 type: 'series.graph', 50847 50848 init: function (option) { 50849 GraphSeries.superApply(this, 'init', arguments); 50850 50851 // Provide data for legend select 50852 this.legendDataProvider = function () { 50853 return this._categoriesData; 50854 }; 50855 50856 this.fillDataTextStyle(option.edges || option.links); 50857 50858 this._updateCategoriesData(); 50859 }, 50860 50861 mergeOption: function (option) { 50862 GraphSeries.superApply(this, 'mergeOption', arguments); 50863 50864 this.fillDataTextStyle(option.edges || option.links); 50865 50866 this._updateCategoriesData(); 50867 }, 50868 50869 mergeDefaultAndTheme: function (option) { 50870 GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments); 50871 defaultEmphasis(option, ['edgeLabel'], ['show']); 50872 }, 50873 50874 getInitialData: function (option, ecModel) { 50875 var edges = option.edges || option.links || []; 50876 var nodes = option.data || option.nodes || []; 50877 var self = this; 50878 50879 if (nodes && edges) {
50880 return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data; 50881 } 50882 50883 function beforeLink(nodeData, edgeData) { 50884 // Overwrite nodeData.getItemModel to 50885 nodeData.wrapMethod('getItemModel', function (model) { 50886 var categoriesModels = self._categoriesModels; 50887 var categoryIdx = model.getShallow('category'); 50888 var categoryModel = categoriesModels[categoryIdx]; 50889 if (categoryModel) { 50890 categoryModel.parentModel = model.parentModel; 50891 model.parentModel = categoryModel; 50892 } 50893 return model; 50894 }); 50895 50896 var edgeLabelModel = self.getModel('edgeLabel'); 50897 // For option `edgeLabel` can be found by label.xxx.xxx on item mode. 50898 var fakeSeriesModel = new Model( 50899 {label: edgeLabelModel.option}, 50900 edgeLabelModel.parentModel, 50901 ecModel 50902 ); 50903 var emphasisEdgeLabelModel = self.getModel('emphasis.edgeLabel'); 50904 var emphasisFakeSeriesModel = new Model( 50905 {emphasis: {label: emphasisEdgeLabelModel.option}}, 50906 emphasisEdgeLabelModel.parentModel, 50907 ecModel 50908 ); 50909 50910 edgeData.wrapMethod('getItemModel', function (model) { 50911 model.customizeGetParent(edgeGetParent); 50912 return model; 50913 }); 50914 50915 function edgeGetParent(path) { 50916 path = this.parsePath(path); 50917 return (path && path[0] === 'label') 50918 ? fakeSeriesModel 50919 : (path && path[0] === 'emphasis' && path[1] === 'label') 50920 ? emphasisFakeSeriesModel 50921 : this.parentModel; 50922 } 50923 } 50924 }, 50925 50926 /** 50927 * @return {module:echarts/data/Graph} 50928 */ 50929 getGraph: function () { 50930 return this.getData().graph; 50931 }, 50932 50933 /** 50934 * @return {module:echarts/data/List} 50935 */ 50936 getEdgeData: function () { 50937 return this.getGraph().edgeData; 50938 }, 50939 50940 /** 50941 * @return {module:echarts/data/List} 50942 */ 50943 getCategoriesData: function () { 50944 return this._categoriesData; 50945 }, 50946 50947 /** 50948 * @override 50949 */ 50950 formatTooltip: function (dataIndex, multipleSeries, dataType) { 50951 if (dataType === 'edge') { 50952 var nodeData = this.getData(); 50953 var params = this.getDataParams(dataIndex, dataType); 50954 var edge = nodeData.graph.getEdgeByIndex(dataIndex); 50955 var sourceName = nodeData.getName(edge.node1.dataIndex); 50956 var targetName = nodeData.getName(edge.node2.dataIndex); 50957 50958 var html = []; 50959 sourceName != null && html.push(sourceName); 50960 targetName != null && html.push(targetName); 50961 html = encodeHTML(html.join(' > ')); 50962 50963 if (params.value) { 50964 html += ' : ' + encodeHTML(params.value); 50965 } 50966 return html; 50967 } 50968 else { // dataType === 'node' or empty 50969 return GraphSeries.superApply(this, 'formatTooltip', arguments); 50970 } 50971 }, 50972 50973 _updateCategoriesData: function () { 50974 var categories = map(this.option.categories || [], function (category) { 50975 // Data must has value 50976 return category.value != null ? category : extend({ 50977 value: 0 50978 }, category); 50979 }); 50980 var categoriesData = new List(['value'], this); 50981 categoriesData.initData(categories); 50982 50983 this._categoriesData = categoriesData; 50984 50985 this._categoriesModels = categoriesData.mapArray(function (idx) { 50986 return categoriesData.getItemModel(idx, true); 50987 }); 50988 }, 50989 50990 setZoom: function (zoom) { 50991 this.option.zoom = zoom; 50992 }, 50993 50994 setCenter: function (center) { 50995 this.option.center = center; 50996 }, 50997 50998 isAnimationEnabled: function () { 50999 return GraphSeries.superCall(this, 'isAnimationEnabled') 51000 // Not enable animation when do force layout 51001 && !(this.get('layout') === 'force' && this.get('force.layoutAnimation')); 51002 }, 51003 51004 defaultOption: { 51005 zlevel: 0, 51006 z: 2, 51007 51008 coordinateSystem: 'view', 51009
51010 // Default option for all coordinate systems 51011 // xAxisIndex: 0, 51012 // yAxisIndex: 0, 51013 // polarIndex: 0, 51014 // geoIndex: 0, 51015 51016 legendHoverLink: true, 51017 51018 hoverAnimation: true, 51019 51020 layout: null, 51021 51022 focusNodeAdjacency: false, 51023 51024 // Configuration of circular layout 51025 circular: { 51026 rotateLabel: false 51027 }, 51028 // Configuration of force directed layout 51029 force: { 51030 initLayout: null, 51031 // Node repulsion. Can be an array to represent range. 51032 repulsion: [0, 50], 51033 gravity: 0.1, 51034 51035 // Edge length. Can be an array to represent range. 51036 edgeLength: 30, 51037 51038 layoutAnimation: true 51039 }, 51040 51041 left: 'center', 51042 top: 'center', 51043 // right: null, 51044 // bottom: null, 51045 // width: '80%', 51046 // height: '80%', 51047 51048 symbol: 'circle', 51049 symbolSize: 10, 51050 51051 edgeSymbol: ['none', 'none'], 51052 edgeSymbolSize: 10, 51053 edgeLabel: { 51054 position: 'middle' 51055 }, 51056 51057 draggable: false, 51058 51059 roam: false, 51060 51061 // Default on center of graph 51062 center: null, 51063 51064 zoom: 1, 51065 // Symbol size scale ratio in roam 51066 nodeScaleRatio: 0.6, 51067 // cursor: null, 51068 51069 // categories: [], 51070 51071 // data: [] 51072 // Or 51073 // nodes: [] 51074 // 51075 // links: [] 51076 // Or 51077 // edges: [] 51078 51079 label: { 51080 show: false, 51081 formatter: '{b}' 51082 }, 51083 51084 itemStyle: {}, 51085 51086 lineStyle: { 51087 color: '#aaa', 51088 width: 1, 51089 curveness: 0, 51090 opacity: 0.5 51091 }, 51092 emphasis: { 51093 label: { 51094 show: true 51095 } 51096 } 51097 } 51098 }); 51099 51100 /* 51101* Licensed to the Apache Software Foundation (ASF) under one 51102* or more contributor license agreements. See the NOTICE file 51103* distributed with this work for additional information 51104* regarding copyright ownership. The ASF licenses this file 51105* to you under the Apache License, Version 2.0 (the 51106* "License"); you may not use this file except in compliance 51107* with the License. You may obtain a copy of the License at 51108* 51109* http://www.apache.org/licenses/LICENSE-2.0 51110* 51111* Unless required by applicable law or agreed to in writing, 51112* software distributed under the License is distributed on an 51113* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 51114* KIND, either express or implied. See the License for the 51115* specific language governing permissions and limitations 51116* under the License. 51117*/ 51118 51119 /** 51120 * Line path for bezier and straight line draw 51121 */ 51122 51123 var straightLineProto = Line.prototype; 51124 var bezierCurveProto = BezierCurve.prototype; 51125 51126 function isLine(shape) { 51127 return isNaN(+shape.cpx1) || isNaN(+shape.cpy1); 51128 } 51129 51130 var LinePath = extendShape({ 51131 51132 type: 'ec-line', 51133 51134 style: { 51135 stroke: '#000', 51136 fill: null 51137 }, 51138 51139 shape: { 51140 x1: 0, 51141 y1: 0, 51142 x2: 0, 51143 y2: 0, 51144 percent: 1, 51145 cpx1: null, 51146 cpy1: null 51147 }, 51148 51149 buildPath: function (ctx, shape) { 51150 (isLine(shape) ? straightLineProto : bezierCurveProto).buildPath(ctx, shape); 51151 }, 51152 51153 pointAt: function (t) { 51154 return isLine(this.shape) 51155 ? straightLineProto.pointAt.call(this, t) 51156 : bezierCurveProto.pointAt.call(this, t); 51157 }, 51158 51159 tangentAt: function (t) { 51160 var shape = this.shape; 51161 var p = isLine(shape) 51162 ? [shape.x2 - shape.x1, shape.y2 - shape.y1] 51163 : bezierCurveProto.tangentAt.call(this, t); 51164 return normalize(p, p); 51165 } 51166 });
51167 51168 /* 51169* Licensed to the Apache Software Foundation (ASF) under one 51170* or more contributor license agreements. See the NOTICE file 51171* distributed with this work for additional information 51172* regarding copyright ownership. The ASF licenses this file 51173* to you under the Apache License, Version 2.0 (the 51174* "License"); you may not use this file except in compliance 51175* with the License. You may obtain a copy of the License at 51176* 51177* http://www.apache.org/licenses/LICENSE-2.0 51178* 51179* Unless required by applicable law or agreed to in writing, 51180* software distributed under the License is distributed on an 51181* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 51182* KIND, either express or implied. See the License for the 51183* specific language governing permissions and limitations 51184* under the License. 51185*/ 51186 51187 /** 51188 * @module echarts/chart/helper/Line 51189 */ 51190 51191 var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol']; 51192 51193 function makeSymbolTypeKey(symbolCategory) { 51194 return '_' + symbolCategory + 'Type'; 51195 } 51196 /** 51197 * @inner 51198 */ 51199 function createSymbol$1(name, lineData, idx) { 51200 var color = lineData.getItemVisual(idx, 'color'); 51201 var symbolType = lineData.getItemVisual(idx, name); 51202 var symbolSize = lineData.getItemVisual(idx, name + 'Size'); 51203 51204 if (!symbolType || symbolType === 'none') { 51205 return; 51206 } 51207 51208 if (!isArray(symbolSize)) { 51209 symbolSize = [symbolSize, symbolSize]; 51210 } 51211 var symbolPath = createSymbol( 51212 symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2, 51213 symbolSize[0], symbolSize[1], color 51214 ); 51215 51216 symbolPath.name = name; 51217 51218 return symbolPath; 51219 } 51220 51221 function createLine(points) { 51222 var line = new LinePath({ 51223 name: 'line' 51224 }); 51225 setLinePoints(line.shape, points); 51226 return line; 51227 } 51228 51229 function setLinePoints(targetShape, points) { 51230 var p1 = points[0]; 51231 var p2 = points[1]; 51232 var cp1 = points[2]; 51233 targetShape.x1 = p1[0]; 51234 targetShape.y1 = p1[1]; 51235 targetShape.x2 = p2[0]; 51236 targetShape.y2 = p2[1]; 51237 targetShape.percent = 1; 51238 51239 if (cp1) { 51240 targetShape.cpx1 = cp1[0]; 51241 targetShape.cpy1 = cp1[1]; 51242 } 51243 else { 51244 targetShape.cpx1 = NaN; 51245 targetShape.cpy1 = NaN; 51246 } 51247 } 51248 51249 function updateSymbolAndLabelBeforeLineUpdate () { 51250 var lineGroup = this; 51251 var symbolFrom = lineGroup.childOfName('fromSymbol'); 51252 var symbolTo = lineGroup.childOfName('toSymbol'); 51253 var label = lineGroup.childOfName('label'); 51254 // Quick reject 51255 if (!symbolFrom && !symbolTo && label.ignore) { 51256 return; 51257 } 51258 51259 var invScale = 1; 51260 var parentNode = this.parent; 51261 while (parentNode) { 51262 if (parentNode.scale) { 51263 invScale /= parentNode.scale[0]; 51264 } 51265 parentNode = parentNode.parent; 51266 } 51267 51268 var line = lineGroup.childOfName('line'); 51269 // If line not changed 51270 // FIXME Parent scale changed 51271 if (!this.__dirty && !line.__dirty) { 51272 return; 51273 } 51274 51275 var percent = line.shape.percent; 51276 var fromPos = line.pointAt(0); 51277 var toPos = line.pointAt(percent); 51278 51279 var d = sub([], toPos, fromPos); 51280 normalize(d, d); 51281 51282 if (symbolFrom) { 51283 symbolFrom.attr('position', fromPos); 51284 var tangent = line.tangentAt(0); 51285 symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2( 51286 tangent[1], tangent[0] 51287 )); 51288 symbolFrom.attr('scale', [invScale * percent, invScale * percent]); 51289 } 51290 if (symbolTo) { 51291 symbolTo.attr('position', toPos); 51292 var tangent = line.tangentAt(1); 51293 symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2( 51294 tangent[1], tangent[0] 51295 )); 51296 symbolTo.attr('scale', [invScale * percent, invScale * percent]); 51297 } 51298 51299 if (!label.ignore) { 51300 label.attr('position', toPos); 51301 51302 var textPosition; 51303 var textAlign; 51304 var textVerticalAlign; 51305 51306 var distance$$1 = 5 * invScale; 51307 // End 51308 if (label.__position === 'end') { 51309 textPosition = [d[0] * distance$$1 + toPos[0], d[1] * distance$$1 + toPos[1]]; 51310 textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center'); 51311 textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle'); 51312 } 51313 // Middle 51314 else if (label.__position === 'middle') { 51315 var halfPercent = percent / 2; 51316 var tangent = line.tangentAt(halfPercent); 51317 var n = [tangent[1], -tangent[0]]; 51318 var cp = line.pointAt(halfPercent); 51319 if (n[1] > 0) { 51320 n[0] = -n[0]; 51321 n[1] = -n[1]; 51322 } 51323 textPosition = [cp[0] + n[0] * distance$$1, cp[1] + n[1] * distance$$1]; 51324 textAlign = 'center'; 51325 textVerticalAlign = 'bottom';
51326 var rotation = -Math.atan2(tangent[1], tangent[0]); 51327 if (toPos[0] < fromPos[0]) { 51328 rotation = Math.PI + rotation; 51329 } 51330 label.attr('rotation', rotation); 51331 } 51332 // Start 51333 else { 51334 textPosition = [-d[0] * distance$$1 + fromPos[0], -d[1] * distance$$1 + fromPos[1]]; 51335 textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center'); 51336 textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle'); 51337 } 51338 label.attr({ 51339 style: { 51340 // Use the user specified text align and baseline first 51341 textVerticalAlign: label.__verticalAlign || textVerticalAlign, 51342 textAlign: label.__textAlign || textAlign 51343 }, 51344 position: textPosition, 51345 scale: [invScale, invScale] 51346 }); 51347 } 51348 } 51349 51350 /** 51351 * @constructor 51352 * @extends {module:zrender/graphic/Group} 51353 * @alias {module:echarts/chart/helper/Line} 51354 */ 51355 function Line$1(lineData, idx, seriesScope) { 51356 Group.call(this); 51357 51358 this._createLine(lineData, idx, seriesScope); 51359 } 51360 51361 var lineProto = Line$1.prototype; 51362 51363// Update symbol position and rotation 51364 lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate; 51365 51366 lineProto._createLine = function (lineData, idx, seriesScope) { 51367 var seriesModel = lineData.hostModel; 51368 var linePoints = lineData.getItemLayout(idx); 51369 51370 var line = createLine(linePoints); 51371 line.shape.percent = 0; 51372 initProps(line, { 51373 shape: { 51374 percent: 1 51375 } 51376 }, seriesModel, idx); 51377 51378 this.add(line); 51379 51380 var label = new Text({ 51381 name: 'label' 51382 }); 51383 this.add(label); 51384 51385 each$1(SYMBOL_CATEGORIES, function (symbolCategory) { 51386 var symbol = createSymbol$1(symbolCategory, lineData, idx); 51387 // symbols must added after line to make sure 51388 // it will be updated after line#update. 51389 // Or symbol position and rotation update in line#beforeUpdate will be one frame slow 51390 this.add(symbol); 51391 this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory); 51392 }, this); 51393 51394 this._updateCommonStl(lineData, idx, seriesScope); 51395 }; 51396 51397 lineProto.updateData = function (lineData, idx, seriesScope) { 51398 var seriesModel = lineData.hostModel; 51399 51400 var line = this.childOfName('line'); 51401 var linePoints = lineData.getItemLayout(idx); 51402 var target = { 51403 shape: {} 51404 }; 51405 setLinePoints(target.shape, linePoints); 51406 updateProps(line, target, seriesModel, idx); 51407 51408 each$1(SYMBOL_CATEGORIES, function (symbolCategory) { 51409 var symbolType = lineData.getItemVisual(idx, symbolCategory); 51410 var key = makeSymbolTypeKey(symbolCategory); 51411 // Symbol changed 51412 if (this[key] !== symbolType) { 51413 this.remove(this.childOfName(symbolCategory)); 51414 var symbol = createSymbol$1(symbolCategory, lineData, idx); 51415 this.add(symbol); 51416 } 51417 this[key] = symbolType; 51418 }, this); 51419 51420 this._updateCommonStl(lineData, idx, seriesScope); 51421 }; 51422 51423 lineProto._updateCommonStl = function (lineData, idx, seriesScope) { 51424 var seriesModel = lineData.hostModel; 51425 51426 var line = this.childOfName('line'); 51427 51428 var lineStyle = seriesScope && seriesScope.lineStyle; 51429 var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle; 51430 var labelModel = seriesScope && seriesScope.labelModel; 51431 var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; 51432 51433 // Optimization for large dataset 51434 if (!seriesScope || lineData.hasItemOption) { 51435 var itemModel = lineData.getItemModel(idx); 51436 51437 lineStyle = itemModel.getModel('lineStyle').getLineStyle(); 51438 hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle(); 51439 51440 labelModel = itemModel.getModel('label'); 51441 hoverLabelModel = itemModel.getModel('emphasis.label'); 51442 } 51443 51444 var visualColor = lineData.getItemVisual(idx, 'color'); 51445 var visualOpacity = retrieve3( 51446 lineData.getItemVisual(idx, 'opacity'), 51447 lineStyle.opacity, 51448 1 51449 ); 51450 51451 line.useStyle(defaults( 51452 { 51453 strokeNoScale: true, 51454 fill: 'none', 51455 stroke: visualColor, 51456 opacity: visualOpacity 51457 }, 51458 lineStyle 51459 )); 51460 line.hoverStyle = hoverLineStyle; 51461 51462 // Update symbol 51463 each$1(SYMBOL_CATEGORIES, function (symbolCategory) { 51464 var symbol = this.childOfName(symbolCategory); 51465 if (symbol) { 51466 symbol.setColor(visualColor); 51467 symbol.setStyle({ 51468 opacity: visualOpacity 51469 }); 51470 } 51471 }, this); 51472 51473 var showLabel = labelModel.getShallow('show'); 51474 var hoverShowLabel = hoverLabelModel.getShallow('show'); 51475 51476 var label = this.childOfName('label'); 51477 var defaultLabelColor; 51478 var baseText; 51479 51480 // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`. 51481 if (showLabel || hoverShowLabel) { 51482 defaultLabelColor = visualColor || '#000'; 51483 51484 baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType); 51485 if (baseText == null) { 51486 var rawVal = seriesModel.getRawValue(idx); 51487 baseText = rawVal == null 51488 ? lineData.getName(idx) 51489 : isFinite(rawVal) 51490 ? round$1(rawVal) 51491 : rawVal; 51492 } 51493 } 51494 var normalText = showLabel ? baseText : null; 51495 var emphasisText = hoverShowLabel 51496 ? retrieve2( 51497 seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType), 51498 baseText 51499 ) 51500 : null; 51501 51502 var labelStyle = label.style; 51503 51504 // Always set `textStyle` even if `normalStyle.text` is null, because default 51505 // values have to be set on `normalStyle`. 51506 if (normalText != null || emphasisText != null) { 51507 setTextStyle(label.style, labelModel, { 51508 text: normalText 51509 }, { 51510 autoColor: defaultLabelColor 51511 }); 51512 51513 label.__textAlign = labelStyle.textAlign; 51514 label.__verticalAlign = labelStyle.textVerticalAlign; 51515 // 'start', 'middle', 'end' 51516 label.__position = labelModel.get('position') || 'middle'; 51517 } 51518 51519 if (emphasisText != null) { 51520 // Only these properties supported in this emphasis
51520style here. 51521 label.hoverStyle = { 51522 text: emphasisText, 51523 textFill: hoverLabelModel.getTextColor(true), 51524 // For merging hover style to normal style, do not use 51525 // `hoverLabelModel.getFont()` here. 51526 fontStyle: hoverLabelModel.getShallow('fontStyle'), 51527 fontWeight: hoverLabelModel.getShallow('fontWeight'), 51528 fontSize: hoverLabelModel.getShallow('fontSize'), 51529 fontFamily: hoverLabelModel.getShallow('fontFamily') 51530 }; 51531 } 51532 else { 51533 label.hoverStyle = { 51534 text: null 51535 }; 51536 } 51537 51538 label.ignore = !showLabel && !hoverShowLabel; 51539 51540 setHoverStyle(this); 51541 }; 51542 51543 lineProto.highlight = function () { 51544 this.trigger('emphasis'); 51545 }; 51546 51547 lineProto.downplay = function () { 51548 this.trigger('normal'); 51549 }; 51550 51551 lineProto.updateLayout = function (lineData, idx) { 51552 this.setLinePoints(lineData.getItemLayout(idx)); 51553 }; 51554 51555 lineProto.setLinePoints = function (points) { 51556 var linePath = this.childOfName('line'); 51557 setLinePoints(linePath.shape, points); 51558 linePath.dirty(); 51559 }; 51560 51561 inherits(Line$1, Group); 51562 51563 /* 51564* Licensed to the Apache Software Foundation (ASF) under one 51565* or more contributor license agreements. See the NOTICE file 51566* distributed with this work for additional information 51567* regarding copyright ownership. The ASF licenses this file 51568* to you under the Apache License, Version 2.0 (the 51569* "License"); you may not use this file except in compliance 51570* with the License. You may obtain a copy of the License at 51571* 51572* http://www.apache.org/licenses/LICENSE-2.0 51573* 51574* Unless required by applicable law or agreed to in writing, 51575* software distributed under the License is distributed on an 51576* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 51577* KIND, either express or implied. See the License for the 51578* specific language governing permissions and limitations 51579* under the License. 51580*/ 51581 51582 /** 51583 * @module echarts/chart/helper/LineDraw 51584 */ 51585 51586// import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable'; 51587 51588 /** 51589 * @alias module:echarts/component/marker/LineDraw 51590 * @constructor 51591 */ 51592 function LineDraw(ctor) { 51593 this._ctor = ctor || Line$1; 51594 51595 this.group = new Group(); 51596 } 51597 51598 var lineDrawProto = LineDraw.prototype; 51599 51600 lineDrawProto.isPersistent = function () { 51601 return true; 51602 }; 51603 51604 /** 51605 * @param {module:echarts/data/List} lineData 51606 */ 51607 lineDrawProto.updateData = function (lineData) { 51608 var lineDraw = this; 51609 var group = lineDraw.group; 51610 51611 var oldLineData = lineDraw._lineData; 51612 lineDraw._lineData = lineData; 51613 51614 // There is no oldLineData only when first rendering or switching from 51615 // stream mode to normal mode, where previous elements should be removed. 51616 if (!oldLineData) { 51617 group.removeAll(); 51618 } 51619 51620 var seriesScope = makeSeriesScope$1(lineData); 51621 51622 lineData.diff(oldLineData) 51623 .add(function (idx) { 51624 doAdd(lineDraw, lineData, idx, seriesScope); 51625 }) 51626 .update(function (newIdx, oldIdx) { 51627 doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope); 51628 }) 51629 .remove(function (idx) { 51630 group.remove(oldLineData.getItemGraphicEl(idx)); 51631 }) 51632 .execute(); 51633 }; 51634 51635 function doAdd(lineDraw, lineData, idx, seriesScope) { 51636 var itemLayout = lineData.getItemLayout(idx); 51637 51638 if (!lineNeedsDraw(itemLayout)) { 51639 return; 51640 } 51641 51642 var el = new lineDraw._ctor(lineData, idx, seriesScope); 51643 lineData.setItemGraphicEl(idx, el); 51644 lineDraw.group.add(el); 51645 } 51646 51647 function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScope) { 51648 var itemEl = oldLineData.getItemGraphicEl(oldIdx); 51649 51650 if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) { 51651 lineDraw.group.remove(itemEl); 51652 return; 51653 } 51654 51655 if (!itemEl) { 51656 itemEl = new lineDraw._ctor(newLineData, newIdx, seriesScope); 51657 } 51658 else { 51659 itemEl.updateData(newLineData, newIdx, seriesScope); 51660 } 51661 51662 newLineData.setItemGraphicEl(newIdx, itemEl); 51663 51664 lineDraw.group.add(itemEl); 51665 } 51666 51667 lineDrawProto.updateLayout = function () { 51668 var lineData = this._lineData; 51669
51670 // Do not support update layout in incremental mode. 51671 if (!lineData) { 51672 return; 51673 } 51674 51675 lineData.eachItemGraphicEl(function (el, idx) { 51676 el.updateLayout(lineData, idx); 51677 }, this); 51678 }; 51679 51680 lineDrawProto.incrementalPrepareUpdate = function (lineData) { 51681 this._seriesScope = makeSeriesScope$1(lineData); 51682 this._lineData = null; 51683 this.group.removeAll(); 51684 }; 51685 51686 lineDrawProto.incrementalUpdate = function (taskParams, lineData) { 51687 function updateIncrementalAndHover(el) { 51688 if (!el.isGroup) { 51689 el.incremental = el.useHoverLayer = true; 51690 } 51691 } 51692 51693 for (var idx = taskParams.start; idx < taskParams.end; idx++) { 51694 var itemLayout = lineData.getItemLayout(idx); 51695 51696 if (lineNeedsDraw(itemLayout)) { 51697 var el = new this._ctor(lineData, idx, this._seriesScope); 51698 el.traverse(updateIncrementalAndHover); 51699 51700 this.group.add(el); 51701 lineData.setItemGraphicEl(idx, el); 51702 } 51703 } 51704 }; 51705 51706 function makeSeriesScope$1(lineData) { 51707 var hostModel = lineData.hostModel; 51708 return { 51709 lineStyle: hostModel.getModel('lineStyle').getLineStyle(), 51710 hoverLineStyle: hostModel.getModel('emphasis.lineStyle').getLineStyle(), 51711 labelModel: hostModel.getModel('label'), 51712 hoverLabelModel: hostModel.getModel('emphasis.label') 51713 }; 51714 } 51715 51716 lineDrawProto.remove = function () { 51717 this._clearIncremental(); 51718 this._incremental = null; 51719 this.group.removeAll(); 51720 }; 51721 51722 lineDrawProto._clearIncremental = function () { 51723 var incremental = this._incremental; 51724 if (incremental) { 51725 incremental.clearDisplaybles(); 51726 } 51727 }; 51728 51729 function isPointNaN(pt) { 51730 return isNaN(pt[0]) || isNaN(pt[1]); 51731 } 51732 51733 function lineNeedsDraw(pts) { 51734 return !isPointNaN(pts[0]) && !isPointNaN(pts[1]); 51735 } 51736 51737 /* 51738* Licensed to the Apache Software Foundation (ASF) under one 51739* or more contributor license agreements. See the NOTICE file 51740* distributed with this work for additional information 51741* regarding copyright ownership. The ASF licenses this file 51742* to you under the Apache License, Version 2.0 (the 51743* "License"); you may not use this file except in compliance 51744* with the License. You may obtain a copy of the License at 51745* 51746* http://www.apache.org/licenses/LICENSE-2.0 51747* 51748* Unless required by applicable law or agreed to in writing, 51749* software distributed under the License is distributed on an 51750* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 51751* KIND, either express or implied. See the License for the 51752* specific language governing permissions and limitations 51753* under the License. 51754*/ 51755 51756 var v1 = []; 51757 var v2 = []; 51758 var v3 = []; 51759 var quadraticAt$1 = quadraticAt; 51760 var v2DistSquare = distSquare; 51761 var mathAbs$1 = Math.abs; 51762 function intersectCurveCircle(curvePoints, center, radius) { 51763 var p0 = curvePoints[0]; 51764 var p1 = curvePoints[1]; 51765 var p2 = curvePoints[2]; 51766 51767 var d = Infinity; 51768 var t; 51769 var radiusSquare = radius * radius; 51770 var interval = 0.1; 51771 51772 for (var _t = 0.1; _t <= 0.9; _t += 0.1) { 51773 v1[0] = quadraticAt$1(p0[0], p1[0], p2[0], _t); 51774 v1[1] = quadraticAt$1(p0[1], p1[1], p2[1], _t); 51775 var diff = mathAbs$1(v2DistSquare(v1, center) - radiusSquare); 51776 if (diff < d) { 51777 d = diff; 51778 t = _t; 51779 } 51780 } 51781 51782 // Assume the segment is monotoneï¼ÅFind root through Bisection method 51783 // At most 32 iteration 51784 for (var i = 0; i < 32; i++) { 51785 // var prev = t - interval; 51786 var next = t + interval; 51787 // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev); 51788 // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev); 51789 v2[0] = quadraticAt$1(p0[0], p1[0], p2[0], t); 51790 v2[1] = quadraticAt$1(p0[1], p1[1], p2[1], t); 51791 v3[0] = quadraticAt$1(p0[0], p1[0], p2[0], next); 51792 v3[1] = quadraticAt$1(p0[1], p1[1], p2[1], next); 51793
51794 var diff = v2DistSquare(v2, center) - radiusSquare; 51795 if (mathAbs$1(diff) < 1e-2) { 51796 break; 51797 } 51798 51799 // var prevDiff = v2DistSquare(v1, center) - radiusSquare; 51800 var nextDiff = v2DistSquare(v3, center) - radiusSquare; 51801 51802 interval /= 2; 51803 if (diff < 0) { 51804 if (nextDiff >= 0) { 51805 t = t + interval; 51806 } 51807 else { 51808 t = t - interval; 51809 } 51810 } 51811 else { 51812 if (nextDiff >= 0) { 51813 t = t - interval; 51814 } 51815 else { 51816 t = t + interval; 51817 } 51818 } 51819 } 51820 51821 return t; 51822 } 51823 51824// Adjust edge to avoid 51825 var adjustEdge = function (graph, scale$$1) { 51826 var tmp0 = []; 51827 var quadraticSubdivide$$1 = quadraticSubdivide; 51828 var pts = [[], [], []]; 51829 var pts2 = [[], []]; 51830 var v = []; 51831 scale$$1 /= 2; 51832 51833 function getSymbolSize(node) { 51834 var symbolSize = node.getVisual('symbolSize'); 51835 if (symbolSize instanceof Array) { 51836 symbolSize = (symbolSize[0] + symbolSize[1]) / 2; 51837 } 51838 return symbolSize; 51839 } 51840 graph.eachEdge(function (edge, idx) { 51841 var linePoints = edge.getLayout(); 51842 var fromSymbol = edge.getVisual('fromSymbol'); 51843 var toSymbol = edge.getVisual('toSymbol'); 51844 51845 if (!linePoints.__original) { 51846 linePoints.__original = [ 51847 clone$1(linePoints[0]), 51848 clone$1(linePoints[1]) 51849 ]; 51850 if (linePoints[2]) { 51851 linePoints.__original.push(clone$1(linePoints[2])); 51852 } 51853 } 51854 var originalPoints = linePoints.__original; 51855 // Quadratic curve 51856 if (linePoints[2] != null) { 51857 copy(pts[0], originalPoints[0]); 51858 copy(pts[1], originalPoints[2]); 51859 copy(pts[2], originalPoints[1]); 51860 if (fromSymbol && fromSymbol != 'none') { 51861 var symbolSize = getSymbolSize(edge.node1); 51862 51863 var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale$$1); 51864 // Subdivide and get the second 51865 quadraticSubdivide$$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0); 51866 pts[0][0] = tmp0[3]; 51867 pts[1][0] = tmp0[4]; 51868 quadraticSubdivide$$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0); 51869 pts[0][1] = tmp0[3]; 51870 pts[1][1] = tmp0[4]; 51871 } 51872 if (toSymbol && toSymbol != 'none') { 51873 var symbolSize = getSymbolSize(edge.node2); 51874 51875 var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale$$1); 51876 // Subdivide and get the first 51877 quadraticSubdivide$$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0); 51878 pts[1][0] = tmp0[1]; 51879 pts[2][0] = tmp0[2]; 51880 quadraticSubdivide$$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0); 51881 pts[1][1] = tmp0[1]; 51882 pts[2][1] = tmp0[2]; 51883 } 51884 // Copy back to layout 51885 copy(linePoints[0], pts[0]); 51886 copy(linePoints[1], pts[2]); 51887 copy(linePoints[2], pts[1]); 51888 } 51889 // Line 51890 else { 51891 copy(pts2[0], originalPoints[0]); 51892 copy(pts2[1], originalPoints[1]); 51893 51894 sub(v, pts2[1], pts2[0]); 51895 normalize(v, v); 51896 if (fromSymbol && fromSymbol != 'none') { 51897 51898 var symbolSize = getSymbolSize(edge.node1); 51899 51900 scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale$$1); 51901 } 51902 if (toSymbol && toSymbol != 'none') { 51903 var symbolSize = getSymbolSize(edge.node2); 51904 51905 scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale$$1); 51906 } 51907 copy(linePoints[0], pts2[0]); 51908 copy(linePoints[1], pts2[1]); 51909 } 51910 }); 51911 }; 51912 51913 /* 51914* Licensed to the Apache Software Foundation (ASF) under one 51915* or more contributor license agreements. See the NOTICE file 51916* distributed with this work for additional information 51917* regarding copyright ownership. The ASF licenses this file 51918* to you under the Apache License, Version 2.0 (the 51919* "License");
51919 you may not use this file except in compliance 51920* with the License. You may obtain a copy of the License at 51921* 51922* http://www.apache.org/licenses/LICENSE-2.0 51923* 51924* Unless required by applicable law or agreed to in writing, 51925* software distributed under the License is distributed on an 51926* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 51927* KIND, either express or implied. See the License for the 51928* specific language governing permissions and limitations 51929* under the License. 51930*/ 51931 51932 var nodeOpacityPath = ['itemStyle', 'opacity']; 51933 var lineOpacityPath = ['lineStyle', 'opacity']; 51934 51935 function getItemOpacity(item, opacityPath) { 51936 return item.getVisual('opacity') || item.getModel().get(opacityPath); 51937 } 51938 51939 function fadeOutItem(item, opacityPath, opacityRatio) { 51940 var el = item.getGraphicEl(); 51941 51942 var opacity = getItemOpacity(item, opacityPath); 51943 if (opacityRatio != null) { 51944 opacity == null && (opacity = 1); 51945 opacity *= opacityRatio; 51946 } 51947 51948 el.downplay && el.downplay(); 51949 el.traverse(function (child) { 51950 if (child.type !== 'group') { 51951 child.setStyle('opacity', opacity); 51952 } 51953 }); 51954 } 51955 51956 function fadeInItem(item, opacityPath) { 51957 var opacity = getItemOpacity(item, opacityPath); 51958 var el = item.getGraphicEl(); 51959 51960 el.highlight && el.highlight(); 51961 el.traverse(function (child) { 51962 if (child.type !== 'group') { 51963 child.setStyle('opacity', opacity); 51964 } 51965 }); 51966 } 51967 51968 extendChartView({ 51969 51970 type: 'graph', 51971 51972 init: function (ecModel, api) { 51973 var symbolDraw = new SymbolDraw(); 51974 var lineDraw = new LineDraw(); 51975 var group = this.group; 51976 51977 this._controller = new RoamController(api.getZr()); 51978 this._controllerHost = {target: group}; 51979 51980 group.add(symbolDraw.group); 51981 group.add(lineDraw.group); 51982 51983 this._symbolDraw = symbolDraw; 51984 this._lineDraw = lineDraw; 51985 51986 this._firstRender = true; 51987 }, 51988 51989 render: function (seriesModel, ecModel, api) { 51990 var coordSys = seriesModel.coordinateSystem; 51991 51992 this._model = seriesModel; 51993 this._nodeScaleRatio = seriesModel.get('nodeScaleRatio'); 51994 51995 var symbolDraw = this._symbolDraw; 51996 var lineDraw = this._lineDraw; 51997 51998 var group = this.group; 51999 52000 if (coordSys.type === 'view') { 52001 var groupNewProp = { 52002 position: coordSys.position, 52003 scale: coordSys.scale 52004 }; 52005 if (this._firstRender) { 52006 group.attr(groupNewProp); 52007 } 52008 else { 52009 updateProps(group, groupNewProp, seriesModel); 52010 } 52011 } 52012 // Fix edge contact point with node 52013 adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); 52014 52015 var data = seriesModel.getData(); 52016 symbolDraw.updateData(data); 52017 52018 var edgeData = seriesModel.getEdgeData(); 52019 lineDraw.updateData(edgeData); 52020 52021 this._updateNodeAndLinkScale(); 52022 52023 this._updateController(seriesModel, ecModel, api); 52024 52025 clearTimeout(this._layoutTimeout); 52026 var forceLayout = seriesModel.forceLayout; 52027 var layoutAnimation = seriesModel.get('force.layoutAnimation'); 52028 if (forceLayout) { 52029 this._startForceLayoutIteration(forceLayout, layoutAnimation); 52030 } 52031 52032 data.eachItemGraphicEl(function (el, idx) { 52033 var itemModel = data.getItemModel(idx); 52034 // Update draggable 52035 el.off('drag').off('dragend'); 52036 var draggable = data.getItemModel(idx).get('draggable'); 52037 if (draggable) { 52038 el.on('drag', function () { 52039 if (forceLayout) { 52040 forceLayout.warmUp();
52041 !this._layouting 52042 && this._startForceLayoutIteration(forceLayout, layoutAnimation); 52043 forceLayout.setFixed(idx); 52044 // Write position back to layout 52045 data.setItemLayout(idx, el.position); 52046 } 52047 }, this).on('dragend', function () { 52048 if (forceLayout) { 52049 forceLayout.setUnfixed(idx); 52050 } 52051 }, this); 52052 } 52053 el.setDraggable(draggable && forceLayout); 52054 52055 el.off('mouseover', el.__focusNodeAdjacency); 52056 el.off('mouseout', el.__unfocusNodeAdjacency); 52057 52058 if (itemModel.get('focusNodeAdjacency')) { 52059 el.on('mouseover', el.__focusNodeAdjacency = function () { 52060 api.dispatchAction({ 52061 type: 'focusNodeAdjacency', 52062 seriesId: seriesModel.id, 52063 dataIndex: el.dataIndex 52064 }); 52065 }); 52066 el.on('mouseout', el.__unfocusNodeAdjacency = function () { 52067 api.dispatchAction({ 52068 type: 'unfocusNodeAdjacency', 52069 seriesId: seriesModel.id 52070 }); 52071 }); 52072 52073 } 52074 52075 }, this); 52076 52077 data.graph.eachEdge(function (edge) { 52078 var el = edge.getGraphicEl(); 52079 52080 el.off('mouseover', el.__focusNodeAdjacency); 52081 el.off('mouseout', el.__unfocusNodeAdjacency); 52082 52083 if (edge.getModel().get('focusNodeAdjacency')) { 52084 el.on('mouseover', el.__focusNodeAdjacency = function () { 52085 api.dispatchAction({ 52086 type: 'focusNodeAdjacency', 52087 seriesId: seriesModel.id, 52088 edgeDataIndex: edge.dataIndex 52089 }); 52090 }); 52091 el.on('mouseout', el.__unfocusNodeAdjacency = function () { 52092 api.dispatchAction({ 52093 type: 'unfocusNodeAdjacency', 52094 seriesId: seriesModel.id 52095 }); 52096 }); 52097 } 52098 }); 52099 52100 var circularRotateLabel = seriesModel.get('layout') === 'circular' 52101 && seriesModel.get('circular.rotateLabel'); 52102 var cx = data.getLayout('cx'); 52103 var cy = data.getLayout('cy'); 52104 data.eachItemGraphicEl(function (el, idx) { 52105 var symbolPath = el.getSymbolPath(); 52106 if (circularRotateLabel) { 52107 var pos = data.getItemLayout(idx); 52108 var rad = Math.atan2(pos[1] - cy, pos[0] - cx); 52109 if (rad < 0) { 52110 rad = Math.PI * 2 + rad; 52111 } 52112 var isLeft = pos[0] < cx; 52113 if (isLeft) { 52114 rad = rad - Math.PI; 52115 } 52116 var textPosition = isLeft ? 'left' : 'right'; 52117 symbolPath.setStyle({ 52118 textRotation: -rad, 52119 textPosition: textPosition, 52120 textOrigin: 'center' 52121 }); 52122 symbolPath.hoverStyle && (symbolPath.hoverStyle.textPosition = textPosition); 52123 } 52124 else { 52125 symbolPath.setStyle({ 52126 textRotation: 0 52127 }); 52128 } 52129 }); 52130 52131 this._firstRender = false; 52132 }, 52133 52134 dispose: function () { 52135 this._controller && this._controller.dispose(); 52136 this._controllerHost = {}; 52137 }, 52138 52139 focusNodeAdjacency: function (seriesModel, ecModel, api, payload) { 52140 var data = this._model.getData(); 52141 var graph = data.graph; 52142 var dataIndex = payload.dataIndex; 52143 var edgeDataIndex = payload.edgeDataIndex; 52144 52145 var node = graph.getNodeByIndex(dataIndex); 52146 var edge = graph.getEdgeByIndex(edgeDataIndex); 52147 52148 if (!node && !edge) { 52149 return; 52150 } 52151 52152 graph.eachNode(function (node) { 52153 fadeOutItem(node, nodeOpacityPath, 0.1); 52154 }); 52155 graph.eachEdge(function (edge) { 52156 fadeOutItem(edge, lineOpacityPath, 0.1); 52157 }); 52158 52159 if (node) { 52160 fadeInItem(node, nodeOpacityPath); 52161 each$1(node.edges, function (adjacentEdge) { 52162 if (adjacentEdge.dataIndex < 0) { 52163 return; 52164 } 52165 fadeInItem(adjacentEdge, lineOpacityPath); 52166 fadeInItem(adjacentEdge.node1, nodeOpacityPath); 52167 fadeInItem(adjacentEdge.node2, nodeOpacityPath); 52168 }); 52169 } 52170 if (edge) { 52171 fadeInItem(edge, lineOpacityPath); 52172 fadeInItem(edge.node1, nodeOpacityPath); 52173 fadeInItem(edge.node2, nodeOpacityPath); 52174 } 52175 }, 52176 52177 unfocusNodeAdjacency: function (seriesModel, ecModel, api, payload) { 52178 var graph = this._model.getData().graph; 52179 52180 graph.eachNode(function (node) { 52181 fadeOutItem(node, nodeOpacityPath); 52182 }); 52183 graph.eachEdge(function (edge) { 52184 fadeOutItem(edge, lineOpacityPath); 52185 }); 52186 }, 52187 52188 _startForceLayoutIteration: function (forceLayout, layoutAnimation) { 52189 var self = this; 52190 (function step() { 52191 forceLayout.step(function (stopped) { 52192 self.updateLayout(self._model); 52193 (self._layouting = !stopped) && ( 52194 layoutAnimation 52195 ? (self._layoutTimeout = setTimeout(step, 16)) 52196 : step() 52197 ); 52198 }); 52199 })(); 52200 }, 52201 52202 _updateController: function (seriesModel, ecModel, api) { 52203 var controller = this._controller; 52204 var controllerHost = this._controllerHost; 52205 var group = this.group; 52206 52207 controller.setPointerChecker(function (e, x, y) {
52208 var rect = group.getBoundingRect(); 52209 rect.applyTransform(group.transform); 52210 return rect.contain(x, y) 52211 && !onIrrelevantElement(e, api, seriesModel); 52212 }); 52213 52214 if (seriesModel.coordinateSystem.type !== 'view') { 52215 controller.disable(); 52216 return; 52217 } 52218 controller.enable(seriesModel.get('roam')); 52219 controllerHost.zoomLimit = seriesModel.get('scaleLimit'); 52220 controllerHost.zoom = seriesModel.coordinateSystem.getZoom(); 52221 52222 controller 52223 .off('pan') 52224 .off('zoom') 52225 .on('pan', function (dx, dy) { 52226 updateViewOnPan(controllerHost, dx, dy); 52227 api.dispatchAction({ 52228 seriesId: seriesModel.id, 52229 type: 'graphRoam', 52230 dx: dx, 52231 dy: dy 52232 }); 52233 }) 52234 .on('zoom', function (zoom, mouseX, mouseY) { 52235 updateViewOnZoom(controllerHost, zoom, mouseX, mouseY); 52236 api.dispatchAction({ 52237 seriesId: seriesModel.id, 52238 type: 'graphRoam', 52239 zoom: zoom, 52240 originX: mouseX, 52241 originY: mouseY 52242 }); 52243 this._updateNodeAndLinkScale(); 52244 adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); 52245 this._lineDraw.updateLayout(); 52246 }, this); 52247 }, 52248 52249 _updateNodeAndLinkScale: function () { 52250 var seriesModel = this._model; 52251 var data = seriesModel.getData(); 52252 52253 var nodeScale = this._getNodeGlobalScale(seriesModel); 52254 var invScale = [nodeScale, nodeScale]; 52255 52256 data.eachItemGraphicEl(function (el, idx) { 52257 el.attr('scale', invScale); 52258 }); 52259 }, 52260 52261 _getNodeGlobalScale: function (seriesModel) { 52262 var coordSys = seriesModel.coordinateSystem; 52263 if (coordSys.type !== 'view') { 52264 return 1; 52265 } 52266 52267 var nodeScaleRatio = this._nodeScaleRatio; 52268 52269 var groupScale = coordSys.scale; 52270 var groupZoom = (groupScale && groupScale[0]) || 1; 52271 // Scale node when zoom changes 52272 var roamZoom = coordSys.getZoom(); 52273 var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1; 52274 52275 return nodeScale / groupZoom; 52276 }, 52277 52278 updateLayout: function (seriesModel) { 52279 adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); 52280 52281 this._symbolDraw.updateLayout(); 52282 this._lineDraw.updateLayout(); 52283 }, 52284 52285 remove: function (ecModel, api) { 52286 this._symbolDraw && this._symbolDraw.remove(); 52287 this._lineDraw && this._lineDraw.remove(); 52288 } 52289 }); 52290 52291 /* 52292* Licensed to the Apache Software Foundation (ASF) under one 52293* or more contributor license agreements. See the NOTICE file 52294* distributed with this work for additional information 52295* regarding copyright ownership. The ASF licenses this file 52296* to you under the Apache License, Version 2.0 (the 52297* "License"); you may not use this file except in compliance 52298* with the License. You may obtain a copy of the License at 52299* 52300* http://www.apache.org/licenses/LICENSE-2.0 52301* 52302* Unless required by applicable law or agreed to in writing, 52303* software distributed under the License is distributed on an 52304* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52305* KIND, either express or implied. See the License for the 52306* specific language governing permissions and limitations 52307* under the License. 52308*/ 52309 52310 var actionInfo = { 52311 type: 'graphRoam', 52312 event: 'graphRoam', 52313 update: 'none' 52314 }; 52315 52316 /** 52317 * @payload 52318 * @property {string} name Series name 52319 * @property {number} [dx] 52320 * @property {number} [dy] 52321 * @property {number} [zoom] 52322 * @property {number} [originX] 52323 * @property {number} [originY] 52324 */ 52325 registerAction(actionInfo, function (payload, ecModel) {
52326 ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) { 52327 var coordSys = seriesModel.coordinateSystem; 52328 52329 var res = updateCenterAndZoom(coordSys, payload); 52330 52331 seriesModel.setCenter 52332 && seriesModel.setCenter(res.center); 52333 52334 seriesModel.setZoom 52335 && seriesModel.setZoom(res.zoom); 52336 }); 52337 }); 52338 52339 52340 /** 52341 * @payload 52342 * @property {number} [seriesIndex] 52343 * @property {string} [seriesId] 52344 * @property {string} [seriesName] 52345 * @property {number} [dataIndex] 52346 */ 52347 registerAction({ 52348 type: 'focusNodeAdjacency', 52349 event: 'focusNodeAdjacency', 52350 update: 'series.graph:focusNodeAdjacency' 52351 }, function () {}); 52352 52353 /** 52354 * @payload 52355 * @property {number} [seriesIndex] 52356 * @property {string} [seriesId] 52357 * @property {string} [seriesName] 52358 */ 52359 registerAction({ 52360 type: 'unfocusNodeAdjacency', 52361 event: 'unfocusNodeAdjacency', 52362 update: 'series.graph:unfocusNodeAdjacency' 52363 }, function () {}); 52364 52365 /* 52366* Licensed to the Apache Software Foundation (ASF) under one 52367* or more contributor license agreements. See the NOTICE file 52368* distributed with this work for additional information 52369* regarding copyright ownership. The ASF licenses this file 52370* to you under the Apache License, Version 2.0 (the 52371* "License"); you may not use this file except in compliance 52372* with the License. You may obtain a copy of the License at 52373* 52374* http://www.apache.org/licenses/LICENSE-2.0 52375* 52376* Unless required by applicable law or agreed to in writing, 52377* software distributed under the License is distributed on an 52378* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52379* KIND, either express or implied. See the License for the 52380* specific language governing permissions and limitations 52381* under the License. 52382*/ 52383 52384 52385 var categoryFilter = function (ecModel) { 52386 var legendModels = ecModel.findComponents({ 52387 mainType: 'legend' 52388 }); 52389 if (!legendModels || !legendModels.length) { 52390 return; 52391 } 52392 ecModel.eachSeriesByType('graph', function (graphSeries) { 52393 var categoriesData = graphSeries.getCategoriesData(); 52394 var graph = graphSeries.getGraph(); 52395 var data = graph.data; 52396 52397 var categoryNames = categoriesData.mapArray(categoriesData.getName); 52398 52399 data.filterSelf(function (idx) { 52400 var model = data.getItemModel(idx); 52401 var category = model.getShallow('category'); 52402 if (category != null) { 52403 if (typeof category === 'number') { 52404 category = categoryNames[category]; 52405 } 52406 // If in any legend component the status is not selected. 52407 for (var i = 0; i < legendModels.length; i++) { 52408 if (!legendModels[i].isSelected(category)) { 52409 return false; 52410 } 52411 } 52412 } 52413 return true; 52414 }); 52415 }, this); 52416 }; 52417 52418 /* 52419* Licensed to the Apache Software Foundation (ASF) under one 52420* or more contributor license agreements. See the NOTICE file 52421* distributed with this work for additional information 52422* regarding copyright ownership. The ASF licenses this file 52423* to you under the Apache License, Version 2.0 (the 52424* "License"); you may not use this file except in compliance 52425* with the License. You may obtain a copy of the License at 52426* 52427* http://www.apache.org/licenses/LICENSE-2.0 52428* 52429* Unless required by applicable law or agreed to in writing, 52430* software distributed under the License is distributed on an 52431* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52432* KIND, either express or implied. See the License for the 52433* specific language governing permissions and limitations 52434* under the License. 52435*/ 52436 52437 52438 var categoryVisual = function (ecModel) { 52439 52440 var paletteScope = {}; 52441 ecModel.eachSeriesByType('graph', function (seriesModel) { 52442 var categoriesData = seriesModel.getCategoriesData(); 52443 var data = seriesModel.getData(); 52444 52445 var categoryNameIdxMap = {}; 52446 52447 categoriesData.each(function (idx) { 52448 var name = categoriesData.getName(idx); 52449 // Add prefix to avoid conflict with Object.prototype. 52450 categoryNameIdxMap['ec-' + name] = idx; 52451 52452 var itemModel = categoriesData.getItemModel(idx); 52453 var color = itemModel.get('itemStyle.color') 52454 || seriesModel.getColorFromPalette(name, paletteScope); 52455 categoriesData.setItemVisual(idx, 'color', color); 52456 }); 52457 52458 // Assign category color to visual 52459 if (categoriesData.count()) { 52460 data.each(function (idx) { 52461 var model = data.getItemModel(idx); 52462 var category = model.getShallow('category'); 52463 if (category != null) { 52464 if (typeof category === 'string') { 52465 category = categoryNameIdxMap['ec-' + category]; 52466 } 52467 if (!data.getItemVisual(idx, 'color', true)) { 52468 data.setItemVisual( 52469 idx, 'color', 52470 categoriesData.getItemVisual(category, 'color') 52471 ); 52472 } 52473 } 52474 }); 52475 } 52476 }); 52477 }; 52478 52479 /* 52480* Licensed to the Apache Software Foundation (ASF) under one 52481* or more contributor license agreements. See the NOTICE file 52482* distributed with this work for additional information 52483* regarding copyright ownership. The ASF licenses this file 52484* to you under the Apache License, Version 2.0 (the 52485* "License");
52485 you may not use this file except in compliance 52486* with the License. You may obtain a copy of the License at 52487* 52488* http://www.apache.org/licenses/LICENSE-2.0 52489* 52490* Unless required by applicable law or agreed to in writing, 52491* software distributed under the License is distributed on an 52492* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52493* KIND, either express or implied. See the License for the 52494* specific language governing permissions and limitations 52495* under the License. 52496*/ 52497 52498 52499 function normalize$1(a) { 52500 if (!(a instanceof Array)) { 52501 a = [a, a]; 52502 } 52503 return a; 52504 } 52505 52506 var edgeVisual = function (ecModel) { 52507 ecModel.eachSeriesByType('graph', function (seriesModel) { 52508 var graph = seriesModel.getGraph(); 52509 var edgeData = seriesModel.getEdgeData(); 52510 var symbolType = normalize$1(seriesModel.get('edgeSymbol')); 52511 var symbolSize = normalize$1(seriesModel.get('edgeSymbolSize')); 52512 52513 var colorQuery = 'lineStyle.color'.split('.'); 52514 var opacityQuery = 'lineStyle.opacity'.split('.'); 52515 52516 edgeData.setVisual('fromSymbol', symbolType && symbolType[0]); 52517 edgeData.setVisual('toSymbol', symbolType && symbolType[1]); 52518 edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); 52519 edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]); 52520 edgeData.setVisual('color', seriesModel.get(colorQuery)); 52521 edgeData.setVisual('opacity', seriesModel.get(opacityQuery)); 52522 52523 edgeData.each(function (idx) { 52524 var itemModel = edgeData.getItemModel(idx); 52525 var edge = graph.getEdgeByIndex(idx); 52526 var symbolType = normalize$1(itemModel.getShallow('symbol', true)); 52527 var symbolSize = normalize$1(itemModel.getShallow('symbolSize', true)); 52528 // Edge visual must after node visual 52529 var color = itemModel.get(colorQuery); 52530 var opacity = itemModel.get(opacityQuery); 52531 switch (color) { 52532 case 'source': 52533 color = edge.node1.getVisual('color'); 52534 break; 52535 case 'target': 52536 color = edge.node2.getVisual('color'); 52537 break; 52538 } 52539 52540 symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]); 52541 symbolType[1] && edge.setVisual('toSymbol', symbolType[1]); 52542 symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]); 52543 symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]); 52544 52545 edge.setVisual('color', color); 52546 edge.setVisual('opacity', opacity); 52547 }); 52548 }); 52549 }; 52550 52551 /* 52552* Licensed to the Apache Software Foundation (ASF) under one 52553* or more contributor license agreements. See the NOTICE file 52554* distributed with this work for additional information 52555* regarding copyright ownership. The ASF licenses this file 52556* to you under the Apache License, Version 2.0 (the 52557* "License"); you may not use this file except in compliance 52558* with the License. You may obtain a copy of the License at 52559* 52560* http://www.apache.org/licenses/LICENSE-2.0 52561* 52562* Unless required by applicable law or agreed to in writing, 52563* software distributed under the License is distributed on an 52564* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52565* KIND, either express or implied. See the License for the 52566* specific language governing permissions and limitations 52567* under the License. 52568*/ 52569 52570 function simpleLayout$1(seriesModel) { 52571 var coordSys = seriesModel.coordinateSystem; 52572 if (coordSys && coordSys.type !== 'view') { 52573 return; 52574 } 52575 var graph = seriesModel.getGraph(); 52576 52577 graph.eachNode(function (node) { 52578 var model = node.getModel(); 52579 node.setLayout([+model.get('x'), +model.get('y')]); 52580 }); 52581
52582 simpleLayoutEdge(graph); 52583 } 52584 52585 function simpleLayoutEdge(graph) { 52586 graph.eachEdge(function (edge) { 52587 var curveness = edge.getModel().get('lineStyle.curveness') || 0; 52588 var p1 = clone$1(edge.node1.getLayout()); 52589 var p2 = clone$1(edge.node2.getLayout()); 52590 var points = [p1, p2]; 52591 if (+curveness) { 52592 points.push([ 52593 (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness, 52594 (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness 52595 ]); 52596 } 52597 edge.setLayout(points); 52598 }); 52599 } 52600 52601 /* 52602* Licensed to the Apache Software Foundation (ASF) under one 52603* or more contributor license agreements. See the NOTICE file 52604* distributed with this work for additional information 52605* regarding copyright ownership. The ASF licenses this file 52606* to you under the Apache License, Version 2.0 (the 52607* "License"); you may not use this file except in compliance 52608* with the License. You may obtain a copy of the License at 52609* 52610* http://www.apache.org/licenses/LICENSE-2.0 52611* 52612* Unless required by applicable law or agreed to in writing, 52613* software distributed under the License is distributed on an 52614* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52615* KIND, either express or implied. See the License for the 52616* specific language governing permissions and limitations 52617* under the License. 52618*/ 52619 52620 var simpleLayout = function (ecModel, api) { 52621 ecModel.eachSeriesByType('graph', function (seriesModel) { 52622 var layout = seriesModel.get('layout'); 52623 var coordSys = seriesModel.coordinateSystem; 52624 if (coordSys && coordSys.type !== 'view') { 52625 var data = seriesModel.getData(); 52626 52627 var dimensions = []; 52628 each$1(coordSys.dimensions, function (coordDim) { 52629 dimensions = dimensions.concat(data.mapDimension(coordDim, true)); 52630 }); 52631 52632 for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) { 52633 var value = []; 52634 var hasValue = false; 52635 for (var i = 0; i < dimensions.length; i++) { 52636 var val = data.get(dimensions[i], dataIndex); 52637 if (!isNaN(val)) { 52638 hasValue = true; 52639 } 52640 value.push(val); 52641 } 52642 if (hasValue) { 52643 data.setItemLayout(dataIndex, coordSys.dataToPoint(value)); 52644 } 52645 else { 52646 // Also {Array.<number>}, not undefined to avoid if...else... statement 52647 data.setItemLayout(dataIndex, [NaN, NaN]); 52648 } 52649 } 52650 52651 simpleLayoutEdge(data.graph); 52652 } 52653 else if (!layout || layout === 'none') { 52654 simpleLayout$1(seriesModel); 52655 } 52656 }); 52657 }; 52658 52659 /* 52660* Licensed to the Apache Software Foundation (ASF) under one 52661* or more contributor license agreements. See the NOTICE file 52662* distributed with this work for additional information 52663* regarding copyright ownership. The ASF licenses this file 52664* to you under the Apache License, Version 2.0 (the 52665* "License"); you may not use this file except in compliance 52666* with the License. You may obtain a copy of the License at 52667* 52668* http://www.apache.org/licenses/LICENSE-2.0 52669* 52670* Unless required by applicable law or agreed to in writing, 52671* software distributed under the License is distributed on an 52672* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52673* KIND, either express or implied. See the License for the 52674* specific language governing permissions and limitations 52675* under the License. 52676*/ 52677 52678 function circularLayout$1(seriesModel) { 52679 var coordSys = seriesModel.coordinateSystem; 52680 if (coordSys && coordSys.type !== 'view') { 52681 return; 52682 } 52683
52684 var rect = coordSys.getBoundingRect(); 52685 52686 var nodeData = seriesModel.getData(); 52687 var graph = nodeData.graph; 52688 52689 var angle = 0; 52690 var sum = nodeData.getSum('value'); 52691 var unitAngle = Math.PI * 2 / (sum || nodeData.count()); 52692 52693 var cx = rect.width / 2 + rect.x; 52694 var cy = rect.height / 2 + rect.y; 52695 52696 var r = Math.min(rect.width, rect.height) / 2; 52697 52698 graph.eachNode(function (node) { 52699 var value = node.getValue('value'); 52700 52701 angle += unitAngle * (sum ? value : 1) / 2; 52702 52703 node.setLayout([ 52704 r * Math.cos(angle) + cx, 52705 r * Math.sin(angle) + cy 52706 ]); 52707 52708 angle += unitAngle * (sum ? value : 1) / 2; 52709 }); 52710 52711 nodeData.setLayout({ 52712 cx: cx, 52713 cy: cy 52714 }); 52715 52716 graph.eachEdge(function (edge) { 52717 var curveness = edge.getModel().get('lineStyle.curveness') || 0; 52718 var p1 = clone$1(edge.node1.getLayout()); 52719 var p2 = clone$1(edge.node2.getLayout()); 52720 var cp1; 52721 var x12 = (p1[0] + p2[0]) / 2; 52722 var y12 = (p1[1] + p2[1]) / 2; 52723 if (+curveness) { 52724 curveness *= 3; 52725 cp1 = [ 52726 cx * curveness + x12 * (1 - curveness), 52727 cy * curveness + y12 * (1 - curveness) 52728 ]; 52729 } 52730 edge.setLayout([p1, p2, cp1]); 52731 }); 52732 } 52733 52734 /* 52735* Licensed to the Apache Software Foundation (ASF) under one 52736* or more contributor license agreements. See the NOTICE file 52737* distributed with this work for additional information 52738* regarding copyright ownership. The ASF licenses this file 52739* to you under the Apache License, Version 2.0 (the 52740* "License"); you may not use this file except in compliance 52741* with the License. You may obtain a copy of the License at 52742* 52743* http://www.apache.org/licenses/LICENSE-2.0 52744* 52745* Unless required by applicable law or agreed to in writing, 52746* software distributed under the License is distributed on an 52747* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52748* KIND, either express or implied. See the License for the 52749* specific language governing permissions and limitations 52750* under the License. 52751*/ 52752 52753 var circularLayout = function (ecModel) { 52754 ecModel.eachSeriesByType('graph', function (seriesModel) { 52755 if (seriesModel.get('layout') === 'circular') { 52756 circularLayout$1(seriesModel); 52757 } 52758 }); 52759 }; 52760 52761 /* 52762* Licensed to the Apache Software Foundation (ASF) under one 52763* or more contributor license agreements. See the NOTICE file 52764* distributed with this work for additional information 52765* regarding copyright ownership. The ASF licenses this file 52766* to you under the Apache License, Version 2.0 (the 52767* "License"); you may not use this file except in compliance 52768* with the License. You may obtain a copy of the License at 52769* 52770* http://www.apache.org/licenses/LICENSE-2.0 52771* 52772* Unless required by applicable law or agreed to in writing, 52773* software distributed under the License is distributed on an 52774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52775* KIND, either express or implied. See the License for the 52776* specific language governing permissions and limitations 52777* under the License. 52778*/ 52779 52780 /* 52781* The layout implementation references to d3.js. The use of 52782* the source code of this file is also subject to the terms 52783* and consitions of its license (BSD-3Clause, see 52784* <echarts/src/licenses/LICENSE-d3>). 52785*/ 52786 52787 var scaleAndAdd$2 = scaleAndAdd; 52788 52789// function adjacentNode(n, e) { 52790// return e.n1 === n ? e.n2 : e.n1; 52791// } 52792 52793 function forceLayout$1(nodes, edges, opts) { 52794 var rect = opts.rect; 52795 var width = rect.width; 52796 var height = rect.height; 52797 var center = [rect.x + width / 2, rect.y + height / 2]; 52798 // var scale = opts.scale || 1; 52799 var gravity = opts.gravity == null ? 0.1 : opts.gravity; 52800 52801 // for (var i = 0; i < edges.length; i++) { 52802 // var e = edges[i]; 52803 // var n1 = e.n1; 52804 // var n2 = e.n2; 52805 // n1.edges = n1.edges || []; 52806 // n2.edges = n2.edges || []; 52807 // n1.edges.push(e); 52808 // n2.edges.push(e); 52809 // } 52810 // Init position 52811 for (var i = 0; i < nodes.length; i++) { 52812 var n = nodes[i];
52813 if (!n.p) { 52814 // Use the position from first adjecent node with defined position 52815 // Or use a random position 52816 // From d3 52817 // if (n.edges) { 52818 // var j = -1; 52819 // while (++j < n.edges.length) { 52820 // var e = n.edges[j]; 52821 // var other = adjacentNode(n, e); 52822 // if (other.p) { 52823 // n.p = vec2.clone(other.p); 52824 // break; 52825 // } 52826 // } 52827 // } 52828 // if (!n.p) { 52829 n.p = create( 52830 width * (Math.random() - 0.5) + center[0], 52831 height * (Math.random() - 0.5) + center[1] 52832 ); 52833 // } 52834 } 52835 n.pp = clone$1(n.p); 52836 n.edges = null; 52837 } 52838 52839 // Formula in 'Graph Drawing by Force-directed Placement' 52840 // var k = scale * Math.sqrt(width * height / nodes.length); 52841 // var k2 = k * k; 52842 52843 var friction = 0.6; 52844 52845 return { 52846 warmUp: function () { 52847 friction = 0.5; 52848 }, 52849 52850 setFixed: function (idx) { 52851 nodes[idx].fixed = true; 52852 }, 52853 52854 setUnfixed: function (idx) { 52855 nodes[idx].fixed = false; 52856 }, 52857 52858 step: function (cb) { 52859 var v12 = []; 52860 var nLen = nodes.length; 52861 for (var i = 0; i < edges.length; i++) { 52862 var e = edges[i]; 52863 var n1 = e.n1; 52864 var n2 = e.n2; 52865 52866 sub(v12, n2.p, n1.p); 52867 var d = len(v12) - e.d; 52868 var w = n2.w / (n1.w + n2.w); 52869 52870 if (isNaN(w)) { 52871 w = 0; 52872 } 52873 52874 normalize(v12, v12); 52875 52876 !n1.fixed && scaleAndAdd$2(n1.p, n1.p, v12, w * d * friction); 52877 !n2.fixed && scaleAndAdd$2(n2.p, n2.p, v12, -(1 - w) * d * friction); 52878 } 52879 // Gravity 52880 for (var i = 0; i < nLen; i++) { 52881 var n = nodes[i]; 52882 if (!n.fixed) { 52883 sub(v12, center, n.p); 52884 // var d = vec2.len(v12); 52885 // vec2.scale(v12, v12, 1 / d); 52886 // var gravityFactor = gravity; 52887 scaleAndAdd$2(n.p, n.p, v12, gravity * friction); 52888 } 52889 } 52890 52891 // Repulsive 52892 // PENDING 52893 for (var i = 0; i < nLen; i++) { 52894 var n1 = nodes[i]; 52895 for (var j = i + 1; j < nLen; j++) { 52896 var n2 = nodes[j]; 52897 sub(v12, n2.p, n1.p); 52898 var d = len(v12); 52899 if (d === 0) { 52900 // Random repulse 52901 set(v12, Math.random() - 0.5, Math.random() - 0.5); 52902 d = 1; 52903 } 52904 var repFact = (n1.rep + n2.rep) / d / d; 52905 !n1.fixed && scaleAndAdd$2(n1.pp, n1.pp, v12, repFact); 52906 !n2.fixed && scaleAndAdd$2(n2.pp, n2.pp, v12, -repFact); 52907 } 52908 } 52909 var v = []; 52910 for (var i = 0; i < nLen; i++) { 52911 var n = nodes[i]; 52912 if (!n.fixed) { 52913 sub(v, n.p, n.pp); 52914 scaleAndAdd$2(n.p, n.p, v, friction); 52915 copy(n.pp, n.p); 52916 } 52917 } 52918 52919 friction = friction * 0.992; 52920 52921 cb && cb(nodes, edges, friction < 0.01); 52922 } 52923 }; 52924 } 52925 52926 /* 52927* Licensed to the Apache Software Foundation (ASF) under one 52928* or more contributor license agreements. See the NOTICE file 52929* distributed with this work for additional information 52930* regarding copyright ownership. The ASF licenses this file 52931* to you under the Apache License, Version 2.0 (the 52932* "License");
52932 you may not use this file except in compliance 52933* with the License. You may obtain a copy of the License at 52934* 52935* http://www.apache.org/licenses/LICENSE-2.0 52936* 52937* Unless required by applicable law or agreed to in writing, 52938* software distributed under the License is distributed on an 52939* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 52940* KIND, either express or implied. See the License for the 52941* specific language governing permissions and limitations 52942* under the License. 52943*/ 52944 52945 var forceLayout = function (ecModel) { 52946 ecModel.eachSeriesByType('graph', function (graphSeries) { 52947 var coordSys = graphSeries.coordinateSystem; 52948 if (coordSys && coordSys.type !== 'view') { 52949 return; 52950 } 52951 if (graphSeries.get('layout') === 'force') { 52952 var preservedPoints = graphSeries.preservedPoints || {}; 52953 var graph = graphSeries.getGraph(); 52954 var nodeData = graph.data; 52955 var edgeData = graph.edgeData; 52956 var forceModel = graphSeries.getModel('force'); 52957 var initLayout = forceModel.get('initLayout'); 52958 if (graphSeries.preservedPoints) { 52959 nodeData.each(function (idx) { 52960 var id = nodeData.getId(idx); 52961 nodeData.setItemLayout(idx, preservedPoints[id] || [NaN, NaN]); 52962 }); 52963 } 52964 else if (!initLayout || initLayout === 'none') { 52965 simpleLayout$1(graphSeries); 52966 } 52967 else if (initLayout === 'circular') { 52968 circularLayout$1(graphSeries); 52969 } 52970 52971 var nodeDataExtent = nodeData.getDataExtent('value'); 52972 var edgeDataExtent = edgeData.getDataExtent('value'); 52973 // var edgeDataExtent = edgeData.getDataExtent('value'); 52974 var repulsion = forceModel.get('repulsion'); 52975 var edgeLength = forceModel.get('edgeLength'); 52976 if (!isArray(repulsion)) { 52977 repulsion = [repulsion, repulsion]; 52978 } 52979 if (!isArray(edgeLength)) { 52980 edgeLength = [edgeLength, edgeLength]; 52981 } 52982 // Larger value has smaller length 52983 edgeLength = [edgeLength[1], edgeLength[0]]; 52984 52985 var nodes = nodeData.mapArray('value', function (value, idx) { 52986 var point = nodeData.getItemLayout(idx); 52987 var rep = linearMap(value, nodeDataExtent, repulsion); 52988 if (isNaN(rep)) { 52989 rep = (repulsion[0] + repulsion[1]) / 2; 52990 } 52991 return { 52992 w: rep, 52993 rep: rep, 52994 fixed: nodeData.getItemModel(idx).get('fixed'), 52995 p: (!point || isNaN(point[0]) || isNaN(point[1])) ? null : point 52996 }; 52997 }); 52998 var edges = edgeData.mapArray('value', function (value, idx) { 52999 var edge = graph.getEdgeByIndex(idx); 53000 var d = linearMap(value, edgeDataExtent, edgeLength); 53001 if (isNaN(d)) { 53002 d = (edgeLength[0] + edgeLength[1]) / 2; 53003 } 53004 return { 53005 n1: nodes[edge.node1.dataIndex], 53006 n2: nodes[edge.node2.dataIndex], 53007 d: d, 53008 curveness: edge.getModel().get('lineStyle.curveness') || 0 53009 }; 53010 }); 53011 53012 var coordSys = graphSeries.coordinateSystem; 53013 var rect = coordSys.getBoundingRect(); 53014 var forceInstance = forceLayout$1(nodes, edges, { 53015 rect: rect, 53016 gravity: forceModel.get('gravity') 53017 }); 53018 var oldStep = forceInstance.step; 53019 forceInstance.step = function (cb) { 53020 for (var i = 0, l = nodes.length; i < l; i++) { 53021 if (nodes[i].fixed) { 53022 // Write back to layout instance 53023 copy(nodes[i].p, graph.getNodeByIndex(i).getLayout()); 53024 } 53025 } 53026 oldStep(function (nodes, edges, stopped) { 53027 for (var i = 0, l = nodes.length; i < l; i++) { 53028 if (!nodes[i].fixed) { 53029 graph.getNodeByIndex(i).setLayout(nodes[i].p); 53030 } 53031 preservedPoints[nodeData.getId(i)] = nodes[i].p; 53032 } 53033 for (var i = 0, l = edges.length; i < l; i++) { 53034 var e = edges[i]; 53035 var edge = graph.getEdgeByIndex(i); 53036 var p1 = e.n1.p; 53037 var p2 = e.n2.p; 53038 var points = edge.getLayout(); 53039 points = points ? points.slice() : []; 53040 points[0] = points[0] || []; 53041 points[1] = points[1] || []; 53042 copy(points[0], p1); 53043 copy(points[1], p2); 53044 if (+e.curveness) { 53045 points[2] = [ 53046 (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness, 53047 (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness 53048 ]; 53049 } 53050 edge.setLayout(points); 53051 } 53052 // Update layout 53053 53054 cb && cb(stopped); 53055 }); 53056 }; 53057 graphSeries.forceLayout = forceInstance; 53058 graphSeries.preservedPoints = preservedPoints; 53059 53060 // Step to get the layout 53061 forceInstance.step(); 53062 } 53063 else { 53064 // Remove prev injected forceLayout instance 53065 graphSeries.forceLayout = null; 53066 } 53067 }); 53068 }; 53069 53070 /* 53071* Licensed to the Apache Software Foundation (ASF) under one 53072* or more contributor license agreements. See the NOTICE file 53073* distributed with this work for additional information 53074* regarding copyright ownership. The ASF licenses this file 53075* to you under the Apache License, Version 2.0 (the 53076* "License");
53076 you may not use this file except in compliance 53077* with the License. You may obtain a copy of the License at 53078* 53079* http://www.apache.org/licenses/LICENSE-2.0 53080* 53081* Unless required by applicable law or agreed to in writing, 53082* software distributed under the License is distributed on an 53083* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53084* KIND, either express or implied. See the License for the 53085* specific language governing permissions and limitations 53086* under the License. 53087*/ 53088 53089// FIXME Where to create the simple view coordinate system 53090 function getViewRect$1(seriesModel, api, aspect) { 53091 var option = seriesModel.getBoxLayoutParams(); 53092 option.aspect = aspect; 53093 return getLayoutRect(option, { 53094 width: api.getWidth(), 53095 height: api.getHeight() 53096 }); 53097 } 53098 53099 var createView = function (ecModel, api) { 53100 var viewList = []; 53101 ecModel.eachSeriesByType('graph', function (seriesModel) { 53102 var coordSysType = seriesModel.get('coordinateSystem'); 53103 if (!coordSysType || coordSysType === 'view') { 53104 53105 var data = seriesModel.getData(); 53106 var positions = data.mapArray(function (idx) { 53107 var itemModel = data.getItemModel(idx); 53108 return [+itemModel.get('x'), +itemModel.get('y')]; 53109 }); 53110 53111 var min = []; 53112 var max = []; 53113 53114 fromPoints(positions, min, max); 53115 53116 // If width or height is 0 53117 if (max[0] - min[0] === 0) { 53118 max[0] += 1; 53119 min[0] -= 1; 53120 } 53121 if (max[1] - min[1] === 0) { 53122 max[1] += 1; 53123 min[1] -= 1; 53124 } 53125 var aspect = (max[0] - min[0]) / (max[1] - min[1]); 53126 // FIXME If get view rect after data processed?
53127 var viewRect = getViewRect$1(seriesModel, api, aspect); 53128 // Position may be NaN, use view rect instead 53129 if (isNaN(aspect)) { 53130 min = [viewRect.x, viewRect.y]; 53131 max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height]; 53132 } 53133 53134 var bbWidth = max[0] - min[0]; 53135 var bbHeight = max[1] - min[1]; 53136 53137 var viewWidth = viewRect.width; 53138 var viewHeight = viewRect.height; 53139 53140 var viewCoordSys = seriesModel.coordinateSystem = new View(); 53141 viewCoordSys.zoomLimit = seriesModel.get('scaleLimit'); 53142 53143 viewCoordSys.setBoundingRect( 53144 min[0], min[1], bbWidth, bbHeight 53145 ); 53146 viewCoordSys.setViewRect( 53147 viewRect.x, viewRect.y, viewWidth, viewHeight 53148 ); 53149 53150 // Update roam info 53151 viewCoordSys.setCenter(seriesModel.get('center')); 53152 viewCoordSys.setZoom(seriesModel.get('zoom')); 53153 53154 viewList.push(viewCoordSys); 53155 } 53156 }); 53157 53158 return viewList; 53159 }; 53160 53161 /* 53162* Licensed to the Apache Software Foundation (ASF) under one 53163* or more contributor license agreements. See the NOTICE file 53164* distributed with this work for additional information 53165* regarding copyright ownership. The ASF licenses this file 53166* to you under the Apache License, Version 2.0 (the 53167* "License"); you may not use this file except in compliance 53168* with the License. You may obtain a copy of the License at 53169* 53170* http://www.apache.org/licenses/LICENSE-2.0 53171* 53172* Unless required by applicable law or agreed to in writing, 53173* software distributed under the License is distributed on an 53174* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53175* KIND, either express or implied. See the License for the 53176* specific language governing permissions and limitations 53177* under the License. 53178*/ 53179 53180 registerProcessor(categoryFilter); 53181 53182 registerVisual(visualSymbol('graph', 'circle', null)); 53183 registerVisual(categoryVisual); 53184 registerVisual(edgeVisual); 53185 53186 registerLayout(simpleLayout); 53187 registerLayout(circularLayout); 53188 registerLayout(forceLayout); 53189 53190// Graph view coordinate system 53191 registerCoordinateSystem('graphView', { 53192 create: createView 53193 }); 53194 53195 /* 53196* Licensed to the Apache Software Foundation (ASF) under one 53197* or more contributor license agreements. See the NOTICE file 53198* distributed with this work for additional information 53199* regarding copyright ownership. The ASF licenses this file 53200* to you under the Apache License, Version 2.0 (the 53201* "License"); you may not use this file except in compliance 53202* with the License. You may obtain a copy of the License at 53203* 53204* http://www.apache.org/licenses/LICENSE-2.0 53205* 53206* Unless required by applicable law or agreed to in writing, 53207* software distributed under the License is distributed on an 53208* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53209* KIND, either express or implied. See the License for the 53210* specific language governing permissions and limitations 53211* under the License. 53212*/ 53213 53214 var GaugeSeries = SeriesModel.extend({ 53215 53216 type: 'series.gauge', 53217 53218 getInitialData: function (option, ecModel) { 53219 var dataOpt = option.data || []; 53220 if (!isArray(dataOpt)) { 53221 dataOpt = [dataOpt]; 53222 } 53223 option.data = dataOpt; 53224 return createListSimply(this, ['value']); 53225 }, 53226 53227 defaultOption: { 53228 zlevel: 0, 53229 z: 2, 53230 // é»Ë认åâ¦Â¨Ã¥Â±â¬Ã¥Â±â¦Ã¤Â¸Â 53231 center: ['50%', '50%'], 53232 legendHoverLink: true, 53233 radius: '75%', 53234 startAngle: 225, 53235 endAngle: -45, 53236 clockwise: true, 53237 // æÅâ¬Ã¥Â°ÂÃ¥â¬Â¼ 53238 min: 0, 53239 // æÅâ¬Ã¥Â¤Â§Ã¥â¬Â¼ 53240 max: 100,
53241 // Ã¥Ëâ Ã¥â°Â²Ã¦Â®ÂµÃ¦â¢Â°Ã¯Â¼Åé»Ë认为10 53242 splitNumber: 10, 53243 // Ã¥ÂÂæ â¡Ã¨Â½Â´Ã§ÂºÂ¿ 53244 axisLine: { 53245 // é»Ë认æË¾ç¤ºï¼Å属æâ¬Â§show控å˶æË¾ç¤ºä¸Žå¦ 53246 show: true, 53247 lineStyle: { // 属æâ¬Â§lineStyle控å˶线æÂ¡æ ·å¼ 53248 color: [[0.2, '#91c7ae'], [0.8, '#63869e'], [1, '#c23531']], 53249 width: 30 53250 } 53251 }, 53252 // Ã¥Ëâ éšâ线 53253 splitLine: { 53254 // é»Ë认æË¾ç¤ºï¼Å属æâ¬Â§show控å˶æË¾ç¤ºä¸Žå¦ 53255 show: true, 53256 // 属æâ¬Â§length控å˶线éâ¢Â¿ 53257 length: 30, 53258 // 属æâ¬Â§lineStyleï¼Ë详è§ÂlineStyleï¼â°Ã¦Å½Â§Ã¥Ë¶çº¿æÂ¡æ ·å¼ 53259 lineStyle: { 53260 color: '#eee', 53261 width: 2, 53262 type: 'solid' 53263 } 53264 }, 53265 // Ã¥ÂÂæ â¡Ã¨Â½Â´Ã¥Â°Âæ â¡Ã¨Â®Â° 53266 axisTick: { 53267 // 属æâ¬Â§show控å˶æË¾ç¤ºä¸Žå¦ï¼Åé»Ë认ä¸ÂæË¾ç¤º 53268 show: true, 53269 // æ¯Â份splitç»â Ã¥Ëâ 多å°â段 53270 splitNumber: 5, 53271 // 属æâ¬Â§length控å˶线éâ¢Â¿ 53272 length: 8, 53273 // 属æâ¬Â§lineStyle控å˶线æÂ¡æ ·å¼ 53274 lineStyle: { 53275 color: '#eee', 53276 width: 1, 53277 type: 'solid' 53278 } 53279 }, 53280 axisLabel: { 53281 show: true, 53282 distance: 5, 53283 // formatter: null, 53284 color: 'auto' 53285 }, 53286 pointer: { 53287 show: true, 53288 length: '80%', 53289 width: 8 53290 }, 53291 itemStyle: { 53292 color: 'auto' 53293 }, 53294 title: { 53295 show: true, 53296 // x, yï¼ÅÃ¥Ââ¢Ã¤Â½Âpx 53297 offsetCenter: [0, '-40%'], 53298 // Ã¥â¦Â¶Ã¤Â½â¢Ã¥Â±Å¾Ã¦â¬Â§Ã©Â»Ë认使çâ¨åâ¦Â¨Ã¥Â±â¬Ã¦ââ¡Ã¦Å¾ ·å¼Âï¼Å详è§ÂTEXTSTYLE 53299 color: '#333', 53300 fontSize: 15 53301 }, 53302 detail: { 53303 show: true, 53304 backgroundColor: 'rgba(0,0,0,0)', 53305 borderWidth: 0, 53306 borderColor: '#ccc', 53307 width: 100, 53308 height: null, // self-adaption 53309 padding: [5, 10], 53310 // x, yï¼ÅÃ¥Ââ¢Ã¤Â½Âpx 53311 offsetCenter: [0, '40%'], 53312 // formatter: null, 53313 // Ã¥â¦Â¶Ã¤Â½â¢Ã¥Â±Å¾Ã¦â¬Â§Ã©Â»Ë认使çâ¨åâ¦Â¨Ã¥Â±â¬Ã¦ââ¡Ã¦Å¾ ·å¼Âï¼Å详è§ÂTEXTSTYLE 53314 color: 'auto', 53315 fontSize: 30 53316 } 53317 } 53318 }); 53319 53320 /* 53321* Licensed to the Apache Software Foundation (ASF) under one 53322* or more contributor license agreements. See the NOTICE file 53323* distributed with this work for additional information 53324* regarding copyright ownership. The ASF licenses this file 53325* to you under the Apache License, Version 2.0 (the 53326* "License"); you may not use this file except in compliance 53327* with the License. You may obtain a copy of the License at 53328* 53329* http://www.apache.org/licenses/LICENSE-2.0 53330* 53331* Unless required by applicable law or agreed to in writing, 53332* software distributed under the License is distributed on an 53333* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53334* KIND, either express or implied. See the License for the 53335* specific language governing permissions and limitations 53336* under the License. 53337*/ 53338 53339 var PointerPath = Path.extend({ 53340 53341 type: 'echartsGaugePointer', 53342 53343 shape: { 53344 angle: 0, 53345 53346 width: 10, 53347 53348 r: 10, 53349 53350 x: 0, 53351 53352 y: 0 53353 }, 53354 53355 buildPath: function (ctx, shape) { 53356 var mathCos = Math.cos; 53357 var mathSin = Math.sin; 53358 53359 var r = shape.r; 53360 var width = shape.width; 53361 var angle = shape.angle; 53362 var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2); 53363 var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2); 53364 53365 angle = shape.angle - Math.PI / 2; 53366 ctx.moveTo(x, y); 53367 ctx.lineTo( 53368 shape.x + mathCos(angle) * width, 53369 shape.y + mathSin(angle) * width 53370 ); 53371 ctx.lineTo( 53372 shape.x + mathCos(shape.angle) * r, 53373 shape.y + mathSin(shape.angle) * r 53374 ); 53375 ctx.lineTo( 53376 shape.x - mathCos(angle) * width, 53377 shape.y - mathSin(angle) * width 53378 ); 53379 ctx.lineTo(x, y); 53380 return; 53381 } 53382 });
53383 53384 /* 53385* Licensed to the Apache Software Foundation (ASF) under one 53386* or more contributor license agreements. See the NOTICE file 53387* distributed with this work for additional information 53388* regarding copyright ownership. The ASF licenses this file 53389* to you under the Apache License, Version 2.0 (the 53390* "License"); you may not use this file except in compliance 53391* with the License. You may obtain a copy of the License at 53392* 53393* http://www.apache.org/licenses/LICENSE-2.0 53394* 53395* Unless required by applicable law or agreed to in writing, 53396* software distributed under the License is distributed on an 53397* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53398* KIND, either express or implied. See the License for the 53399* specific language governing permissions and limitations 53400* under the License. 53401*/ 53402 53403 function parsePosition(seriesModel, api) { 53404 var center = seriesModel.get('center'); 53405 var width = api.getWidth(); 53406 var height = api.getHeight(); 53407 var size = Math.min(width, height); 53408 var cx = parsePercent$1(center[0], api.getWidth()); 53409 var cy = parsePercent$1(center[1], api.getHeight());
53410 var r = parsePercent$1(seriesModel.get('radius'), size / 2); 53411 53412 return { 53413 cx: cx, 53414 cy: cy, 53415 r: r 53416 }; 53417 } 53418 53419 function formatLabel(label, labelFormatter) { 53420 if (labelFormatter) { 53421 if (typeof labelFormatter === 'string') { 53422 label = labelFormatter.replace('{value}', label != null ? label : ''); 53423 } 53424 else if (typeof labelFormatter === 'function') { 53425 label = labelFormatter(label); 53426 } 53427 } 53428 53429 return label; 53430 } 53431 53432 var PI2$5 = Math.PI * 2; 53433 53434 var GaugeView = Chart.extend({ 53435 53436 type: 'gauge', 53437 53438 render: function (seriesModel, ecModel, api) { 53439 53440 this.group.removeAll(); 53441 53442 var colorList = seriesModel.get('axisLine.lineStyle.color'); 53443 var posInfo = parsePosition(seriesModel, api); 53444 53445 this._renderMain( 53446 seriesModel, ecModel, api, colorList, posInfo 53447 ); 53448 }, 53449 53450 dispose: function () {}, 53451 53452 _renderMain: function (seriesModel, ecModel, api, colorList, posInfo) { 53453 var group = this.group; 53454 53455 var axisLineModel = seriesModel.getModel('axisLine'); 53456 var lineStyleModel = axisLineModel.getModel('lineStyle'); 53457 53458 var clockwise = seriesModel.get('clockwise'); 53459 var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI; 53460 var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI; 53461 53462 var angleRangeSpan = (endAngle - startAngle) % PI2$5; 53463 53464 var prevEndAngle = startAngle; 53465 var axisLineWidth = lineStyleModel.get('width'); 53466 53467 for (var i = 0; i < colorList.length; i++) { 53468 // Clamp 53469 var percent = Math.min(Math.max(colorList[i][0], 0), 1); 53470 var endAngle = startAngle + angleRangeSpan * percent; 53471 var sector = new Sector({ 53472 shape: { 53473 startAngle: prevEndAngle, 53474 endAngle: endAngle, 53475 cx: posInfo.cx, 53476 cy: posInfo.cy, 53477 clockwise: clockwise, 53478 r0: posInfo.r - axisLineWidth, 53479 r: posInfo.r 53480 }, 53481 silent: true 53482 }); 53483 53484 sector.setStyle({ 53485 fill: colorList[i][1] 53486 }); 53487 53488 sector.setStyle(lineStyleModel.getLineStyle( 53489 // Because we use sector to simulate arc 53490 // so the properties for stroking are useless 53491 ['color', 'borderWidth', 'borderColor'] 53492 )); 53493 53494 group.add(sector); 53495 53496 prevEndAngle = endAngle; 53497 } 53498 53499 var getColor = function (percent) { 53500 // Less than 0 53501 if (percent <= 0) { 53502 return colorList[0][1]; 53503 } 53504 for (var i = 0; i < colorList.length; i++) { 53505 if (colorList[i][0] >= percent 53506 && (i === 0 ? 0 : colorList[i - 1][0]) < percent 53507 ) { 53508 return colorList[i][1]; 53509 } 53510 } 53511 // More than 1 53512 return colorList[i - 1][1]; 53513 }; 53514 53515 if (!clockwise) { 53516 var tmp = startAngle; 53517 startAngle = endAngle; 53518 endAngle = tmp; 53519 } 53520 53521 this._renderTicks( 53522 seriesModel, ecModel, api, getColor, posInfo, 53523 startAngle, endAngle, clockwise 53524 ); 53525 53526 this._renderPointer( 53527 seriesModel, ecModel, api, getColor, posInfo, 53528 startAngle, endAngle, clockwise 53529 ); 53530 53531 this._renderTitle( 53532 seriesModel, ecModel, api, getColor, posInfo 53533 ); 53534 this._renderDetail( 53535 seriesModel, ecModel, api, getColor, posInfo 53536 ); 53537 }, 53538 53539 _renderTicks: function ( 53540 seriesModel, ecModel, api, getColor, posInfo, 53541 startAngle, endAngle, clockwise 53542 ) { 53543 var group = this.group; 53544 var cx = posInfo.cx; 53545 var cy = posInfo.cy; 53546 var r = posInfo.r; 53547 53548 var minVal = +seriesModel.get('min'); 53549 var maxVal = +seriesModel.get('max'); 53550 53551 var splitLineModel = seriesModel.getModel('splitLine'); 53552 var tickModel = seriesModel.getModel('axisTick'); 53553 var labelModel = seriesModel.getModel('axisLabel'); 53554 53555 var splitNumber = seriesModel.get('splitNumber'); 53556 var subSplitNumber = tickModel.get('splitNumber'); 53557 53558 var splitLineLen = parsePercent$1( 53559 splitLineModel.get('length'), r 53560 ); 53561 var tickLen = parsePercent$1( 53562 tickModel.get('length'), r 53563 ); 53564 53565 var angle = startAngle; 53566 var step = (endAngle - startAngle) / splitNumber; 53567 var subStep = step / subSplitNumber; 53568 53569 var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle(); 53570 var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle(); 53571 53572 for (var i = 0; i <= splitNumber; i++) { 53573 var unitX = Math.cos(angle); 53574 var unitY = Math.sin(angle); 53575 // Split line 53576 if (splitLineModel.get('show')) { 53577 var splitLine = new Line({ 53578 shape: { 53579 x1: unitX * r + cx, 53580 y1: unitY * r + cy, 53581 x2: unitX * (r - splitLineLen) + cx, 53582 y2: unitY * (r - splitLineLen) + cy 53583 }, 53584 style: splitLineStyle, 53585 silent: true 53586 }); 53587 if (splitLineStyle.stroke === 'auto') { 53588 splitLine.setStyle({ 53589 stroke: getColor(i / splitNumber) 53590 }); 53591 } 53592 53593 group.add(splitLine); 53594 } 53595 53596 // Label 53597 if (labelModel.get('show')) { 53598 var label = formatLabel( 53599 round$1(i / splitNumber * (maxVal - minVal) + minVal), 53600 labelModel.get('formatter') 53601 ); 53602 var distance = labelModel.get('distance'); 53603 var autoColor = getColor(i / splitNumber); 53604 53605 group.add(new Text({ 53606 style: setTextStyle({}, labelModel, { 53607 text: label, 53608 x: unitX * (r - splitLineLen - distance) + cx, 53609 y: unitY * (r - splitLineLen - distance) + cy, 53610 textVerticalAlign: unitY < -0.4 ? 'top' : (unitY > 0.4 ? 'bottom' : 'middle'), 53611 textAlign: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center') 53612 }, {autoColor: autoColor}), 53613 silent: true 53614 })); 53615 } 53616 53617 // Axis tick 53618 if (tickModel.get('show') && i !== splitNumber) { 53619 for (var j = 0; j <= subSplitNumber; j++) { 53620 var unitX = Math.cos(angle); 53621 var unitY = Math.sin(angle); 53622 var tickLine = new Line({ 53623 shape: { 53624 x1: unitX * r + cx, 53625 y1: unitY * r + cy, 53626 x2: unitX * (r - tickLen) + cx, 53627 y2: unitY * (r - tickLen) + cy 53628 }, 53629 silent: true, 53630 style: tickLineStyle 53631 }); 53632 53633 if (tickLineStyle.stroke === 'auto') { 53634 tickLine.setStyle({ 53635 stroke: getColor((i + j / subSplitNumber) / splitNumber) 53636 }); 53637 } 53638 53639 group.add(tickLine); 53640 angle += subStep; 53641 } 53642 angle -= subStep; 53643 } 53644 else { 53645 angle += step; 53646 } 53647 } 53648 }, 53649 53650 _renderPointer: function ( 53651 seriesModel, ecModel, api, getColor, posInfo, 53652 startAngle, endAngle, clockwise 53653 ) { 53654 53655 var group = this.group; 53656 var oldData = this._data; 53657
53658 if (!seriesModel.get('pointer.show')) { 53659 // Remove old element 53660 oldData && oldData.eachItemGraphicEl(function (el) { 53661 group.remove(el); 53662 }); 53663 return; 53664 } 53665 53666 var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')]; 53667 var angleExtent = [startAngle, endAngle]; 53668 53669 var data = seriesModel.getData(); 53670 var valueDim = data.mapDimension('value'); 53671 53672 data.diff(oldData) 53673 .add(function (idx) { 53674 var pointer = new PointerPath({ 53675 shape: { 53676 angle: startAngle 53677 } 53678 }); 53679 53680 initProps(pointer, { 53681 shape: { 53682 angle: linearMap(data.get(valueDim, idx), valueExtent, angleExtent, true) 53683 } 53684 }, seriesModel); 53685 53686 group.add(pointer); 53687 data.setItemGraphicEl(idx, pointer); 53688 }) 53689 .update(function (newIdx, oldIdx) { 53690 var pointer = oldData.getItemGraphicEl(oldIdx); 53691 53692 updateProps(pointer, { 53693 shape: { 53694 angle: linearMap(data.get(valueDim, newIdx), valueExtent, angleExtent, true) 53695 } 53696 }, seriesModel); 53697 53698 group.add(pointer); 53699 data.setItemGraphicEl(newIdx, pointer); 53700 }) 53701 .remove(function (idx) { 53702 var pointer = oldData.getItemGraphicEl(idx); 53703 group.remove(pointer); 53704 }) 53705 .execute(); 53706 53707 data.eachItemGraphicEl(function (pointer, idx) { 53708 var itemModel = data.getItemModel(idx); 53709 var pointerModel = itemModel.getModel('pointer'); 53710 53711 pointer.setShape({ 53712 x: posInfo.cx, 53713 y: posInfo.cy, 53714 width: parsePercent$1( 53715 pointerModel.get('width'), posInfo.r 53716 ), 53717 r: parsePercent$1(pointerModel.get('length'), posInfo.r) 53718 }); 53719 53720 pointer.useStyle(itemModel.getModel('itemStyle').getItemStyle()); 53721 53722 if (pointer.style.fill === 'auto') { 53723 pointer.setStyle('fill', getColor( 53724 linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true) 53725 )); 53726 } 53727 53728 setHoverStyle( 53729 pointer, itemModel.getModel('emphasis.itemStyle').getItemStyle() 53730 ); 53731 }); 53732 53733 this._data = data; 53734 }, 53735 53736 _renderTitle: function ( 53737 seriesModel, ecModel, api, getColor, posInfo 53738 ) { 53739 var data = seriesModel.getData(); 53740 var valueDim = data.mapDimension('value'); 53741 var titleModel = seriesModel.getModel('title'); 53742 if (titleModel.get('show')) { 53743 var offsetCenter = titleModel.get('offsetCenter'); 53744 var x = posInfo.cx + parsePercent$1(offsetCenter[0], posInfo.r); 53745 var y = posInfo.cy + parsePercent$1(offsetCenter[1], posInfo.r); 53746 53747 var minVal = +seriesModel.get('min'); 53748 var maxVal = +seriesModel.get('max'); 53749 var value = seriesModel.getData().get(valueDim, 0); 53750 var autoColor = getColor( 53751 linearMap(value, [minVal, maxVal], [0, 1], true) 53752 ); 53753 53754 this.group.add(new Text({ 53755 silent: true, 53756 style: setTextStyle({}, titleModel, { 53757 x: x, 53758 y: y, 53759 // FIXME First data name ? 53760 text: data.getName(0), 53761 textAlign: 'center', 53762 textVerticalAlign: 'middle' 53763 }, {autoColor: autoColor, forceRich: true}) 53764 })); 53765 } 53766 }, 53767
53768 _renderDetail: function ( 53769 seriesModel, ecModel, api, getColor, posInfo 53770 ) { 53771 var detailModel = seriesModel.getModel('detail'); 53772 var minVal = +seriesModel.get('min'); 53773 var maxVal = +seriesModel.get('max'); 53774 if (detailModel.get('show')) { 53775 var offsetCenter = detailModel.get('offsetCenter'); 53776 var x = posInfo.cx + parsePercent$1(offsetCenter[0], posInfo.r); 53777 var y = posInfo.cy + parsePercent$1(offsetCenter[1], posInfo.r); 53778 var width = parsePercent$1(detailModel.get('width'), posInfo.r); 53779 var height = parsePercent$1(detailModel.get('height'), posInfo.r); 53780 var data = seriesModel.getData(); 53781 var value = data.get(data.mapDimension('value'), 0); 53782 var autoColor = getColor( 53783 linearMap(value, [minVal, maxVal], [0, 1], true) 53784 ); 53785 53786 this.group.add(new Text({ 53787 silent: true, 53788 style: setTextStyle({}, detailModel, { 53789 x: x, 53790 y: y, 53791 text: formatLabel( 53792 // FIXME First data name ? 53793 value, detailModel.get('formatter') 53794 ), 53795 textWidth: isNaN(width) ? null : width, 53796 textHeight: isNaN(height) ? null : height, 53797 textAlign: 'center', 53798 textVerticalAlign: 'middle' 53799 }, {autoColor: autoColor, forceRich: true}) 53800 })); 53801 } 53802 } 53803 }); 53804 53805 /* 53806* Licensed to the Apache Software Foundation (ASF) under one 53807* or more contributor license agreements. See the NOTICE file 53808* distributed with this work for additional information 53809* regarding copyright ownership. The ASF licenses this file 53810* to you under the Apache License, Version 2.0 (the 53811* "License"); you may not use this file except in compliance 53812* with the License. You may obtain a copy of the License at 53813* 53814* http://www.apache.org/licenses/LICENSE-2.0 53815* 53816* Unless required by applicable law or agreed to in writing, 53817* software distributed under the License is distributed on an 53818* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53819* KIND, either express or implied. See the License for the 53820* specific language governing permissions and limitations 53821* under the License. 53822*/ 53823 53824 /* 53825* Licensed to the Apache Software Foundation (ASF) under one 53826* or more contributor license agreements. See the NOTICE file 53827* distributed with this work for additional information 53828* regarding copyright ownership. The ASF licenses this file 53829* to you under the Apache License, Version 2.0 (the 53830* "License"); you may not use this file except in compliance 53831* with the License. You may obtain a copy of the License at 53832* 53833* http://www.apache.org/licenses/LICENSE-2.0 53834* 53835* Unless required by applicable law or agreed to in writing, 53836* software distributed under the License is distributed on an 53837* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53838* KIND, either express or implied. See the License for the 53839* specific language governing permissions and limitations 53840* under the License. 53841*/ 53842 53843 var FunnelSeries = extendSeriesModel({ 53844 53845 type: 'series.funnel', 53846 53847 init: function (option) { 53848 FunnelSeries.superApply(this, 'init', arguments); 53849 53850 // Enable legend selection for each data item 53851 // Use a function instead of direct access because data reference may changed 53852 this.legendDataProvider = function () { 53853 return this.getRawData(); 53854 }; 53855 // Extend labelLine emphasis 53856 this._defaultLabelLine(option); 53857 }, 53858 53859 getInitialData: function (option, ecModel) { 53860 return createListSimply(this, ['value']); 53861 }, 53862 53863 _defaultLabelLine: function (option) { 53864 // Extend labelLine emphasis 53865 defaultEmphasis(option, 'labelLine', ['show']); 53866
53867 var labelLineNormalOpt = option.labelLine; 53868 var labelLineEmphasisOpt = option.emphasis.labelLine; 53869 // Not show label line if `label.normal.show = false` 53870 labelLineNormalOpt.show = labelLineNormalOpt.show 53871 && option.label.show; 53872 labelLineEmphasisOpt.show = labelLineEmphasisOpt.show 53873 && option.emphasis.label.show; 53874 }, 53875 53876 // Overwrite 53877 getDataParams: function (dataIndex) { 53878 var data = this.getData(); 53879 var params = FunnelSeries.superCall(this, 'getDataParams', dataIndex); 53880 var valueDim = data.mapDimension('value'); 53881 var sum = data.getSum(valueDim); 53882 // Percent is 0 if sum is 0 53883 params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2); 53884 53885 params.$vars.push('percent'); 53886 return params; 53887 }, 53888 53889 defaultOption: { 53890 zlevel: 0, // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 53891 z: 2, // äºÅ级å±âå 53892 legendHoverLink: true, 53893 left: 80, 53894 top: 60, 53895 right: 80, 53896 bottom: 60, 53897 // width: {totalWidth} - left - right, 53898 // height: {totalHeight} - top - bottom, 53899 53900 // é»Ë认åÂâæâ¢Â°Ã¦Â®æÅâ¬Ã¥Â°ÂæÅâ¬Ã¥Â¤Â§Ã¥â¬Â¼ 53901 // min: 0, 53902 // max: 100, 53903 minSize: '0%', 53904 maxSize: '100%', 53905 sort: 'descending', // 'ascending', 'descending' 53906 gap: 0, 53907 funnelAlign: 'center', 53908 label: { 53909 show: true, 53910 position: 'outer' 53911 // formatter: æ â¡Ã§Â¾æââ¡Ã¦Å¾ ¼å¼ÂÃ¥â¢Â¨Ã¯Â¼ÅÃ¥ÂÅTooltip.formatterï¼Åä¸Âæâ¯æÅÂå¼âæÂ¥åâºÅ¾Ã¨Â°Æ 53912 }, 53913 labelLine: { 53914 show: true, 53915 length: 20, 53916 lineStyle: { 53917 // color: Ã¥Ââå¼â, 53918 width: 1, 53919 type: 'solid' 53920 } 53921 }, 53922 itemStyle: { 53923 // color: Ã¥Ââå¼â, 53924 borderColor: '#fff', 53925 borderWidth: 1 53926 }, 53927 emphasis: { 53928 label: { 53929 show: true 53930 } 53931 } 53932 } 53933 }); 53934 53935 /* 53936* Licensed to the Apache Software Foundation (ASF) under one 53937* or more contributor license agreements. See the NOTICE file 53938* distributed with this work for additional information 53939* regarding copyright ownership. The ASF licenses this file 53940* to you under the Apache License, Version 2.0 (the 53941* "License"); you may not use this file except in compliance 53942* with the License. You may obtain a copy of the License at 53943* 53944* http://www.apache.org/licenses/LICENSE-2.0 53945* 53946* Unless required by applicable law or agreed to in writing, 53947* software distributed under the License is distributed on an 53948* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 53949* KIND, either express or implied. See the License for the 53950* specific language governing permissions and limitations 53951* under the License. 53952*/ 53953 53954 /** 53955 * Piece of pie including Sector, Label, LabelLine 53956 * @constructor 53957 * @extends {module:zrender/graphic/Group} 53958 */ 53959 function FunnelPiece(data, idx) { 53960 53961 Group.call(this); 53962 53963 var polygon = new Polygon(); 53964 var labelLine = new Polyline(); 53965 var text = new Text(); 53966 this.add(polygon); 53967 this.add(labelLine); 53968 this.add(text); 53969 53970 this.updateData(data, idx, true); 53971 53972 // Hover to change label and labelLine 53973 function onEmphasis() { 53974 labelLine.ignore = labelLine.hoverIgnore; 53975 text.ignore = text.hoverIgnore; 53976 } 53977 function onNormal() { 53978 labelLine.ignore = labelLine.normalIgnore; 53979 text.ignore = text.normalIgnore; 53980 } 53981 this.on('emphasis', onEmphasis) 53982 .on('normal', onNormal) 53983 .on('mouseover', onEmphasis) 53984 .on('mouseout', onNormal); 53985 } 53986 53987 var funnelPieceProto = FunnelPiece.prototype; 53988 53989 var opacityAccessPath = ['itemStyle', 'opacity']; 53990 funnelPieceProto.updateData = function (data, idx, firstCreate) { 53991 53992 var polygon = this.childAt(0); 53993 53994 var seriesModel = data.hostModel; 53995 var itemModel = data.getItemModel(idx); 53996 var layout = data.getItemLayout(idx); 53997 var opacity = data.getItemModel(idx).get(opacityAccessPath); 53998 opacity = opacity == null ? 1 : opacity; 53999 54000 // Reset style 54001 polygon.useStyle({}); 54002 54003 if (firstCreate) { 54004 polygon.setShape({ 54005 points: layout.points 54006 });
54007 polygon.setStyle({ opacity : 0 }); 54008 initProps(polygon, { 54009 style: { 54010 opacity: opacity 54011 } 54012 }, seriesModel, idx); 54013 } 54014 else { 54015 updateProps(polygon, { 54016 style: { 54017 opacity: opacity 54018 }, 54019 shape: { 54020 points: layout.points 54021 } 54022 }, seriesModel, idx); 54023 } 54024 54025 // Update common style 54026 var itemStyleModel = itemModel.getModel('itemStyle'); 54027 var visualColor = data.getItemVisual(idx, 'color'); 54028 54029 polygon.setStyle( 54030 defaults( 54031 { 54032 lineJoin: 'round', 54033 fill: visualColor 54034 }, 54035 itemStyleModel.getItemStyle(['opacity']) 54036 ) 54037 ); 54038 polygon.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle(); 54039 54040 this._updateLabel(data, idx); 54041 54042 setHoverStyle(this); 54043 }; 54044 54045 funnelPieceProto._updateLabel = function (data, idx) { 54046 54047 var labelLine = this.childAt(1); 54048 var labelText = this.childAt(2); 54049 54050 var seriesModel = data.hostModel; 54051 var itemModel = data.getItemModel(idx); 54052 var layout = data.getItemLayout(idx); 54053 var labelLayout = layout.label; 54054 var visualColor = data.getItemVisual(idx, 'color'); 54055 54056 updateProps(labelLine, { 54057 shape: { 54058 points: labelLayout.linePoints || labelLayout.linePoints 54059 } 54060 }, seriesModel, idx); 54061 54062 updateProps(labelText, { 54063 style: { 54064 x: labelLayout.x, 54065 y: labelLayout.y 54066 } 54067 }, seriesModel, idx); 54068 labelText.attr({ 54069 rotation: labelLayout.rotation, 54070 origin: [labelLayout.x, labelLayout.y], 54071 z2: 10 54072 }); 54073 54074 var labelModel = itemModel.getModel('label'); 54075 var labelHoverModel = itemModel.getModel('emphasis.label'); 54076 var labelLineModel = itemModel.getModel('labelLine'); 54077 var labelLineHoverModel = itemModel.getModel('emphasis.labelLine'); 54078 var visualColor = data.getItemVisual(idx, 'color'); 54079 54080 setLabelStyle( 54081 labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel, 54082 { 54083 labelFetcher: data.hostModel, 54084 labelDataIndex: idx, 54085 defaultText: data.getName(idx), 54086 autoColor: visualColor, 54087 useInsideStyle: !!labelLayout.inside 54088 }, 54089 { 54090 textAlign: labelLayout.textAlign, 54091 textVerticalAlign: labelLayout.verticalAlign 54092 } 54093 ); 54094 54095 labelText.ignore = labelText.normalIgnore = !labelModel.get('show'); 54096 labelText.hoverIgnore = !labelHoverModel.get('show'); 54097 54098 labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show'); 54099 labelLine.hoverIgnore = !labelLineHoverModel.get('show'); 54100 54101 // Default use item visual color 54102 labelLine.setStyle({ 54103 stroke: visualColor 54104 }); 54105 labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle()); 54106 54107 labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); 54108 }; 54109 54110 inherits(FunnelPiece, Group); 54111 54112 54113 var FunnelView = Chart.extend({ 54114 54115 type: 'funnel', 54116 54117 render: function (seriesModel, ecModel, api) { 54118 var data = seriesModel.getData(); 54119 var oldData = this._data; 54120 54121 var group = this.group; 54122 54123 data.diff(oldData) 54124 .add(function (idx) { 54125 var funnelPiece = new FunnelPiece(data, idx); 54126 54127 data.setItemGraphicEl(idx, funnelPiece); 54128 54129 group.add(funnelPiece); 54130 }) 54131 .update(function (newIdx, oldIdx) { 54132 var piePiece = oldData.getItemGraphicEl(oldIdx); 54133 54134 piePiece.updateData(data, newIdx); 54135 54136 group.add(piePiece); 54137 data.setItemGraphicEl(newIdx, piePiece); 54138 }) 54139 .remove(function (idx) { 54140 var piePiece = oldData.getItemGraphicEl(idx); 54141 group.remove(piePiece); 54142 }) 54143 .execute(); 54144 54145 this._data = data; 54146 }, 54147 54148 remove: function () { 54149 this.group.removeAll(); 54150 this._data = null; 54151 }, 54152 54153 dispose: function () {} 54154 });
54155 54156 /* 54157* Licensed to the Apache Software Foundation (ASF) under one 54158* or more contributor license agreements. See the NOTICE file 54159* distributed with this work for additional information 54160* regarding copyright ownership. The ASF licenses this file 54161* to you under the Apache License, Version 2.0 (the 54162* "License"); you may not use this file except in compliance 54163* with the License. You may obtain a copy of the License at 54164* 54165* http://www.apache.org/licenses/LICENSE-2.0 54166* 54167* Unless required by applicable law or agreed to in writing, 54168* software distributed under the License is distributed on an 54169* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54170* KIND, either express or implied. See the License for the 54171* specific language governing permissions and limitations 54172* under the License. 54173*/ 54174 54175 function getViewRect$2(seriesModel, api) { 54176 return getLayoutRect( 54177 seriesModel.getBoxLayoutParams(), { 54178 width: api.getWidth(), 54179 height: api.getHeight() 54180 } 54181 ); 54182 } 54183 54184 function getSortedIndices(data, sort) { 54185 var valueDim = data.mapDimension('value'); 54186 var valueArr = data.mapArray(valueDim, function (val) { 54187 return val; 54188 }); 54189 var indices = []; 54190 var isAscending = sort === 'ascending'; 54191 for (var i = 0, len = data.count(); i < len; i++) { 54192 indices[i] = i; 54193 } 54194 54195 // Add custom sortable function & none sortable opetion by "options.sort" 54196 if (typeof sort === 'function') { 54197 indices.sort(sort); 54198 } else if (sort !== 'none') { 54199 indices.sort(function (a, b) { 54200 return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a]; 54201 }); 54202 } 54203 return indices; 54204 } 54205 54206 function labelLayout$1(data) { 54207 data.each(function (idx) { 54208 var itemModel = data.getItemModel(idx); 54209 var labelModel = itemModel.getModel('label'); 54210 var labelPosition = labelModel.get('position'); 54211 54212 var labelLineModel = itemModel.getModel('labelLine'); 54213 54214 var layout = data.getItemLayout(idx); 54215 var points = layout.points; 54216 54217 var isLabelInside = labelPosition === 'inner' 54218 || labelPosition === 'inside' || labelPosition === 'center'; 54219 54220 var textAlign; 54221 var textX; 54222 var textY; 54223 var linePoints; 54224 54225 if (isLabelInside) { 54226 textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4; 54227 textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4; 54228 textAlign = 'center'; 54229 linePoints = [ 54230 [textX, textY], [textX, textY] 54231 ]; 54232 } 54233 else { 54234 var x1; 54235 var y1; 54236 var x2; 54237 var labelLineLen = labelLineModel.get('length'); 54238 if (labelPosition === 'left') { 54239 // Left side 54240 x1 = (points[3][0] + points[0][0]) / 2; 54241 y1 = (points[3][1] + points[0][1]) / 2; 54242 x2 = x1 - labelLineLen; 54243 textX = x2 - 5; 54244 textAlign = 'right'; 54245 } 54246 else { 54247 // Right side 54248 x1 = (points[1][0] + points[2][0]) / 2; 54249 y1 = (points[1][1] + points[2][1]) / 2; 54250 x2 = x1 + labelLineLen; 54251 textX = x2 + 5; 54252 textAlign = 'left'; 54253 } 54254 var y2 = y1; 54255 54256 linePoints = [[x1, y1], [x2, y2]]; 54257 textY = y2; 54258 } 54259 54260 layout.label = { 54261 linePoints: linePoints, 54262 x: textX, 54263 y: textY, 54264 verticalAlign: 'middle', 54265 textAlign: textAlign, 54266 inside: isLabelInside 54267 }; 54268 }); 54269 } 54270 54271 var funnelLayout = function (ecModel, api, payload) { 54272 ecModel.eachSeriesByType('funnel', function (seriesModel) { 54273 var data = seriesModel.getData(); 54274 var valueDim = data.mapDimension('value'); 54275 var sort = seriesModel.get('sort'); 54276 var viewRect = getViewRect$2(seriesModel, api); 54277 var indices = getSortedIndices(data, sort); 54278 54279 var sizeExtent = [
54280 parsePercent$1(seriesModel.get('minSize'), viewRect.width), 54281 parsePercent$1(seriesModel.get('maxSize'), viewRect.width) 54282 ]; 54283 var dataExtent = data.getDataExtent(valueDim); 54284 var min = seriesModel.get('min'); 54285 var max = seriesModel.get('max'); 54286 if (min == null) { 54287 min = Math.min(dataExtent[0], 0); 54288 } 54289 if (max == null) { 54290 max = dataExtent[1]; 54291 } 54292 54293 var funnelAlign = seriesModel.get('funnelAlign'); 54294 var gap = seriesModel.get('gap'); 54295 var itemHeight = (viewRect.height - gap * (data.count() - 1)) / data.count(); 54296 54297 var y = viewRect.y; 54298 54299 var getLinePoints = function (idx, offY) { 54300 // End point index is data.count() and we assign it 0 54301 var val = data.get(valueDim, idx) || 0; 54302 var itemWidth = linearMap(val, [min, max], sizeExtent, true); 54303 var x0; 54304 switch (funnelAlign) { 54305 case 'left': 54306 x0 = viewRect.x; 54307 break; 54308 case 'center': 54309 x0 = viewRect.x + (viewRect.width - itemWidth) / 2; 54310 break; 54311 case 'right': 54312 x0 = viewRect.x + viewRect.width - itemWidth; 54313 break; 54314 } 54315 return [ 54316 [x0, offY], 54317 [x0 + itemWidth, offY] 54318 ]; 54319 }; 54320 54321 if (sort === 'ascending') { 54322 // From bottom to top 54323 itemHeight = -itemHeight; 54324 gap = -gap; 54325 y += viewRect.height; 54326 indices = indices.reverse(); 54327 } 54328 54329 for (var i = 0; i < indices.length; i++) { 54330 var idx = indices[i]; 54331 var nextIdx = indices[i + 1]; 54332 54333 var itemModel = data.getItemModel(idx); 54334 var height = itemModel.get('itemStyle.height'); 54335 if (height == null) { 54336 height = itemHeight; 54337 } 54338 else { 54339 height = parsePercent$1(height, viewRect.height); 54340 if (sort === 'ascending') { 54341 height = -height; 54342 } 54343 } 54344 54345 var start = getLinePoints(idx, y); 54346 var end = getLinePoints(nextIdx, y + height); 54347 54348 y += height + gap; 54349 54350 data.setItemLayout(idx, { 54351 points: start.concat(end.slice().reverse()) 54352 }); 54353 } 54354 54355 labelLayout$1(data); 54356 }); 54357 }; 54358 54359 /* 54360* Licensed to the Apache Software Foundation (ASF) under one 54361* or more contributor license agreements. See the NOTICE file 54362* distributed with this work for additional information 54363* regarding copyright ownership. The ASF licenses this file 54364* to you under the Apache License, Version 2.0 (the 54365* "License"); you may not use this file except in compliance 54366* with the License. You may obtain a copy of the License at 54367* 54368* http://www.apache.org/licenses/LICENSE-2.0 54369* 54370* Unless required by applicable law or agreed to in writing, 54371* software distributed under the License is distributed on an 54372* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54373* KIND, either express or implied. See the License for the 54374* specific language governing permissions and limitations 54375* under the License. 54376*/ 54377 54378 registerVisual(dataColor('funnel')); 54379 registerLayout(funnelLayout); 54380 registerProcessor(dataFilter('funnel')); 54381 54382 /* 54383* Licensed to the Apache Software Foundation (ASF) under one 54384* or more contributor license agreements. See the NOTICE file 54385* distributed with this work for additional information 54386* regarding copyright ownership. The ASF licenses this file 54387* to you under the Apache License, Version 2.0 (the 54388* "License");
54388 you may not use this file except in compliance 54389* with the License. You may obtain a copy of the License at 54390* 54391* http://www.apache.org/licenses/LICENSE-2.0 54392* 54393* Unless required by applicable law or agreed to in writing, 54394* software distributed under the License is distributed on an 54395* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54396* KIND, either express or implied. See the License for the 54397* specific language governing permissions and limitations 54398* under the License. 54399*/ 54400 54401 var parallelPreprocessor = function (option) { 54402 createParallelIfNeeded(option); 54403 mergeAxisOptionFromParallel(option); 54404 }; 54405 54406 /** 54407 * Create a parallel coordinate if not exists. 54408 * @inner 54409 */ 54410 function createParallelIfNeeded(option) { 54411 if (option.parallel) { 54412 return; 54413 } 54414 54415 var hasParallelSeries = false; 54416 54417 each$1(option.series, function (seriesOpt) { 54418 if (seriesOpt && seriesOpt.type === 'parallel') { 54419 hasParallelSeries = true; 54420 } 54421 }); 54422 54423 if (hasParallelSeries) { 54424 option.parallel = [{}]; 54425 } 54426 } 54427 54428 /** 54429 * Merge aixs definition from parallel option (if exists) to axis option. 54430 * @inner 54431 */ 54432 function mergeAxisOptionFromParallel(option) { 54433 var axes = normalizeToArray(option.parallelAxis); 54434 54435 each$1(axes, function (axisOption) { 54436 if (!isObject$1(axisOption)) { 54437 return; 54438 } 54439 54440 var parallelIndex = axisOption.parallelIndex || 0; 54441 var parallelOption = normalizeToArray(option.parallel)[parallelIndex]; 54442 54443 if (parallelOption && parallelOption.parallelAxisDefault) { 54444 merge(axisOption, parallelOption.parallelAxisDefault, false); 54445 } 54446 }); 54447 } 54448 54449 /* 54450* Licensed to the Apache Software Foundation (ASF) under one 54451* or more contributor license agreements. See the NOTICE file 54452* distributed with this work for additional information 54453* regarding copyright ownership. The ASF licenses this file 54454* to you under the Apache License, Version 2.0 (the 54455* "License"); you may not use this file except in compliance 54456* with the License. You may obtain a copy of the License at 54457* 54458* http://www.apache.org/licenses/LICENSE-2.0 54459* 54460* Unless required by applicable law or agreed to in writing, 54461* software distributed under the License is distributed on an 54462* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54463* KIND, either express or implied. See the License for the 54464* specific language governing permissions and limitations 54465* under the License. 54466*/ 54467 54468 /** 54469 * @constructor module:echarts/coord/parallel/ParallelAxis 54470 * @extends {module:echarts/coord/Axis} 54471 * @param {string} dim 54472 * @param {*} scale 54473 * @param {Array.<number>} coordExtent 54474 * @param {string} axisType 54475 */ 54476 var ParallelAxis = function (dim, scale, coordExtent, axisType, axisIndex) { 54477 54478 Axis.call(this, dim, scale, coordExtent); 54479 54480 /** 54481 * Axis type 54482 * - 'category' 54483 * - 'value' 54484 * - 'time' 54485 * - 'log' 54486 * @type {string} 54487 */ 54488 this.type = axisType || 'value'; 54489 54490 /** 54491 * @type {number} 54492 * @readOnly 54493 */ 54494 this.axisIndex = axisIndex; 54495 }; 54496 54497 ParallelAxis.prototype = { 54498 54499 constructor: ParallelAxis, 54500 54501 /** 54502 * Axis model 54503 * @param {module:echarts/coord/parallel/AxisModel} 54504 */ 54505 model: null, 54506 54507 /** 54508 * @override 54509 */ 54510 isHorizontal: function () { 54511 return this.coordinateSystem.getModel().get('layout') !== 'horizontal'; 54512 } 54513 54514 }; 54515 54516 inherits(ParallelAxis, Axis); 54517 54518 /* 54519* Licensed to the Apache Software Foundation (ASF) under one 54520* or more contributor license agreements. See the NOTICE file 54521* distributed with this work for additional information 54522* regarding copyright ownership. The ASF licenses this file 54523* to you under the Apache License, Version 2.0 (the 54524* "License");
54524 you may not use this file except in compliance 54525* with the License. You may obtain a copy of the License at 54526* 54527* http://www.apache.org/licenses/LICENSE-2.0 54528* 54529* Unless required by applicable law or agreed to in writing, 54530* software distributed under the License is distributed on an 54531* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54532* KIND, either express or implied. See the License for the 54533* specific language governing permissions and limitations 54534* under the License. 54535*/ 54536 54537 /** 54538 * Calculate slider move result. 54539 * Usage: 54540 * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as 54541 * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`. 54542 * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`. 54543 * 54544 * @param {number} delta Move length. 54545 * @param {Array.<number>} handleEnds handleEnds[0] can be bigger then handleEnds[1]. 54546 * handleEnds will be modified in this method. 54547 * @param {Array.<number>} extent handleEnds is restricted by extent. 54548 * extent[0] should less or equals than extent[1]. 54549 * @param {number|string} handleIndex Can be 'all', means that both move the two handleEnds, 54550 * where the input minSpan and maxSpan will not work. 54551 * @param {number} [minSpan] The range of dataZoom can not be smaller than that. 54552 * If not set, handle0 and cross handle1. If set as a non-negative 54553 * number (including `0`), handles will push each other when reaching 54554 * the minSpan. 54555 * @param {number} [maxSpan] The range of dataZoom can not be larger than that. 54556 * @return {Array.<number>} The input handleEnds. 54557 */ 54558 var sliderMove = function (delta, handleEnds, extent, handleIndex, minSpan, maxSpan) { 54559 // Normalize firstly. 54560 handleEnds[0] = restrict$1(handleEnds[0], extent); 54561 handleEnds[1] = restrict$1(handleEnds[1], extent); 54562 54563 delta = delta || 0; 54564 54565 var extentSpan = extent[1] - extent[0]; 54566 54567 // Notice maxSpan and minSpan can be null/undefined. 54568 if (minSpan != null) { 54569 minSpan = restrict$1(minSpan, [0, extentSpan]); 54570 } 54571 if (maxSpan != null) { 54572 maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0); 54573 } 54574 if (handleIndex === 'all') { 54575 minSpan = maxSpan = Math.abs(handleEnds[1] - handleEnds[0]); 54576 handleIndex = 0; 54577 } 54578 54579 var originalDistSign = getSpanSign(handleEnds, handleIndex); 54580 54581 handleEnds[handleIndex] += delta; 54582 54583 // Restrict in extent. 54584 var extentMinSpan = minSpan || 0; 54585 var realExtent = extent.slice(); 54586 originalDistSign.sign < 0 ? (realExtent[0] += extentMinSpan) : (realExtent[1] -= extentMinSpan); 54587 handleEnds[handleIndex] = restrict$1(handleEnds[handleIndex], realExtent); 54588 54589 // Expand span. 54590 var currDistSign = getSpanSign(handleEnds, handleIndex); 54591 if (minSpan != null && ( 54592 currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan 54593 )) { 54594 // If minSpan exists, 'cross' is forbinden. 54595 handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan; 54596 } 54597 54598 // Shrink span. 54599 var currDistSign = getSpanSign(handleEnds, handleIndex); 54600 if (maxSpan != null && currDistSign.span > maxSpan) { 54601 handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan; 54602 } 54603 54604 return handleEnds; 54605 }; 54606 54607 function getSpanSign(handleEnds, handleIndex) { 54608 var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; 54609 // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0] 54610 // is at left of handleEnds[1] for non-cross case. 54611 return {span: Math.abs(dist), sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1}; 54612 } 54613 54614 function restrict$1(value, extend) { 54615 return Math.min(extend[1], Math.max(extend[0], value)); 54616 } 54617 54618 /* 54619* Licensed to the Apache Software Foundation (ASF) under one 54620* or more contributor license agreements. See the NOTICE file 54621* distributed with this work for additional information 54622* regarding copyright ownership. The ASF licenses this file 54623* to you under the Apache License, Version 2.0 (the 54624* "License");
54624 you may not use this file except in compliance 54625* with the License. You may obtain a copy of the License at 54626* 54627* http://www.apache.org/licenses/LICENSE-2.0 54628* 54629* Unless required by applicable law or agreed to in writing, 54630* software distributed under the License is distributed on an 54631* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 54632* KIND, either express or implied. See the License for the 54633* specific language governing permissions and limitations 54634* under the License. 54635*/ 54636 54637 /** 54638 * Parallel Coordinates 54639 * <https://en.wikipedia.org/wiki/Parallel_coordinates> 54640 */ 54641 54642 var each$11 = each$1; 54643 var mathMin$5 = Math.min; 54644 var mathMax$5 = Math.max; 54645 var mathFloor$2 = Math.floor; 54646 var mathCeil$2 = Math.ceil; 54647 var round$2 = round$1; 54648 54649 var PI$3 = Math.PI; 54650 54651 function Parallel(parallelModel, ecModel, api) { 54652 54653 /** 54654 * key: dimension 54655 * @type {Object.<string, module:echarts/coord/parallel/Axis>} 54656 * @private 54657 */ 54658 this._axesMap = createHashMap(); 54659 54660 /** 54661 * key: dimension 54662 * value: {position: [], rotation, } 54663 * @type {Object.<string, Object>} 54664 * @private 54665 */ 54666 this._axesLayout = {}; 54667 54668 /** 54669 * Always follow axis order. 54670 * @type {Array.<string>} 54671 * @readOnly 54672 */ 54673 this.dimensions = parallelModel.dimensions; 54674 54675 /** 54676 * @type {module:zrender/core/BoundingRect} 54677 */ 54678 this._rect; 54679 54680 /** 54681 * @type {module:echarts/coord/parallel/ParallelModel} 54682 */ 54683 this._model = parallelModel; 54684 54685 this._init(parallelModel, ecModel, api); 54686 } 54687 54688 Parallel.prototype = { 54689 54690 type: 'parallel', 54691 54692 constructor: Parallel, 54693 54694 /** 54695 * Initialize cartesian coordinate systems 54696 * @private 54697 */ 54698 _init: function (parallelModel, ecModel, api) { 54699 54700 var dimensions = parallelModel.dimensions; 54701 var parallelAxisIndex = parallelModel.parallelAxisIndex; 54702 54703 each$11(dimensions, function (dim, idx) { 54704 54705 var axisIndex = parallelAxisIndex[idx]; 54706 var axisModel = ecModel.getComponent('parallelAxis', axisIndex); 54707 54708 var axis = this._axesMap.set(dim, new ParallelAxis( 54709 dim, 54710 createScaleByModel(axisModel), 54711 [0, 0], 54712 axisModel.get('type'), 54713 axisIndex 54714 )); 54715 54716 var isCategory = axis.type === 'category'; 54717 axis.onBand = isCategory && axisModel.get('boundaryGap'); 54718 axis.inverse = axisModel.get('inverse'); 54719 54720 // Injection 54721 axisModel.axis = axis; 54722 axis.model = axisModel; 54723 axis.coordinateSystem = axisModel.coordinateSystem = this; 54724 54725 }, this); 54726 }, 54727 54728 /** 54729 * Update axis scale after data processed 54730 * @param {module:echarts/model/Global} ecModel 54731 * @param {module:echarts/ExtensionAPI} api 54732 */ 54733 update: function (ecModel, api) { 54734 this._updateAxesFromSeries(this._model, ecModel); 54735 }, 54736 54737 /** 54738 * @override 54739 */ 54740 containPoint: function (point) { 54741 var layoutInfo = this._makeLayoutInfo(); 54742 var axisBase = layoutInfo.axisBase; 54743 var layoutBase = layoutInfo.layoutBase; 54744 var pixelDimIndex = layoutInfo.pixelDimIndex; 54745 var pAxis = point[1 - pixelDimIndex]; 54746 var pLayout = point[pixelDimIndex]; 54747 54748 return pAxis >= axisBase 54749 && pAxis <= axisBase + layoutInfo.axisLength 54750 && pLayout >= layoutBase 54751 && pLayout <= layoutBase + layoutInfo.layoutLength; 54752 }, 54753 54754 getModel: function () { 54755 return this._model; 54756 }, 54757 54758 /** 54759 * Update properties from series 54760 * @private 54761 */ 54762 _updateAxesFromSeries: function (parallelModel, ecModel) { 54763 ecModel.eachSeries(function (seriesModel) { 54764
vendor: 7,615 bytes, lines 54765-54960
54765 if (!parallelModel.contains(seriesModel, ecModel)) { 54766 return; 54767 } 54768 54769 var data = seriesModel.getData(); 54770 54771 each$11(this.dimensions, function (dim) { 54772 var axis = this._axesMap.get(dim); 54773 axis.scale.unionExtentFromData(data, data.mapDimension(dim)); 54774 niceScaleExtent(axis.scale, axis.model); 54775 }, this); 54776 }, this); 54777 }, 54778 54779 /** 54780 * Resize the parallel coordinate system. 54781 * @param {module:echarts/coord/parallel/ParallelModel} parallelModel 54782 * @param {module:echarts/ExtensionAPI} api 54783 */ 54784 resize: function (parallelModel, api) { 54785 this._rect = getLayoutRect( 54786 parallelModel.getBoxLayoutParams(), 54787 { 54788 width: api.getWidth(), 54789 height: api.getHeight() 54790 } 54791 ); 54792 54793 this._layoutAxes(); 54794 }, 54795 54796 /** 54797 * @return {module:zrender/core/BoundingRect} 54798 */ 54799 getRect: function () { 54800 return this._rect; 54801 }, 54802 54803 /** 54804 * @private 54805 */ 54806 _makeLayoutInfo: function () { 54807 var parallelModel = this._model; 54808 var rect = this._rect; 54809 var xy = ['x', 'y']; 54810 var wh = ['width', 'height']; 54811 var layout = parallelModel.get('layout'); 54812 var pixelDimIndex = layout === 'horizontal' ? 0 : 1; 54813 var layoutLength = rect[wh[pixelDimIndex]]; 54814 var layoutExtent = [0, layoutLength]; 54815 var axisCount = this.dimensions.length; 54816 54817 var axisExpandWidth = restrict(parallelModel.get('axisExpandWidth'), layoutExtent); 54818 var axisExpandCount = restrict(parallelModel.get('axisExpandCount') || 0, [0, axisCount]); 54819 var axisExpandable = parallelModel.get('axisExpandable') 54820 && axisCount > 3 54821 && axisCount > axisExpandCount 54822 && axisExpandCount > 1 54823 && axisExpandWidth > 0 54824 && layoutLength > 0; 54825 54826 // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength], 54827 // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow), 54828 // where collapsed axes should be overlapped. 54829 var axisExpandWindow = parallelModel.get('axisExpandWindow'); 54830 var winSize; 54831 if (!axisExpandWindow) { 54832 winSize = restrict(axisExpandWidth * (axisExpandCount - 1), layoutExtent); 54833 var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor$2(axisCount / 2); 54834 axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2]; 54835 axisExpandWindow[1] = axisExpandWindow[0] + winSize; 54836 } 54837 else { 54838 winSize = restrict(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent); 54839 axisExpandWindow[1] = axisExpandWindow[0] + winSize; 54840 } 54841 54842 var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount); 54843 // Avoid axisCollapseWidth is too small. 54844 axisCollapseWidth < 3 && (axisCollapseWidth = 0); 54845 54846 // Find the first and last indices > ewin[0] and < ewin[1]. 54847 var winInnerIndices = [ 54848 mathFloor$2(round$2(axisExpandWindow[0] / axisExpandWidth, 1)) + 1, 54849 mathCeil$2(round$2(axisExpandWindow[1] / axisExpandWidth, 1)) - 1 54850 ]; 54851 54852 // Pos in ec coordinates. 54853 var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0]; 54854 54855 return { 54856 layout: layout, 54857 pixelDimIndex: pixelDimIndex, 54858 layoutBase: rect[xy[pixelDimIndex]], 54859 layoutLength: layoutLength, 54860 axisBase: rect[xy[1 - pixelDimIndex]], 54861 axisLength: rect[wh[1 - pixelDimIndex]], 54862 axisExpandable: axisExpandable, 54863 axisExpandWidth: axisExpandWidth, 54864 axisCollapseWidth: axisCollapseWidth, 54865 axisExpandWindow: axisExpandWindow, 54866 axisCount: axisCount, 54867 winInnerIndices: winInnerIndices, 54868 axisExpandWindow0Pos: axisExpandWindow0Pos 54869 }; 54870 }, 54871 54872 /** 54873 * @private 54874 */ 54875 _layoutAxes: function () { 54876 var rect = this._rect; 54877 var axes = this._axesMap; 54878 var dimensions = this.dimensions; 54879 var layoutInfo = this._makeLayoutInfo(); 54880 var layout = layoutInfo.layout; 54881 54882 axes.each(function (axis) { 54883 var axisExtent = [0, layoutInfo.axisLength]; 54884 var idx = axis.inverse ? 1 : 0; 54885 axis.setExtent(axisExtent[idx], axisExtent[1 - idx]); 54886 }); 54887 54888 each$11(dimensions, function (dim, idx) { 54889 var posInfo = (layoutInfo.axisExpandable 54890 ? layoutAxisWithExpand : layoutAxisWithoutExpand 54891 )(idx, layoutInfo); 54892 54893 var positionTable = { 54894 horizontal: { 54895 x: posInfo.position, 54896 y: layoutInfo.axisLength 54897 }, 54898 vertical: { 54899 x: 0, 54900 y: posInfo.position 54901 } 54902 }; 54903 var rotationTable = { 54904 horizontal: PI$3 / 2, 54905 vertical: 0 54906 }; 54907 54908 var position = [ 54909 positionTable[layout].x + rect.x, 54910 positionTable[layout].y + rect.y 54911 ]; 54912 54913 var rotation = rotationTable[layout]; 54914 var transform = create$1(); 54915 rotate(transform, transform, rotation); 54916 translate(transform, transform, position); 54917 54918 // TODO 54919 // tickçÂâ°Ã¦Å½âå¸ÆÃ¤Â¿Â¡Ã¦Â¯ãâ¬â 54920 54921 // TODO 54922 // æ ¹æÂ®axis order æâºÂ´Ã¦â° dimensions顺åºÂãâ¬â 54923 54924 this._axesLayout[dim] = { 54925 position: position, 54926 rotation: rotation, 54927 transform: transform, 54928 axisNameAvailableWidth: posInfo.axisNameAvailableWidth, 54929 axisLabelShow: posInfo.axisLabelShow, 54930 nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth, 54931 tickDirection: 1, 54932 labelDirection: 1 54933 }; 54934 }, this); 54935 }, 54936 54937 /** 54938 * Get axis by dim. 54939 * @param {string} dim 54940 * @return {module:echarts/coord/parallel/ParallelAxis} [description] 54941 */ 54942 getAxis: function (dim) { 54943 return this._axesMap.get(dim); 54944 }, 54945 54946 /** 54947 * Convert a dim value of a single item of series data to Point. 54948 * @param {*} value 54949 * @param {string} dim 54950 * @return {Array} 54951 */ 54952 dataToPoint: function (value, dim) { 54953 return this.axisCoordToPoint( 54954 this._axesMap.get(dim).dataToCoord(value), 54955 dim 54956 ); 54957 }, 54958 54959 /** 54960 * Travel data for one time, get activeState of each data item.
54961 * @param {module:echarts/data/List} data 54962 * @param {Functio} cb param: {string} activeState 'active' or 'inactive' or 'normal' 54963 * {number} dataIndex 54964 * @param {number} [start=0] the start dataIndex that travel from. 54965 * @param {number} [end=data.count()] the next dataIndex of the last dataIndex will be travel. 54966 */ 54967 eachActiveState: function (data, callback, start, end) { 54968 start == null && (start = 0); 54969 end == null && (end = data.count()); 54970 54971 var axesMap = this._axesMap; 54972 var dimensions = this.dimensions; 54973 var dataDimensions = []; 54974 var axisModels = []; 54975 54976 each$1(dimensions, function (axisDim) { 54977 dataDimensions.push(data.mapDimension(axisDim)); 54978 axisModels.push(axesMap.get(axisDim).model); 54979 }); 54980 54981 var hasActiveSet = this.hasAxisBrushed(); 54982 54983 for (var dataIndex = start; dataIndex < end; dataIndex++) { 54984 var activeState; 54985 54986 if (!hasActiveSet) { 54987 activeState = 'normal'; 54988 } 54989 else { 54990 activeState = 'active'; 54991 var values = data.getValues(dataDimensions, dataIndex); 54992 for (var j = 0, lenj = dimensions.length; j < lenj; j++) { 54993 var state = axisModels[j].getActiveState(values[j]); 54994 54995 if (state === 'inactive') { 54996 activeState = 'inactive'; 54997 break; 54998 } 54999 } 55000 } 55001 55002 callback(activeState, dataIndex); 55003 } 55004 }, 55005 55006 /** 55007 * Whether has any activeSet. 55008 * @return {boolean} 55009 */ 55010 hasAxisBrushed: function () { 55011 var dimensions = this.dimensions; 55012 var axesMap = this._axesMap; 55013 var hasActiveSet = false; 55014 55015 for (var j = 0, lenj = dimensions.length; j < lenj; j++) { 55016 if (axesMap.get(dimensions[j]).model.getActiveState() !== 'normal') { 55017 hasActiveSet = true; 55018 } 55019 } 55020 55021 return hasActiveSet; 55022 }, 55023 55024 /** 55025 * Convert coords of each axis to Point. 55026 * Return point. For example: [10, 20] 55027 * @param {Array.<number>} coords 55028 * @param {string} dim 55029 * @return {Array.<number>} 55030 */ 55031 axisCoordToPoint: function (coord, dim) { 55032 var axisLayout = this._axesLayout[dim]; 55033 return applyTransform$1([coord, 0], axisLayout.transform); 55034 }, 55035 55036 /** 55037 * Get axis layout. 55038 */ 55039 getAxisLayout: function (dim) { 55040 return clone(this._axesLayout[dim]); 55041 }, 55042 55043 /** 55044 * @param {Array.<number>} point 55045 * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}. 55046 */ 55047 getSlidedAxisExpandWindow: function (point) { 55048 var layoutInfo = this._makeLayoutInfo(); 55049 var pixelDimIndex = layoutInfo.pixelDimIndex; 55050 var axisExpandWindow = layoutInfo.axisExpandWindow.slice(); 55051 var winSize = axisExpandWindow[1] - axisExpandWindow[0]; 55052 var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)]; 55053 55054 // Out of the area of coordinate system. 55055 if (!this.containPoint(point)) { 55056 return {behavior: 'none', axisExpandWindow: axisExpandWindow}; 55057 } 55058 55059 // Conver the point from global to expand coordinates. 55060 var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos; 55061 55062 // For dragging operation convenience, the window should not be 55063 // slided when mouse is the center area of the window. 55064 var delta; 55065 var behavior = 'slide'; 55066 var axisCollapseWidth = layoutInfo.axisCollapseWidth; 55067 var triggerArea = this._model.get('axisExpandSlideTriggerArea'); 55068 // But consider touch device, jump is necessary. 55069 var useJump = triggerArea[0] != null; 55070 55071 if (axisCollapseWidth) { 55072 if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) { 55073 behavior = 'jump'; 55074 delta = pointCoord - winSize * triggerArea[2]; 55075 } 55076 else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) { 55077 behavior = 'jump'; 55078 delta = pointCoord - winSize * (1 - triggerArea[2]); 55079 } 55080 else { 55081 (delta = pointCoord - winSize * triggerArea[1]) >= 0 55082 && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0 55083 && (delta = 0); 55084 } 55085 delta *= layoutInfo.axisExpandWidth / axisCollapseWidth; 55086 delta 55087 ? sliderMove(delta, axisExpandWindow, extent, 'all')
vendor: 4,190 bytes, lines 55088-55197
55088 // Avoid nonsense triger on mousemove. 55089 : (behavior = 'none'); 55090 } 55091 // When screen is too narrow, make it visible and slidable, although it is hard to interact. 55092 else { 55093 var winSize = axisExpandWindow[1] - axisExpandWindow[0]; 55094 var pos = extent[1] * pointCoord / winSize; 55095 axisExpandWindow = [mathMax$5(0, pos - winSize / 2)]; 55096 axisExpandWindow[1] = mathMin$5(extent[1], axisExpandWindow[0] + winSize); 55097 axisExpandWindow[0] = axisExpandWindow[1] - winSize; 55098 } 55099 55100 return { 55101 axisExpandWindow: axisExpandWindow, 55102 behavior: behavior 55103 }; 55104 } 55105 }; 55106 55107 function restrict(len, extent) { 55108 return mathMin$5(mathMax$5(len, extent[0]), extent[1]); 55109 } 55110 55111 function layoutAxisWithoutExpand(axisIndex, layoutInfo) { 55112 var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1); 55113 return { 55114 position: step * axisIndex, 55115 axisNameAvailableWidth: step, 55116 axisLabelShow: true 55117 }; 55118 } 55119 55120 function layoutAxisWithExpand(axisIndex, layoutInfo) { 55121 var layoutLength = layoutInfo.layoutLength; 55122 var axisExpandWidth = layoutInfo.axisExpandWidth; 55123 var axisCount = layoutInfo.axisCount; 55124 var axisCollapseWidth = layoutInfo.axisCollapseWidth; 55125 var winInnerIndices = layoutInfo.winInnerIndices; 55126 55127 var position; 55128 var axisNameAvailableWidth = axisCollapseWidth; 55129 var axisLabelShow = false; 55130 var nameTruncateMaxWidth; 55131 55132 if (axisIndex < winInnerIndices[0]) { 55133 position = axisIndex * axisCollapseWidth; 55134 nameTruncateMaxWidth = axisCollapseWidth; 55135 } 55136 else if (axisIndex <= winInnerIndices[1]) { 55137 position = layoutInfo.axisExpandWindow0Pos 55138 + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0]; 55139 axisNameAvailableWidth = axisExpandWidth; 55140 axisLabelShow = true; 55141 } 55142 else { 55143 position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth; 55144 nameTruncateMaxWidth = axisCollapseWidth; 55145 } 55146 55147 return { 55148 position: position, 55149 axisNameAvailableWidth: axisNameAvailableWidth, 55150 axisLabelShow: axisLabelShow, 55151 nameTruncateMaxWidth: nameTruncateMaxWidth 55152 }; 55153 } 55154 55155 /* 55156* Licensed to the Apache Software Foundation (ASF) under one 55157* or more contributor license agreements. See the NOTICE file 55158* distributed with this work for additional information 55159* regarding copyright ownership. The ASF licenses this file 55160* to you under the Apache License, Version 2.0 (the 55161* "License"); you may not use this file except in compliance 55162* with the License. You may obtain a copy of the License at 55163* 55164* http://www.apache.org/licenses/LICENSE-2.0 55165* 55166* Unless required by applicable law or agreed to in writing, 55167* software distributed under the License is distributed on an 55168* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 55169* KIND, either express or implied. See the License for the 55170* specific language governing permissions and limitations 55171* under the License. 55172*/ 55173 55174 /** 55175 * Parallel coordinate system creater. 55176 */ 55177 55178 function create$2(ecModel, api) { 55179 var coordSysList = []; 55180 55181 ecModel.eachComponent('parallel', function (parallelModel, idx) { 55182 var coordSys = new Parallel(parallelModel, ecModel, api); 55183 55184 coordSys.name = 'parallel_' + idx; 55185 coordSys.resize(parallelModel, api); 55186 55187 parallelModel.coordinateSystem = coordSys; 55188 coordSys.model = parallelModel; 55189 55190 coordSysList.push(coordSys); 55191 }); 55192 55193 // Inject the coordinateSystems into seriesModel 55194 ecModel.eachSeries(function (seriesModel) { 55195 if (seriesModel.get('coordinateSystem') === 'parallel') { 55196 var parallelModel = ecModel.queryComponents({ 55197 mainType: 'parallel',
55198 index: seriesModel.get('parallelIndex'), 55199 id: seriesModel.get('parallelId') 55200 })[0]; 55201 seriesModel.coordinateSystem = parallelModel.coordinateSystem; 55202 } 55203 }); 55204 55205 return coordSysList; 55206 } 55207 55208 CoordinateSystemManager.register('parallel', {create: create$2}); 55209 55210 /* 55211* Licensed to the Apache Software Foundation (ASF) under one 55212* or more contributor license agreements. See the NOTICE file 55213* distributed with this work for additional information 55214* regarding copyright ownership. The ASF licenses this file 55215* to you under the Apache License, Version 2.0 (the 55216* "License"); you may not use this file except in compliance 55217* with the License. You may obtain a copy of the License at 55218* 55219* http://www.apache.org/licenses/LICENSE-2.0 55220* 55221* Unless required by applicable law or agreed to in writing, 55222* software distributed under the License is distributed on an 55223* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 55224* KIND, either express or implied. See the License for the 55225* specific language governing permissions and limitations 55226* under the License. 55227*/ 55228 55229 var AxisModel$2 = ComponentModel.extend({ 55230 55231 type: 'baseParallelAxis', 55232 55233 /** 55234 * @type {module:echarts/coord/parallel/Axis} 55235 */ 55236 axis: null, 55237 55238 /** 55239 * @type {Array.<Array.<number>} 55240 * @readOnly 55241 */ 55242 activeIntervals: [], 55243 55244 /** 55245 * @return {Object} 55246 */ 55247 getAreaSelectStyle: function () { 55248 return makeStyleMapper( 55249 [ 55250 ['fill', 'color'], 55251 ['lineWidth', 'borderWidth'], 55252 ['stroke', 'borderColor'], 55253 ['width', 'width'], 55254 ['opacity', 'opacity'] 55255 ] 55256 )(this.getModel('areaSelectStyle')); 55257 }, 55258 55259 /** 55260 * The code of this feature is put on AxisModel but not ParallelAxis, 55261 * because axisModel can be alive after echarts updating but instance of 55262 * ParallelAxis having been disposed. this._activeInterval should be kept 55263 * when action dispatched (i.e. legend click). 55264 * 55265 * @param {Array.<Array<number>>} intervals interval.length === 0 55266 * means set all active. 55267 * @public 55268 */ 55269 setActiveIntervals: function (intervals) { 55270 var activeIntervals = this.activeIntervals = clone(intervals); 55271 55272 // Normalize 55273 if (activeIntervals) { 55274 for (var i = activeIntervals.length - 1; i >= 0; i--) { 55275 asc(activeIntervals[i]); 55276 } 55277 } 55278 }, 55279 55280 /** 55281 * @param {number|string} [value] When attempting to detect 'no activeIntervals set', 55282 * value can not be input. 55283 * @return {string} 'normal': no activeIntervals set, 55284 * 'active', 55285 * 'inactive'. 55286 * @public 55287 */ 55288 getActiveState: function (value) { 55289 var activeIntervals = this.activeIntervals; 55290 55291 if (!activeIntervals.length) { 55292 return 'normal'; 55293 } 55294 55295 if (value == null || isNaN(value)) { 55296 return 'inactive'; 55297 } 55298 55299 // Simple optimization 55300 if (activeIntervals.length === 1) { 55301 var interval = activeIntervals[0]; 55302 if (interval[0] <= value && value <= interval[1]) { 55303 return 'active'; 55304 } 55305 } 55306 else { 55307 for (var i = 0, len = activeIntervals.length; i < len; i++) { 55308 if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) { 55309 return 'active'; 55310 } 55311 } 55312 } 55313 55314 return 'inactive'; 55315 } 55316 55317 }); 55318 55319 var defaultOption$1 = { 55320 55321 type: 'value', 55322 55323 /** 55324 * @type {Array.<number>} 55325 */ 55326 dim: null, // 0, 1, 2, ... 55327 55328 // parallelIndex: null, 55329 55330 areaSelectStyle: { 55331 width: 20, 55332 borderWidth: 1, 55333 borderColor: 'rgba(160,197,232)', 55334 color: 'rgba(160,197,232)', 55335 opacity: 0.3 55336 }, 55337 55338 realtime: true, // Whether realtime update view when select. 55339 55340 z: 10 55341 }; 55342 55343 merge(AxisModel$2.prototype, axisModelCommonMixin);
55344 55345 function getAxisType$1(axisName, option) { 55346 return option.type || (option.data ? 'category' : 'value'); 55347 } 55348 55349 axisModelCreator('parallel', AxisModel$2, getAxisType$1, defaultOption$1); 55350 55351 /* 55352* Licensed to the Apache Software Foundation (ASF) under one 55353* or more contributor license agreements. See the NOTICE file 55354* distributed with this work for additional information 55355* regarding copyright ownership. The ASF licenses this file 55356* to you under the Apache License, Version 2.0 (the 55357* "License"); you may not use this file except in compliance 55358* with the License. You may obtain a copy of the License at 55359* 55360* http://www.apache.org/licenses/LICENSE-2.0 55361* 55362* Unless required by applicable law or agreed to in writing, 55363* software distributed under the License is distributed on an 55364* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 55365* KIND, either express or implied. See the License for the 55366* specific language governing permissions and limitations 55367* under the License. 55368*/ 55369 55370 ComponentModel.extend({ 55371 55372 type: 'parallel', 55373 55374 dependencies: ['parallelAxis'], 55375 55376 /** 55377 * @type {module:echarts/coord/parallel/Parallel} 55378 */ 55379 coordinateSystem: null, 55380 55381 /** 55382 * Each item like: 'dim0', 'dim1', 'dim2', ... 55383 * @type {Array.<string>} 55384 * @readOnly 55385 */ 55386 dimensions: null, 55387 55388 /** 55389 * Coresponding to dimensions. 55390 * @type {Array.<number>} 55391 * @readOnly 55392 */ 55393 parallelAxisIndex: null, 55394 55395 layoutMode: 'box', 55396 55397 defaultOption: { 55398 zlevel: 0, 55399 z: 0, 55400 left: 80, 55401 top: 60, 55402 right: 80, 55403 bottom: 60, 55404 // width: {totalWidth} - left - right, 55405 // height: {totalHeight} - top - bottom, 55406 55407 layout: 'horizontal', // 'horizontal' or 'vertical' 55408 55409 // FIXME 55410 // naming? 55411 axisExpandable: false, 55412 axisExpandCenter: null, 55413 axisExpandCount: 0, 55414 axisExpandWidth: 50, // FIXME '10%' ? 55415 axisExpandRate: 17, 55416 axisExpandDebounce: 50, 55417 // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full. 55418 // Do not doc to user until necessary. 55419 axisExpandSlideTriggerArea: [-0.15, 0.05, 0.4], 55420 axisExpandTriggerOn: 'click', // 'mousemove' or 'click' 55421 55422 parallelAxisDefault: null 55423 }, 55424 55425 /** 55426 * @override 55427 */ 55428 init: function () { 55429 ComponentModel.prototype.init.apply(this, arguments); 55430 55431 this.mergeOption({}); 55432 }, 55433 55434 /** 55435 * @override 55436 */ 55437 mergeOption: function (newOption) { 55438 var thisOption = this.option; 55439 55440 newOption && merge(thisOption, newOption, true); 55441 55442 this._initDimensions(); 55443 }, 55444 55445 /** 55446 * Whether series or axis is in this coordinate system. 55447 * @param {module:echarts/model/Series|module:echarts/coord/parallel/AxisModel} model 55448 * @param {module:echarts/model/Global} ecModel 55449 */ 55450 contains: function (model, ecModel) { 55451 var parallelIndex = model.get('parallelIndex'); 55452 return parallelIndex != null 55453 && ecModel.getComponent('parallel', parallelIndex) === this; 55454 }, 55455 55456 setAxisExpand: function (opt) { 55457 each$1( 55458 ['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'], 55459 function (name) { 55460 if (opt.hasOwnProperty(name)) { 55461 this.option[name] = opt[name]; 55462 } 55463 }, 55464 this 55465 ); 55466 }, 55467 55468 /** 55469 * @private 55470 */ 55471 _initDimensions: function () { 55472 var dimensions = this.dimensions = []; 55473 var parallelAxisIndex = this.parallelAxisIndex = []; 55474 55475 var axisModels = filter(this.dependentModels.parallelAxis, function (axisModel) { 55476 // Can not use this.contains here, because 55477 // initialization has not been completed yet. 55478 return (axisModel.get('parallelIndex') || 0) === this.componentIndex; 55479 }, this); 55480 55481 each$1(axisModels, function (axisModel) { 55482 dimensions.push('dim' + axisModel.get('dim')); 55483 parallelAxisIndex.push(axisModel.componentIndex); 55484 }); 55485 } 55486 55487 });
55488 55489 /* 55490* Licensed to the Apache Software Foundation (ASF) under one 55491* or more contributor license agreements. See the NOTICE file 55492* distributed with this work for additional information 55493* regarding copyright ownership. The ASF licenses this file 55494* to you under the Apache License, Version 2.0 (the 55495* "License"); you may not use this file except in compliance 55496* with the License. You may obtain a copy of the License at 55497* 55498* http://www.apache.org/licenses/LICENSE-2.0 55499* 55500* Unless required by applicable law or agreed to in writing, 55501* software distributed under the License is distributed on an 55502* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 55503* KIND, either express or implied. See the License for the 55504* specific language governing permissions and limitations 55505* under the License. 55506*/ 55507 55508 /** 55509 * @payload 55510 * @property {string} parallelAxisId 55511 * @property {Array.<Array.<number>>} intervals 55512 */ 55513 var actionInfo$1 = { 55514 type: 'axisAreaSelect', 55515 event: 'axisAreaSelected' 55516 // update: 'updateVisual' 55517 }; 55518 55519 registerAction(actionInfo$1, function (payload, ecModel) { 55520 ecModel.eachComponent( 55521 {mainType: 'parallelAxis', query: payload}, 55522 function (parallelAxisModel) { 55523 parallelAxisModel.axis.model.setActiveIntervals(payload.intervals); 55524 } 55525 ); 55526 }); 55527 55528 /** 55529 * @payload 55530 */ 55531 registerAction('parallelAxisExpand', function (payload, ecModel) { 55532 ecModel.eachComponent( 55533 {mainType: 'parallel', query: payload}, 55534 function (parallelModel) { 55535 parallelModel.setAxisExpand(payload); 55536 } 55537 ); 55538 55539 }); 55540 55541 /* 55542* Licensed to the Apache Software Foundation (ASF) under one 55543* or more contributor license agreements. See the NOTICE file 55544* distributed with this work for additional information 55545* regarding copyright ownership. The ASF licenses this file 55546* to you under the Apache License, Version 2.0 (the 55547* "License"); you may not use this file except in compliance 55548* with the License. You may obtain a copy of the License at 55549* 55550* http://www.apache.org/licenses/LICENSE-2.0 55551* 55552* Unless required by applicable law or agreed to in writing, 55553* software distributed under the License is distributed on an 55554* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 55555* KIND, either express or implied. See the License for the 55556* specific language governing permissions and limitations 55557* under the License. 55558*/ 55559 55560 var curry$2 = curry; 55561 var each$12 = each$1; 55562 var map$2 = map; 55563 var mathMin$6 = Math.min; 55564 var mathMax$6 = Math.max; 55565 var mathPow$2 = Math.pow; 55566 55567 var COVER_Z = 10000; 55568 var UNSELECT_THRESHOLD = 6; 55569 var MIN_RESIZE_LINE_WIDTH = 6; 55570 var MUTEX_RESOURCE_KEY = 'globalPan'; 55571 55572 var DIRECTION_MAP = { 55573 w: [0, 0], 55574 e: [0, 1], 55575 n: [1, 0], 55576 s: [1, 1] 55577 }; 55578 var CURSOR_MAP = { 55579 w: 'ew', 55580 e: 'ew', 55581 n: 'ns', 55582 s: 'ns', 55583 ne: 'nesw', 55584 sw: 'nesw', 55585 nw: 'nwse', 55586 se: 'nwse' 55587 }; 55588 var DEFAULT_BRUSH_OPT = { 55589 brushStyle: { 55590 lineWidth: 2, 55591 stroke: 'rgba(0,0,0,0.3)', 55592 fill: 'rgba(0,0,0,0.1)' 55593 }, 55594 transformable: true, 55595 brushMode: 'single', 55596 removeOnClick: false 55597 }; 55598 55599 var baseUID = 0; 55600 55601 /** 55602 * @alias module:echarts/component/helper/BrushController 55603 * @constructor 55604 * @mixin {module:zrender/mixin/Eventful} 55605 * @event module:echarts/component/helper/BrushController#brush 55606 * params: 55607 * areas: Array.<Array>, coord relates to container group, 55608 * If no container specified, to global. 55609 * opt { 55610 * isEnd: boolean, 55611 * removeOnClick: boolean 55612 * } 55613 * 55614 * @param {module:zrender/zrender~ZRender} zr 55615 */ 55616 function BrushController(zr) { 55617 55618 if (__DEV__) { 55619 assert$1(zr); 55620 } 55621 55622 Eventful.call(this); 55623 55624 /** 55625 * @type {module:zrender/zrender~ZRender} 55626 * @private 55627 */ 55628 this._zr = zr; 55629 55630 /** 55631 * @type {module:zrender/container/Group} 55632 * @readOnly 55633 */ 55634 this.group = new Group(); 55635 55636 /** 55637 * Only for drawing (after enabledBrush). 55638 * 'line', 'rect', 'polygon' or false 55639 * If passing false/null/undefined, disable brush. 55640 * If passing 'auto', determined by panel.defaultBrushType 55641 * @private 55642 * @type {string} 55643 */ 55644 this._brushType; 55645 55646 /** 55647 * Only for drawing (after enabledBrush). 55648 * 55649 * @private 55650 * @type {Object} 55651 */ 55652 this._brushOption; 55653 55654 /** 55655 * @private 55656 * @type {Object} 55657 */ 55658 this._panels; 55659 55660 /** 55661 * @private 55662 * @type {Array.<nubmer>} 55663 */ 55664 this._track = []; 55665 55666 /** 55667 * @private 55668 * @type {boolean} 55669 */ 55670 this._dragging; 55671 55672 /** 55673 * @private 55674 * @type {Array} 55675 */ 55676 this._covers = []; 55677 55678 /** 55679 * @private 55680 * @type {moudule:zrender/container/Group} 55681 */ 55682 this._creatingCover; 55683 55684 /** 55685 * `true` means global panel 55686 * @private 55687 * @type {module:zrender/container/Group|boolean} 55688 */ 55689 this._creatingPanel; 55690 55691 /** 55692 * @private 55693 * @type {boolean} 55694 */ 55695 this._enableGlobalPan; 55696 55697 /** 55698 * @private 55699 * @type {boolean} 55700 */ 55701 if (__DEV__) { 55702 this._mounted; 55703 } 55704 55705 /** 55706 * @private 55707 * @type {string} 55708 */ 55709 this._uid = 'brushController_' + baseUID++; 55710 55711 /** 55712 * @private 55713 * @type {Object} 55714 */ 55715 this._handlers = {}; 55716 each$12(mouseHandlers, function (handler, eventName) { 55717 this._handlers[eventName] = bind(handler, this); 55718 }, this); 55719 } 55720 55721 BrushController.prototype = { 55722 55723 constructor: BrushController, 55724 55725 /** 55726 * If set to null/undefined/false, select disabled. 55727 * @param {Object} brushOption 55728 * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false 55729 * If passing false/null/undefined, disable brush. 55730 * If passing 'auto', determined by panel.defaultBrushType. 55731 * ('auto' can not be used in global panel) 55732 * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple' 55733 * @param {boolean} [brushOption.transformable=true] 55734 * @param {boolean} [brushOption.removeOnClick=false] 55735 * @param {Object} [brushOption.brushStyle] 55736 * @param {number} [brushOption.brushStyle.width] 55737 * @param {number} [brushOption.brushStyle.lineWidth] 55738 * @param {string} [brushOption.brushStyle.stroke] 55739 * @param {string} [brushOption.brushStyle.fill] 55740 * @param {number} [brushOption.z] 55741 */ 55742 enableBrush: function (brushOption) { 55743 if (__DEV__) { 55744 assert$1(this._mounted); 55745 } 55746 55747 this._brushType && doDisableBrush(this); 55748 brushOption.brushType && doEnableBrush(this, brushOption); 55749 55750 return this; 55751 }, 55752 55753 /** 55754 * @param {Array.<Object>} panelOpts If not pass, it is global brush. 55755 * Each items: { 55756 * panelId, // mandatory. 55757 * clipPath, // mandatory. function. 55758 * isTargetByCursor, // mandatory. function. 55759 * defaultBrushType, // optional, only used when brushType is 'auto'. 55760 * getLinearBrushOtherExtent, // optional. function. 55761 * } 55762 */ 55763 setPanels: function (panelOpts) { 55764 if (panelOpts && panelOpts.length) { 55765 var panels = this._panels = {}; 55766 each$1(panelOpts, function (panelOpts) { 55767 panels[panelOpts.panelId] = clone(panelOpts); 55768 }); 55769 } 55770 else { 55771 this._panels = null; 55772 } 55773 return this; 55774 }, 55775 55776 /** 55777 * @param {Object} [opt] 55778 * @return {boolean} [opt.enableGlobalPan=false] 55779 */ 55780 mount: function (opt) { 55781 opt = opt || {}; 55782 55783 if (__DEV__) { 55784 this._mounted = true; // should be at first. 55785 } 55786 55787 this._enableGlobalPan = opt.enableGlobalPan; 55788 55789 var thisGroup = this.group; 55790 this._zr.add(thisGroup); 55791 55792 thisGroup.attr({ 55793 position: opt.position || [0, 0], 55794 rotation: opt.rotation || 0, 55795 scale: opt.scale || [1, 1] 55796 });
55797 this._transform = thisGroup.getLocalTransform(); 55798 55799 return this; 55800 }, 55801 55802 eachCover: function (cb, context) { 55803 each$12(this._covers, cb, context); 55804 }, 55805 55806 /** 55807 * Update covers. 55808 * @param {Array.<Object>} brushOptionList Like: 55809 * [ 55810 * {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable}, 55811 * {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]}, 55812 * ... 55813 * ] 55814 * `brushType` is required in each cover info. (can not be 'auto') 55815 * `id` is not mandatory. 55816 * `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default. 55817 * If brushOptionList is null/undefined, all covers removed. 55818 */ 55819 updateCovers: function (brushOptionList) { 55820 if (__DEV__) { 55821 assert$1(this._mounted); 55822 } 55823 55824 brushOptionList = map(brushOptionList, function (brushOption) { 55825 return merge(clone(DEFAULT_BRUSH_OPT), brushOption, true); 55826 }); 55827 55828 var tmpIdPrefix = '\0-brush-index-'; 55829 var oldCovers = this._covers; 55830 var newCovers = this._covers = []; 55831 var controller = this; 55832 var creatingCover = this._creatingCover; 55833 55834 (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey)) 55835 .add(addOrUpdate) 55836 .update(addOrUpdate) 55837 .remove(remove) 55838 .execute(); 55839 55840 return this; 55841 55842 function getKey(brushOption, index) { 55843 return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) 55844 + '-' + brushOption.brushType; 55845 } 55846 55847 function oldGetKey(cover, index) { 55848 return getKey(cover.__brushOption, index); 55849 } 55850 55851 function addOrUpdate(newIndex, oldIndex) { 55852 var newBrushOption = brushOptionList[newIndex]; 55853 // Consider setOption in event listener of brushSelect, 55854 // where updating cover when creating should be forbiden. 55855 if (oldIndex != null && oldCovers[oldIndex] === creatingCover) { 55856 newCovers[newIndex] = oldCovers[oldIndex]; 55857 } 55858 else { 55859 var cover = newCovers[newIndex] = oldIndex != null 55860 ? ( 55861 oldCovers[oldIndex].__brushOption = newBrushOption, 55862 oldCovers[oldIndex] 55863 ) 55864 : endCreating(controller, createCover(controller, newBrushOption)); 55865 updateCoverAfterCreation(controller, cover); 55866 } 55867 } 55868 55869 function remove(oldIndex) { 55870 if (oldCovers[oldIndex] !== creatingCover) { 55871 controller.group.remove(oldCovers[oldIndex]); 55872 } 55873 } 55874 }, 55875 55876 unmount: function () { 55877 if (__DEV__) { 55878 if (!this._mounted) { 55879 return; 55880 } 55881 } 55882 55883 this.enableBrush(false); 55884 55885 // container may 'removeAll' outside. 55886 clearCovers(this); 55887 this._zr.remove(this.group); 55888 55889 if (__DEV__) { 55890 this._mounted = false; // should be at last. 55891 } 55892 55893 return this; 55894 }, 55895 55896 dispose: function () { 55897 this.unmount(); 55898 this.off(); 55899 } 55900 }; 55901 55902 mixin(BrushController, Eventful); 55903 55904 function doEnableBrush(controller, brushOption) { 55905 var zr = controller._zr; 55906 55907 // Consider roam, which takes globalPan too. 55908 if (!controller._enableGlobalPan) { 55909 take(zr, MUTEX_RESOURCE_KEY, controller._uid); 55910 } 55911 55912 each$12(controller._handlers, function (handler, eventName) { 55913 zr.on(eventName, handler); 55914 }); 55915 55916 controller._brushType = brushOption.brushType; 55917 controller._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true); 55918 } 55919 55920 function doDisableBrush(controller) { 55921 var zr = controller._zr; 55922 55923 release(zr, MUTEX_RESOURCE_KEY, controller._uid); 55924 55925 each$12(controller._handlers, function (handler, eventName) { 55926 zr.off(eventName, handler); 55927 }); 55928 55929 controller._brushType = controller._brushOption = null; 55930 } 55931 55932 function createCover(controller, brushOption) { 55933 var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption); 55934 cover.__brushOption = brushOption; 55935 updateZ$1(cover, brushOption); 55936 controller.group.add(cover); 55937 return cover; 55938 } 55939 55940 function endCreating(controller, creatingCover) {
55941 var coverRenderer = getCoverRenderer(creatingCover); 55942 if (coverRenderer.endCreating) { 55943 coverRenderer.endCreating(controller, creatingCover); 55944 updateZ$1(creatingCover, creatingCover.__brushOption); 55945 } 55946 return creatingCover; 55947 } 55948 55949 function updateCoverShape(controller, cover) { 55950 var brushOption = cover.__brushOption; 55951 getCoverRenderer(cover).updateCoverShape( 55952 controller, cover, brushOption.range, brushOption 55953 ); 55954 } 55955 55956 function updateZ$1(cover, brushOption) { 55957 var z = brushOption.z; 55958 z == null && (z = COVER_Z); 55959 cover.traverse(function (el) { 55960 el.z = z; 55961 el.z2 = z; // Consider in given container. 55962 }); 55963 } 55964 55965 function updateCoverAfterCreation(controller, cover) { 55966 getCoverRenderer(cover).updateCommon(controller, cover); 55967 updateCoverShape(controller, cover); 55968 } 55969 55970 function getCoverRenderer(cover) { 55971 return coverRenderers[cover.__brushOption.brushType]; 55972 } 55973 55974// return target panel or `true` (means global panel) 55975 function getPanelByPoint(controller, e, localCursorPoint) { 55976 var panels = controller._panels; 55977 if (!panels) { 55978 return true; // Global panel 55979 } 55980 var panel; 55981 var transform = controller._transform; 55982 each$12(panels, function (pn) { 55983 pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn); 55984 }); 55985 return panel; 55986 } 55987 55988// Return a panel or true 55989 function getPanelByCover(controller, cover) { 55990 var panels = controller._panels; 55991 if (!panels) { 55992 return true; // Global panel 55993 } 55994 var panelId = cover.__brushOption.panelId; 55995 // User may give cover without coord sys info, 55996 // which is then treated as global panel. 55997 return panelId != null ? panels[panelId] : true; 55998 } 55999 56000 function clearCovers(controller) { 56001 var covers = controller._covers; 56002 var originalLength = covers.length; 56003 each$12(covers, function (cover) { 56004 controller.group.remove(cover); 56005 }, controller); 56006 covers.length = 0; 56007 56008 return !!originalLength; 56009 } 56010 56011 function trigger(controller, opt) { 56012 var areas = map$2(controller._covers, function (cover) { 56013 var brushOption = cover.__brushOption; 56014 var range = clone(brushOption.range); 56015 return { 56016 brushType: brushOption.brushType, 56017 panelId: brushOption.panelId, 56018 range: range 56019 }; 56020 }); 56021 56022 controller.trigger('brush', areas, { 56023 isEnd: !!opt.isEnd, 56024 removeOnClick: !!opt.removeOnClick 56025 }); 56026 } 56027 56028 function shouldShowCover(controller) { 56029 var track = controller._track; 56030 56031 if (!track.length) { 56032 return false; 56033 } 56034 56035 var p2 = track[track.length - 1]; 56036 var p1 = track[0]; 56037 var dx = p2[0] - p1[0]; 56038 var dy = p2[1] - p1[1]; 56039 var dist = mathPow$2(dx * dx + dy * dy, 0.5); 56040 56041 return dist > UNSELECT_THRESHOLD; 56042 } 56043 56044 function getTrackEnds(track) { 56045 var tail = track.length - 1; 56046 tail < 0 && (tail = 0); 56047 return [track[0], track[tail]]; 56048 } 56049 56050 function createBaseRectCover(doDrift, controller, brushOption, edgeNames) { 56051 var cover = new Group(); 56052 56053 cover.add(new Rect({ 56054 name: 'main', 56055 style: makeStyle(brushOption), 56056 silent: true, 56057 draggable: true, 56058 cursor: 'move', 56059 drift: curry$2(doDrift, controller, cover, 'nswe'), 56060 ondragend: curry$2(trigger, controller, {isEnd: true}) 56061 })); 56062 56063 each$12( 56064 edgeNames, 56065 function (name) { 56066 cover.add(new Rect({ 56067 name: name, 56068 style: {opacity: 0}, 56069 draggable: true, 56070 silent: true, 56071 invisible: true, 56072 drift: curry$2(doDrift, controller, cover, name),
vendor: 6,840 bytes, lines 56073-56259
56073 ondragend: curry$2(trigger, controller, {isEnd: true}) 56074 })); 56075 } 56076 ); 56077 56078 return cover; 56079 } 56080 56081 function updateBaseRect(controller, cover, localRange, brushOption) { 56082 var lineWidth = brushOption.brushStyle.lineWidth || 0; 56083 var handleSize = mathMax$6(lineWidth, MIN_RESIZE_LINE_WIDTH); 56084 var x = localRange[0][0]; 56085 var y = localRange[1][0]; 56086 var xa = x - lineWidth / 2; 56087 var ya = y - lineWidth / 2; 56088 var x2 = localRange[0][1]; 56089 var y2 = localRange[1][1]; 56090 var x2a = x2 - handleSize + lineWidth / 2; 56091 var y2a = y2 - handleSize + lineWidth / 2; 56092 var width = x2 - x; 56093 var height = y2 - y; 56094 var widtha = width + lineWidth; 56095 var heighta = height + lineWidth; 56096 56097 updateRectShape(controller, cover, 'main', x, y, width, height); 56098 56099 if (brushOption.transformable) { 56100 updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta); 56101 updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta); 56102 updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize); 56103 updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize); 56104 56105 updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize); 56106 updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize); 56107 updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize); 56108 updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize); 56109 } 56110 } 56111 56112 function updateCommon(controller, cover) { 56113 var brushOption = cover.__brushOption; 56114 var transformable = brushOption.transformable; 56115 56116 var mainEl = cover.childAt(0); 56117 mainEl.useStyle(makeStyle(brushOption)); 56118 mainEl.attr({ 56119 silent: !transformable, 56120 cursor: transformable ? 'move' : 'default' 56121 }); 56122 56123 each$12( 56124 ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'], 56125 function (name) { 56126 var el = cover.childOfName(name); 56127 var globalDir = getGlobalDirection(controller, name); 56128 56129 el && el.attr({ 56130 silent: !transformable, 56131 invisible: !transformable, 56132 cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null 56133 }); 56134 } 56135 ); 56136 } 56137 56138 function updateRectShape(controller, cover, name, x, y, w, h) { 56139 var el = cover.childOfName(name); 56140 el && el.setShape(pointsToRect( 56141 clipByPanel(controller, cover, [[x, y], [x + w, y + h]]) 56142 )); 56143 } 56144 56145 function makeStyle(brushOption) { 56146 return defaults({strokeNoScale: true}, brushOption.brushStyle); 56147 } 56148 56149 function formatRectRange(x, y, x2, y2) { 56150 var min = [mathMin$6(x, x2), mathMin$6(y, y2)]; 56151 var max = [mathMax$6(x, x2), mathMax$6(y, y2)]; 56152 56153 return [ 56154 [min[0], max[0]], // x range 56155 [min[1], max[1]] // y range 56156 ]; 56157 } 56158 56159 function getTransform$1(controller) { 56160 return getTransform(controller.group); 56161 } 56162 56163 function getGlobalDirection(controller, localDirection) { 56164 if (localDirection.length > 1) { 56165 localDirection = localDirection.split(''); 56166 var globalDir = [ 56167 getGlobalDirection(controller, localDirection[0]), 56168 getGlobalDirection(controller, localDirection[1]) 56169 ]; 56170 (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse(); 56171 return globalDir.join(''); 56172 } 56173 else { 56174 var map$$1 = {w: 'left', e: 'right', n: 'top', s: 'bottom'}; 56175 var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'}; 56176 var globalDir = transformDirection( 56177 map$$1[localDirection], getTransform$1(controller) 56178 ); 56179 return inverseMap[globalDir]; 56180 } 56181 } 56182 56183 function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) { 56184 var brushOption = cover.__brushOption; 56185 var rectRange = toRectRange(brushOption.range); 56186 var localDelta = toLocalDelta(controller, dx, dy); 56187 56188 each$12(name.split(''), function (namePart) { 56189 var ind = DIRECTION_MAP[namePart]; 56190 rectRange[ind[0]][ind[1]] += localDelta[ind[0]]; 56191 }); 56192 56193 brushOption.range = fromRectRange(formatRectRange( 56194 rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1] 56195 )); 56196 56197 updateCoverAfterCreation(controller, cover); 56198 trigger(controller, {isEnd: false}); 56199 } 56200 56201 function driftPolygon(controller, cover, dx, dy, e) { 56202 var range = cover.__brushOption.range; 56203 var localDelta = toLocalDelta(controller, dx, dy); 56204 56205 each$12(range, function (point) { 56206 point[0] += localDelta[0]; 56207 point[1] += localDelta[1]; 56208 }); 56209 56210 updateCoverAfterCreation(controller, cover); 56211 trigger(controller, {isEnd: false}); 56212 } 56213 56214 function toLocalDelta(controller, dx, dy) { 56215 var thisGroup = controller.group; 56216 var localD = thisGroup.transformCoordToLocal(dx, dy); 56217 var localZero = thisGroup.transformCoordToLocal(0, 0); 56218 56219 return [localD[0] - localZero[0], localD[1] - localZero[1]]; 56220 } 56221 56222 function clipByPanel(controller, cover, data) { 56223 var panel = getPanelByCover(controller, cover); 56224 56225 return (panel && panel !== true) 56226 ? panel.clipPath(data, controller._transform) 56227 : clone(data); 56228 } 56229 56230 function pointsToRect(points) { 56231 var xmin = mathMin$6(points[0][0], points[1][0]); 56232 var ymin = mathMin$6(points[0][1], points[1][1]); 56233 var xmax = mathMax$6(points[0][0], points[1][0]); 56234 var ymax = mathMax$6(points[0][1], points[1][1]); 56235 56236 return { 56237 x: xmin, 56238 y: ymin, 56239 width: xmax - xmin, 56240 height: ymax - ymin 56241 }; 56242 } 56243 56244 function resetCursor(controller, e, localCursorPoint) { 56245 // Check active 56246 if (!controller._brushType) { 56247 return; 56248 } 56249 56250 var zr = controller._zr; 56251 var covers = controller._covers; 56252 var currPanel = getPanelByPoint(controller, e, localCursorPoint); 56253 56254 // Check whether in covers. 56255 if (!controller._dragging) { 56256 for (var i = 0; i < covers.length; i++) { 56257 var brushOption = covers[i].__brushOption; 56258 if (currPanel 56259 && (currPanel === true || brushOption.panelId === currPanel.panelId)
56260 && coverRenderers[brushOption.brushType].contain( 56261 covers[i], localCursorPoint[0], localCursorPoint[1] 56262 ) 56263 ) { 56264 // Use cursor style set on cover. 56265 return; 56266 } 56267 } 56268 } 56269 56270 currPanel && zr.setCursorStyle('crosshair'); 56271 } 56272 56273 function preventDefault(e) { 56274 var rawE = e.event; 56275 rawE.preventDefault && rawE.preventDefault(); 56276 } 56277 56278 function mainShapeContain(cover, x, y) { 56279 return cover.childOfName('main').contain(x, y); 56280 } 56281 56282 function updateCoverByMouse(controller, e, localCursorPoint, isEnd) { 56283 var creatingCover = controller._creatingCover; 56284 var panel = controller._creatingPanel; 56285 var thisBrushOption = controller._brushOption; 56286 var eventParams; 56287 56288 controller._track.push(localCursorPoint.slice()); 56289 56290 if (shouldShowCover(controller) || creatingCover) { 56291 56292 if (panel && !creatingCover) { 56293 thisBrushOption.brushMode === 'single' && clearCovers(controller); 56294 var brushOption = clone(thisBrushOption); 56295 brushOption.brushType = determineBrushType(brushOption.brushType, panel); 56296 brushOption.panelId = panel === true ? null : panel.panelId; 56297 creatingCover = controller._creatingCover = createCover(controller, brushOption); 56298 controller._covers.push(creatingCover); 56299 } 56300 56301 if (creatingCover) {
vendor: 1,401 bytes, lines 56302-56333
56302 var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)]; 56303 var coverBrushOption = creatingCover.__brushOption; 56304 56305 coverBrushOption.range = coverRenderer.getCreatingRange( 56306 clipByPanel(controller, creatingCover, controller._track) 56307 ); 56308 56309 if (isEnd) { 56310 endCreating(controller, creatingCover); 56311 coverRenderer.updateCommon(controller, creatingCover); 56312 } 56313 56314 updateCoverShape(controller, creatingCover); 56315 56316 eventParams = {isEnd: isEnd}; 56317 } 56318 } 56319 else if ( 56320 isEnd 56321 && thisBrushOption.brushMode === 'single' 56322 && thisBrushOption.removeOnClick 56323 ) { 56324 // Help user to remove covers easily, only by a tiny drag, in 'single' mode. 56325 // But a single click do not clear covers, because user may have casual 56326 // clicks (for example, click on other component and do not expect covers 56327 // disappear). 56328 // Only some cover removed, trigger action, but not every click trigger action. 56329 if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) { 56330 eventParams = {isEnd: isEnd, removeOnClick: true}; 56331 } 56332 } 56333
56334 return eventParams; 56335 } 56336 56337 function determineBrushType(brushType, panel) { 56338 if (brushType === 'auto') { 56339 if (__DEV__) { 56340 assert$1( 56341 panel && panel.defaultBrushType, 56342 'MUST have defaultBrushType when brushType is "atuo"' 56343 ); 56344 } 56345 return panel.defaultBrushType; 56346 } 56347 return brushType; 56348 } 56349 56350 var mouseHandlers = { 56351 56352 mousedown: function (e) { 56353 if (this._dragging) { 56354 // In case some browser do not support globalOut, 56355 // and release mose out side the browser. 56356 handleDragEnd.call(this, e); 56357 } 56358 else if (!e.target || !e.target.draggable) { 56359 56360 preventDefault(e); 56361 56362 var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); 56363 56364 this._creatingCover = null; 56365 var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint); 56366 56367 if (panel) { 56368 this._dragging = true; 56369 this._track = [localCursorPoint.slice()]; 56370 } 56371 } 56372 }, 56373 56374 mousemove: function (e) { 56375 var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); 56376 56377 resetCursor(this, e, localCursorPoint); 56378 56379 if (this._dragging) { 56380 56381 preventDefault(e); 56382 56383 var eventParams = updateCoverByMouse(this, e, localCursorPoint, false); 56384 56385 eventParams && trigger(this, eventParams); 56386 } 56387 }, 56388 56389 mouseup: handleDragEnd //, 56390 56391 // FIXME 56392 // in tooltip, globalout should not be triggered. 56393 // globalout: handleDragEnd 56394 }; 56395 56396 function handleDragEnd(e) { 56397 if (this._dragging) { 56398 56399 preventDefault(e); 56400 56401 var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); 56402 var eventParams = updateCoverByMouse(this, e, localCursorPoint, true); 56403 56404 this._dragging = false; 56405 this._track = []; 56406 this._creatingCover = null; 56407 56408 // trigger event shoule be at final, after procedure will be nested. 56409 eventParams && trigger(this, eventParams); 56410 } 56411 } 56412 56413 /** 56414 * key: brushType 56415 * @type {Object} 56416 */
56417 var coverRenderers = { 56418 56419 lineX: getLineRenderer(0), 56420 56421 lineY: getLineRenderer(1), 56422 56423 rect: { 56424 createCover: function (controller, brushOption) { 56425 return createBaseRectCover( 56426 curry$2( 56427 driftRect, 56428 function (range) { 56429 return range; 56430 }, 56431 function (range) { 56432 return range; 56433 } 56434 ), 56435 controller, 56436 brushOption, 56437 ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'] 56438 ); 56439 }, 56440 getCreatingRange: function (localTrack) { 56441 var ends = getTrackEnds(localTrack); 56442 return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]); 56443 }, 56444 updateCoverShape: function (controller, cover, localRange, brushOption) { 56445 updateBaseRect(controller, cover, localRange, brushOption); 56446 }, 56447 updateCommon: updateCommon, 56448 contain: mainShapeContain 56449 }, 56450 56451 polygon: { 56452 createCover: function (controller, brushOption) { 56453 var cover = new Group(); 56454 56455 // Do not use graphic.Polygon because graphic.Polyline do not close the 56456 // border of the shape when drawing, which is a better experience for user. 56457 cover.add(new Polyline({ 56458 name: 'main', 56459 style: makeStyle(brushOption), 56460 silent: true 56461 })); 56462 56463 return cover; 56464 }, 56465 getCreatingRange: function (localTrack) { 56466 return localTrack; 56467 }, 56468 endCreating: function (controller, cover) { 56469 cover.remove(cover.childAt(0)); 56470 // Use graphic.Polygon close the shape. 56471 cover.add(new Polygon({ 56472 name: 'main', 56473 draggable: true, 56474 drift: curry$2(driftPolygon, controller, cover), 56475 ondragend: curry$2(trigger, controller, {isEnd: true}) 56476 })); 56477 }, 56478 updateCoverShape: function (controller, cover, localRange, brushOption) { 56479 cover.childAt(0).setShape({ 56480 points: clipByPanel(controller, cover, localRange) 56481 }); 56482 }, 56483 updateCommon: updateCommon, 56484 contain: mainShapeContain 56485 } 56486 }; 56487 56488 function getLineRenderer(xyIndex) { 56489 return { 56490 createCover: function (controller, brushOption) { 56491 return createBaseRectCover( 56492 curry$2( 56493 driftRect, 56494 function (range) { 56495 var rectRange = [range, [0, 100]]; 56496 xyIndex && rectRange.reverse(); 56497 return rectRange; 56498 }, 56499 function (rectRange) { 56500 return rectRange[xyIndex]; 56501 } 56502 ), 56503 controller, 56504 brushOption, 56505 [['w', 'e'], ['n', 's']][xyIndex] 56506 ); 56507 }, 56508 getCreatingRange: function (localTrack) { 56509 var ends = getTrackEnds(localTrack); 56510 var min = mathMin$6(ends[0][xyIndex], ends[1][xyIndex]); 56511 var max = mathMax$6(ends[0][xyIndex], ends[1][xyIndex]); 56512 56513 return [min, max]; 56514 }, 56515 updateCoverShape: function (controller, cover, localRange, brushOption) { 56516 var otherExtent; 56517 // If brushWidth not specified, fit the panel. 56518 var panel = getPanelByCover(controller, cover); 56519 if (panel !== true && panel.getLinearBrushOtherExtent) { 56520 otherExtent = panel.getLinearBrushOtherExtent( 56521 xyIndex, controller._transform 56522 ); 56523 } 56524 else { 56525 var zr = controller._zr; 56526 otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]]; 56527 } 56528 var rectRange = [localRange, otherExtent]; 56529 xyIndex && rectRange.reverse(); 56530 56531 updateBaseRect(controller, cover, rectRange, brushOption); 56532 }, 56533 updateCommon: updateCommon, 56534 contain: mainShapeContain 56535 }; 56536 } 56537 56538 /* 56539* Licensed to the Apache Software Foundation (ASF) under one 56540* or more contributor license agreements. See the NOTICE file 56541* distributed with this work for additional information 56542* regarding copyright ownership. The ASF licenses this file 56543* to you under the Apache License, Version 2.0 (the 56544* "License");
56544 you may not use this file except in compliance 56545* with the License. You may obtain a copy of the License at 56546* 56547* http://www.apache.org/licenses/LICENSE-2.0 56548* 56549* Unless required by applicable law or agreed to in writing, 56550* software distributed under the License is distributed on an 56551* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 56552* KIND, either express or implied. See the License for the 56553* specific language governing permissions and limitations 56554* under the License. 56555*/ 56556 56557 function makeRectPanelClipPath(rect) { 56558 rect = normalizeRect(rect); 56559 return function (localPoints, transform) { 56560 return clipPointsByRect(localPoints, rect); 56561 }; 56562 } 56563 56564 function makeLinearBrushOtherExtent(rect, specifiedXYIndex) { 56565 rect = normalizeRect(rect); 56566 return function (xyIndex) { 56567 var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex; 56568 var brushWidth = idx ? rect.width : rect.height; 56569 var base = idx ? rect.x : rect.y; 56570 return [base, base + (brushWidth || 0)]; 56571 }; 56572 } 56573 56574 function makeRectIsTargetByCursor(rect, api, targetModel) { 56575 rect = normalizeRect(rect); 56576 return function (e, localCursorPoint, transform) { 56577 return rect.contain(localCursorPoint[0], localCursorPoint[1]) 56578 && !onIrrelevantElement(e, api, targetModel); 56579 }; 56580 } 56581 56582// Consider width/height is negative. 56583 function normalizeRect(rect) { 56584 return BoundingRect.create(rect); 56585 } 56586 56587 /* 56588* Licensed to the Apache Software Foundation (ASF) under one 56589* or more contributor license agreements. See the NOTICE file 56590* distributed with this work for additional information 56591* regarding copyright ownership. The ASF licenses this file 56592* to you under the Apache License, Version 2.0 (the 56593* "License"); you may not use this file except in compliance 56594* with the License. You may obtain a copy of the License at 56595* 56596* http://www.apache.org/licenses/LICENSE-2.0 56597* 56598* Unless required by applicable law or agreed to in writing, 56599* software distributed under the License is distributed on an 56600* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 56601* KIND, either express or implied. See the License for the 56602* specific language governing permissions and limitations 56603* under the License. 56604*/ 56605 56606 var elementList = ['axisLine', 'axisTickLabel', 'axisName']; 56607 56608 var AxisView$2 = extendComponentView({ 56609 56610 type: 'parallelAxis', 56611 56612 /** 56613 * @override 56614 */ 56615 init: function (ecModel, api) { 56616 AxisView$2.superApply(this, 'init', arguments); 56617 56618 /** 56619 * @type {module:echarts/component/helper/BrushController} 56620 */ 56621 (this._brushController = new BrushController(api.getZr())) 56622 .on('brush', bind(this._onBrush, this)); 56623 }, 56624 56625 /** 56626 * @override 56627 */ 56628 render: function (axisModel, ecModel, api, payload) { 56629 if (fromAxisAreaSelect(axisModel, ecModel, payload)) { 56630 return; 56631 } 56632 56633 this.axisModel = axisModel; 56634 this.api = api; 56635 56636 this.group.removeAll(); 56637 56638 var oldAxisGroup = this._axisGroup; 56639 this._axisGroup = new Group(); 56640 this.group.add(this._axisGroup); 56641 56642 if (!axisModel.get('show')) { 56643 return; 56644 } 56645 56646 var coordSysModel = getCoordSysModel(axisModel, ecModel); 56647 var coordSys = coordSysModel.coordinateSystem; 56648 56649 var areaSelectStyle = axisModel.getAreaSelectStyle(); 56650 var areaWidth = areaSelectStyle.width; 56651 56652 var dim = axisModel.axis.dim; 56653 var axisLayout = coordSys.getAxisLayout(dim); 56654 56655 var builderOpt = extend( 56656 {strokeContainThreshold: areaWidth}, 56657 axisLayout 56658 ); 56659 56660 var axisBuilder = new AxisBuilder(axisModel, builderOpt); 56661 56662 each$1(elementList, axisBuilder.add, axisBuilder); 56663 56664 this._axisGroup.add(axisBuilder.getGroup()); 56665 56666 this._refreshBrushController( 56667 builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api 56668 ); 56669 56670 var animationModel = (payload && payload.animation === false) ? null : axisModel; 56671 groupTransition(oldAxisGroup, this._axisGroup, animationModel); 56672 }, 56673 56674 // /** 56675 // * @override 56676 // */ 56677 // updateVisual: function (axisModel, ecModel, api, payload) { 56678 // this._brushController && this._brushController 56679 // .updateCovers(getCoverInfoList(axisModel)); 56680 // }, 56681 56682 _refreshBrushController: function ( 56683 builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api 56684 ) { 56685 // After filtering, axis may change, select area needs to be update. 56686 var extent = axisModel.axis.getExtent(); 56687 var extentLen = extent[1] - extent[0]; 56688 var extra = Math.min(30, Math.abs(extentLen) * 0.1); // Arbitrary value. 56689 56690 // width/height might be negative, which will be 56691 // normalized in BoundingRect. 56692 var rect = BoundingRect.create({ 56693 x: extent[0], 56694 y: -areaWidth / 2, 56695 width: extentLen, 56696 height: areaWidth 56697 }); 56698 rect.x -= extra; 56699 rect.width += 2 * extra; 56700 56701 this._brushController 56702 .mount({ 56703 enableGlobalPan: true, 56704 rotation: builderOpt.rotation, 56705 position: builderOpt.position 56706 }) 56707 .setPanels([{ 56708 panelId: 'pl', 56709 clipPath: makeRectPanelClipPath(rect), 56710 isTargetByCursor: makeRectIsTargetByCursor(rect, api, coordSysModel), 56711 getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect, 0) 56712 }]) 56713 .enableBrush({ 56714 brushType: 'lineX', 56715 brushStyle: areaSelectStyle, 56716 removeOnClick: true 56717 }) 56718 .updateCovers(getCoverInfoList(axisModel)); 56719 }, 56720 56721 _onBrush: function (coverInfoList, opt) {
56722 // Do not cache these object, because the mey be changed. 56723 var axisModel = this.axisModel; 56724 var axis = axisModel.axis; 56725 var intervals = map(coverInfoList, function (coverInfo) { 56726 return [ 56727 axis.coordToData(coverInfo.range[0], true), 56728 axis.coordToData(coverInfo.range[1], true) 56729 ]; 56730 }); 56731 56732 // If realtime is true, action is not dispatched on drag end, because 56733 // the drag end emits the same params with the last drag move event, 56734 // and may have some delay when using touch pad. 56735 if (!axisModel.option.realtime === opt.isEnd || opt.removeOnClick) { // jshint ignore:line 56736 this.api.dispatchAction({ 56737 type: 'axisAreaSelect', 56738 parallelAxisId: axisModel.id, 56739 intervals: intervals 56740 }); 56741 } 56742 }, 56743 56744 /** 56745 * @override 56746 */ 56747 dispose: function () { 56748 this._brushController.dispose(); 56749 } 56750 }); 56751 56752 function fromAxisAreaSelect(axisModel, ecModel, payload) { 56753 return payload 56754 && payload.type === 'axisAreaSelect' 56755 && ecModel.findComponents( 56756 {mainType: 'parallelAxis', query: payload} 56757 )[0] === axisModel; 56758 } 56759 56760 function getCoverInfoList(axisModel) { 56761 var axis = axisModel.axis; 56762 return map(axisModel.activeIntervals, function (interval) { 56763 return { 56764 brushType: 'lineX', 56765 panelId: 'pl', 56766 range: [ 56767 axis.dataToCoord(interval[0], true), 56768 axis.dataToCoord(interval[1], true) 56769 ] 56770 }; 56771 }); 56772 } 56773 56774 function getCoordSysModel(axisModel, ecModel) { 56775 return ecModel.getComponent( 56776 'parallel', axisModel.get('parallelIndex') 56777 ); 56778 } 56779 56780 /* 56781* Licensed to the Apache Software Foundation (ASF) under one 56782* or more contributor license agreements. See the NOTICE file 56783* distributed with this work for additional information 56784* regarding copyright ownership. The ASF licenses this file 56785* to you under the Apache License, Version 2.0 (the 56786* "License"); you may not use this file except in compliance 56787* with the License. You may obtain a copy of the License at 56788* 56789* http://www.apache.org/licenses/LICENSE-2.0 56790* 56791* Unless required by applicable law or agreed to in writing, 56792* software distributed under the License is distributed on an 56793* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 56794* KIND, either express or implied. See the License for the 56795* specific language governing permissions and limitations 56796* under the License. 56797*/ 56798 56799 /* 56800* Licensed to the Apache Software Foundation (ASF) under one 56801* or more contributor license agreements. See the NOTICE file 56802* distributed with this work for additional information 56803* regarding copyright ownership. The ASF licenses this file 56804* to you under the Apache License, Version 2.0 (the 56805* "License"); you may not use this file except in compliance 56806* with the License. You may obtain a copy of the License at 56807* 56808* http://www.apache.org/licenses/LICENSE-2.0 56809* 56810* Unless required by applicable law or agreed to in writing, 56811* software distributed under the License is distributed on an 56812* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 56813* KIND, either express or implied. See the License for the 56814* specific language governing permissions and limitations 56815* under the License. 56816*/ 56817 56818 var CLICK_THRESHOLD = 5; // > 4 56819 56820// Parallel view 56821 extendComponentView({ 56822 type: 'parallel', 56823 56824 render: function (parallelModel, ecModel, api) { 56825 this._model = parallelModel; 56826 this._api = api; 56827 56828 if (!this._handlers) { 56829 this._handlers = {}; 56830 each$1(handlers, function (handler, eventName) { 56831 api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this)); 56832 }, this); 56833 } 56834 56835 createOrUpdate( 56836 this, 56837 '_throttledDispatchExpand', 56838 parallelModel.get('axisExpandRate'), 56839 'fixRate' 56840 ); 56841 }, 56842 56843 dispose: function (ecModel, api) { 56844 each$1(this._handlers, function (handler, eventName) { 56845 api.getZr().off(eventName, handler); 56846 }); 56847 this._handlers = null; 56848 }, 56849 56850 /** 56851 * @param {Object} [opt] If null, cancle the last action triggering for debounce.
56852 */ 56853 _throttledDispatchExpand: function (opt) { 56854 this._dispatchExpand(opt); 56855 }, 56856 56857 _dispatchExpand: function (opt) { 56858 opt && this._api.dispatchAction( 56859 extend({type: 'parallelAxisExpand'}, opt) 56860 ); 56861 } 56862 56863 }); 56864 56865 var handlers = { 56866 56867 mousedown: function (e) { 56868 if (checkTrigger(this, 'click')) { 56869 this._mouseDownPoint = [e.offsetX, e.offsetY]; 56870 } 56871 }, 56872 56873 mouseup: function (e) { 56874 var mouseDownPoint = this._mouseDownPoint; 56875 56876 if (checkTrigger(this, 'click') && mouseDownPoint) { 56877 var point = [e.offsetX, e.offsetY]; 56878 var dist = Math.pow(mouseDownPoint[0] - point[0], 2) 56879 + Math.pow(mouseDownPoint[1] - point[1], 2); 56880 56881 if (dist > CLICK_THRESHOLD) { 56882 return; 56883 } 56884 56885 var result = this._model.coordinateSystem.getSlidedAxisExpandWindow( 56886 [e.offsetX, e.offsetY] 56887 ); 56888 56889 result.behavior !== 'none' && this._dispatchExpand({ 56890 axisExpandWindow: result.axisExpandWindow 56891 }); 56892 } 56893 56894 this._mouseDownPoint = null; 56895 }, 56896 56897 mousemove: function (e) { 56898 // Should do nothing when brushing. 56899 if (this._mouseDownPoint || !checkTrigger(this, 'mousemove')) { 56900 return; 56901 } 56902 var model = this._model; 56903 var result = model.coordinateSystem.getSlidedAxisExpandWindow( 56904 [e.offsetX, e.offsetY] 56905 ); 56906 56907 var behavior = result.behavior; 56908 behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce')); 56909 this._throttledDispatchExpand( 56910 behavior === 'none' 56911 ? null // Cancle the last trigger, in case that mouse slide out of the area quickly. 56912 : { 56913 axisExpandWindow: result.axisExpandWindow, 56914 // Jumping uses animation, and sliding suppresses animation. 56915 animation: behavior === 'jump' ? null : false 56916 } 56917 ); 56918 } 56919 }; 56920 56921 function checkTrigger(view, triggerOn) { 56922 var model = view._model; 56923 return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn; 56924 } 56925 56926 registerPreprocessor(parallelPreprocessor); 56927 56928 /* 56929* Licensed to the Apache Software Foundation (ASF) under one 56930* or more contributor license agreements. See the NOTICE file 56931* distributed with this work for additional information 56932* regarding copyright ownership. The ASF licenses this file 56933* to you under the Apache License, Version 2.0 (the 56934* "License"); you may not use this file except in compliance 56935* with the License. You may obtain a copy of the License at 56936* 56937* http://www.apache.org/licenses/LICENSE-2.0 56938* 56939* Unless required by applicable law or agreed to in writing, 56940* software distributed under the License is distributed on an 56941* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 56942* KIND, either express or implied. See the License for the 56943* specific language governing permissions and limitations 56944* under the License. 56945*/ 56946 56947 SeriesModel.extend({ 56948 56949 type: 'series.parallel', 56950 56951 dependencies: ['parallel'], 56952 56953 visualColorAccessPath: 'lineStyle.color', 56954 56955 getInitialData: function (option, ecModel) { 56956 var source = this.getSource(); 56957 56958 setEncodeAndDimensions(source, this); 56959 56960 return createListFromArray(source, this); 56961 }, 56962 56963 /** 56964 * User can get data raw indices on 'axisAreaSelected' event received. 56965 * 56966 * @public 56967 * @param {string} activeState 'active' or 'inactive' or 'normal' 56968 * @return {Array.<number>} Raw indices 56969 */ 56970 getRawIndicesByActiveState: function (activeState) { 56971 var coordSys = this.coordinateSystem; 56972 var data = this.getData(); 56973 var indices = []; 56974 56975 coordSys.eachActiveState(data, function (theActiveState, dataIndex) { 56976 if (activeState === theActiveState) { 56977 indices.push(data.getRawIndex(dataIndex)); 56978 } 56979 }); 56980 56981 return indices; 56982 }, 56983 56984 defaultOption: { 56985 zlevel: 0, // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 56986 z: 2, // äºÅ级å±âå 56987 56988 coordinateSystem: 'parallel',
56989 parallelIndex: 0, 56990 56991 label: { 56992 show: false 56993 }, 56994 56995 inactiveOpacity: 0.05, 56996 activeOpacity: 1, 56997 56998 lineStyle: { 56999 width: 1, 57000 opacity: 0.45, 57001 type: 'solid' 57002 }, 57003 emphasis: { 57004 label: { 57005 show: false 57006 } 57007 }, 57008 57009 progressive: 500, 57010 smooth: false, // true | false | number 57011 57012 animationEasing: 'linear' 57013 } 57014 }); 57015 57016 function setEncodeAndDimensions(source, seriesModel) { 57017 // The mapping of parallelAxis dimension to data dimension can 57018 // be specified in parallelAxis.option.dim. For example, if 57019 // parallelAxis.option.dim is 'dim3', it mapping to the third 57020 // dimension of data. But `data.encode` has higher priority. 57021 // Moreover, parallelModel.dimension should not be regarded as data 57022 // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; 57023 57024 if (source.encodeDefine) { 57025 return; 57026 } 57027 57028 var parallelModel = seriesModel.ecModel.getComponent( 57029 'parallel', seriesModel.get('parallelIndex') 57030 ); 57031 if (!parallelModel) { 57032 return; 57033 } 57034 57035 var encodeDefine = source.encodeDefine = createHashMap(); 57036 each$1(parallelModel.dimensions, function (axisDim) { 57037 var dataDimIndex = convertDimNameToNumber(axisDim); 57038 encodeDefine.set(axisDim, dataDimIndex); 57039 }); 57040 } 57041 57042 function convertDimNameToNumber(dimName) { 57043 return +dimName.replace('dim', ''); 57044 } 57045 57046 /* 57047* Licensed to the Apache Software Foundation (ASF) under one 57048* or more contributor license agreements. See the NOTICE file 57049* distributed with this work for additional information 57050* regarding copyright ownership. The ASF licenses this file 57051* to you under the Apache License, Version 2.0 (the 57052* "License"); you may not use this file except in compliance 57053* with the License. You may obtain a copy of the License at 57054* 57055* http://www.apache.org/licenses/LICENSE-2.0 57056* 57057* Unless required by applicable law or agreed to in writing, 57058* software distributed under the License is distributed on an 57059* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57060* KIND, either express or implied. See the License for the 57061* specific language governing permissions and limitations 57062* under the License. 57063*/ 57064 57065 var DEFAULT_SMOOTH = 0.3; 57066 57067 var ParallelView = Chart.extend({ 57068 57069 type: 'parallel', 57070 57071 init: function () { 57072 57073 /** 57074 * @type {module:zrender/container/Group} 57075 * @private 57076 */ 57077 this._dataGroup = new Group(); 57078 57079 this.group.add(this._dataGroup); 57080 57081 /** 57082 * @type {module:echarts/data/List} 57083 */ 57084 this._data; 57085 57086 /** 57087 * @type {boolean} 57088 */ 57089 this._initialized; 57090 }, 57091 57092 /** 57093 * @override 57094 */ 57095 render: function (seriesModel, ecModel, api, payload) { 57096 var dataGroup = this._dataGroup; 57097 var data = seriesModel.getData(); 57098 var oldData = this._data; 57099 var coordSys = seriesModel.coordinateSystem; 57100 var dimensions = coordSys.dimensions; 57101 var seriesScope = makeSeriesScope$2(seriesModel); 57102 57103 data.diff(oldData) 57104 .add(add) 57105 .update(update) 57106 .remove(remove) 57107 .execute(); 57108 57109 function add(newDataIndex) { 57110 var line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys); 57111 updateElCommon(line, data, newDataIndex, seriesScope); 57112 } 57113 57114 function update(newDataIndex, oldDataIndex) { 57115 var line = oldData.getItemGraphicEl(oldDataIndex); 57116 var points = createLinePoints(data, newDataIndex, dimensions, coordSys); 57117 data.setItemGraphicEl(newDataIndex, line); 57118 var animationModel = (payload && payload.animation === false) ? null : seriesModel; 57119 updateProps(line, {shape: {points: points}}, animationModel, newDataIndex); 57120
57121 updateElCommon(line, data, newDataIndex, seriesScope); 57122 } 57123 57124 function remove(oldDataIndex) { 57125 var line = oldData.getItemGraphicEl(oldDataIndex); 57126 dataGroup.remove(line); 57127 } 57128 57129 // First create 57130 if (!this._initialized) { 57131 this._initialized = true; 57132 var clipPath = createGridClipShape$1( 57133 coordSys, seriesModel, function () { 57134 // Callback will be invoked immediately if there is no animation 57135 setTimeout(function () { 57136 dataGroup.removeClipPath(); 57137 }); 57138 } 57139 ); 57140 dataGroup.setClipPath(clipPath); 57141 } 57142 57143 this._data = data; 57144 }, 57145 57146 incrementalPrepareRender: function (seriesModel, ecModel, api) { 57147 this._initialized = true; 57148 this._data = null; 57149 this._dataGroup.removeAll(); 57150 }, 57151 57152 incrementalRender: function (taskParams, seriesModel, ecModel) { 57153 var data = seriesModel.getData(); 57154 var coordSys = seriesModel.coordinateSystem; 57155 var dimensions = coordSys.dimensions; 57156 var seriesScope = makeSeriesScope$2(seriesModel); 57157 57158 for (var dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) { 57159 var line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys); 57160 line.incremental = true; 57161 updateElCommon(line, data, dataIndex, seriesScope); 57162 } 57163 }, 57164 57165 dispose: function () {}, 57166 57167 // _renderForProgressive: function (seriesModel) { 57168 // var dataGroup = this._dataGroup; 57169 // var data = seriesModel.getData(); 57170 // var oldData = this._data; 57171 // var coordSys = seriesModel.coordinateSystem; 57172 // var dimensions = coordSys.dimensions; 57173 // var option = seriesModel.option; 57174 // var progressive = option.progressive; 57175 // var smooth = option.smooth ? SMOOTH : null; 57176 57177 // // In progressive animation is disabled, so use simple data diff, 57178 // // which effects performance less. 57179 // // (Typically performance for data with length 7000+ like: 57180 // // simpleDiff: 60ms, addEl: 184ms, 57181 // // in RMBP 2.4GHz intel i7, OSX 10.9 chrome 50.0.2661.102 (64-bit)) 57182 // if (simpleDiff(oldData, data, dimensions)) { 57183 // dataGroup.removeAll(); 57184 // data.each(function (dataIndex) { 57185 // addEl(data, dataGroup, dataIndex, dimensions, coordSys); 57186 // }); 57187 // } 57188 57189 // updateElCommon(data, progressive, smooth); 57190 57191 // // Consider switch between progressive and not. 57192 // data.__plProgressive = true; 57193 // this._data = data; 57194 // }, 57195 57196 /** 57197 * @override 57198 */ 57199 remove: function () { 57200 this._dataGroup && this._dataGroup.removeAll(); 57201 this._data = null; 57202 } 57203 }); 57204 57205 function createGridClipShape$1(coordSys, seriesModel, cb) { 57206 var parallelModel = coordSys.model; 57207 var rect = coordSys.getRect(); 57208 var rectEl = new Rect({ 57209 shape: { 57210 x: rect.x, 57211 y: rect.y, 57212 width: rect.width, 57213 height: rect.height 57214 } 57215 }); 57216 57217 var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height'; 57218 rectEl.setShape(dim, 0); 57219 initProps(rectEl, { 57220 shape: { 57221 width: rect.width, 57222 height: rect.height 57223 } 57224 }, seriesModel, cb); 57225 return rectEl; 57226 } 57227 57228 function createLinePoints(data, dataIndex, dimensions, coordSys) { 57229 var points = []; 57230 for (var i = 0; i < dimensions.length; i++) { 57231 var dimName = dimensions[i]; 57232 var value = data.get(data.mapDimension(dimName), dataIndex); 57233 if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) { 57234 points.push(coordSys.dataToPoint(value, dimName)); 57235 } 57236 } 57237 return points; 57238 } 57239 57240 function addEl(data, dataGroup, dataIndex, dimensions, coordSys) { 57241 var points = createLinePoints(data, dataIndex, dimensions, coordSys); 57242 var line = new Polyline({ 57243 shape: {points: points}, 57244 silent: true, 57245 z2: 10 57246 }); 57247 dataGroup.add(line); 57248 data.setItemGraphicEl(dataIndex, line); 57249 return line; 57250 } 57251 57252 function makeSeriesScope$2(seriesModel) { 57253 var smooth = seriesModel.get('smooth', true); 57254 smooth === true && (smooth = DEFAULT_SMOOTH); 57255 return { 57256 lineStyle: seriesModel.getModel('lineStyle').getLineStyle(), 57257 smooth: smooth != null ? smooth : DEFAULT_SMOOTH 57258 }; 57259 } 57260 57261 function updateElCommon(el, data, dataIndex, seriesScope) { 57262 var lineStyle = seriesScope.lineStyle; 57263 57264 if (data.hasItemOption) { 57265 var lineStyleModel = data.getItemModel(dataIndex).getModel('lineStyle'); 57266 lineStyle = lineStyleModel.getLineStyle(); 57267 } 57268 57269 el.useStyle(lineStyle); 57270 57271 var elStyle = el.style; 57272 elStyle.fill = null; 57273 // lineStyle.color have been set to itemVisual in module:echarts/visual/seriesColor. 57274 elStyle.stroke = data.getItemVisual(dataIndex, 'color'); 57275 // lineStyle.opacity have been set to itemVisual in parallelVisual. 57276 elStyle.opacity = data.getItemVisual(dataIndex, 'opacity'); 57277 57278 seriesScope.smooth && (el.shape.smooth = seriesScope.smooth); 57279 } 57280 57281// function simpleDiff(oldData, newData, dimensions) { 57282// var oldLen;
57283// if (!oldData 57284// || !oldData.__plProgressive 57285// || (oldLen = oldData.count()) !== newData.count() 57286// ) { 57287// return true; 57288// } 57289 57290// var dimLen = dimensions.length; 57291// for (var i = 0; i < oldLen; i++) { 57292// for (var j = 0; j < dimLen; j++) { 57293// if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) { 57294// return true; 57295// } 57296// } 57297// } 57298 57299// return false; 57300// } 57301 57302// FIXME 57303// Ã¥â¦Â¬Ã§â¨æâ¹æ³â¢? 57304 function isEmptyValue(val, axisType) { 57305 return axisType === 'category' 57306 ? val == null 57307 : (val == null || isNaN(val)); // axisType === 'value' 57308 } 57309 57310 /* 57311* Licensed to the Apache Software Foundation (ASF) under one 57312* or more contributor license agreements. See the NOTICE file 57313* distributed with this work for additional information 57314* regarding copyright ownership. The ASF licenses this file 57315* to you under the Apache License, Version 2.0 (the 57316* "License"); you may not use this file except in compliance 57317* with the License. You may obtain a copy of the License at 57318* 57319* http://www.apache.org/licenses/LICENSE-2.0 57320* 57321* Unless required by applicable law or agreed to in writing, 57322* software distributed under the License is distributed on an 57323* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57324* KIND, either express or implied. See the License for the 57325* specific language governing permissions and limitations 57326* under the License. 57327*/ 57328 57329 57330 var opacityAccessPath$1 = ['lineStyle', 'normal', 'opacity']; 57331 57332 var parallelVisual = { 57333 57334 seriesType: 'parallel', 57335 57336 reset: function (seriesModel, ecModel, api) { 57337 57338 var itemStyleModel = seriesModel.getModel('itemStyle'); 57339 var lineStyleModel = seriesModel.getModel('lineStyle'); 57340 var globalColors = ecModel.get('color'); 57341 57342 var color = lineStyleModel.get('color') 57343 || itemStyleModel.get('color') 57344 || globalColors[seriesModel.seriesIndex % globalColors.length]; 57345 var inactiveOpacity = seriesModel.get('inactiveOpacity'); 57346 var activeOpacity = seriesModel.get('activeOpacity'); 57347 var lineStyle = seriesModel.getModel('lineStyle').getLineStyle(); 57348 57349 var coordSys = seriesModel.coordinateSystem; 57350 var data = seriesModel.getData(); 57351 57352 var opacityMap = { 57353 normal: lineStyle.opacity, 57354 active: activeOpacity, 57355 inactive: inactiveOpacity 57356 }; 57357 57358 data.setVisual('color', color); 57359 57360 function progress(params, data) { 57361 coordSys.eachActiveState(data, function (activeState, dataIndex) { 57362 var opacity = opacityMap[activeState]; 57363 if (activeState === 'normal' && data.hasItemOption) { 57364 var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath$1, true); 57365 itemOpacity != null && (opacity = itemOpacity); 57366 } 57367 data.setItemVisual(dataIndex, 'opacity', opacity); 57368 }, params.start, params.end); 57369 } 57370 57371 return {progress: progress}; 57372 } 57373 }; 57374 57375 /* 57376* Licensed to the Apache Software Foundation (ASF) under one 57377* or more contributor license agreements. See the NOTICE file 57378* distributed with this work for additional information 57379* regarding copyright ownership. The ASF licenses this file 57380* to you under the Apache License, Version 2.0 (the 57381* "License"); you may not use this file except in compliance 57382* with the License. You may obtain a copy of the License at 57383* 57384* http://www.apache.org/licenses/LICENSE-2.0 57385* 57386* Unless required by applicable law or agreed to in writing, 57387* software distributed under the License is distributed on an 57388* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57389* KIND, either express or implied. See the License for the 57390* specific language governing permissions and limitations 57391* under the License. 57392*/ 57393 57394 registerVisual(parallelVisual); 57395 57396 /* 57397* Licensed to the Apache Software Foundation (ASF) under one 57398* or more contributor license agreements. See the NOTICE file 57399* distributed with this work for additional information 57400* regarding copyright ownership. The ASF licenses this file 57401* to you under the Apache License, Version 2.0 (the 57402* "License");
57402 you may not use this file except in compliance 57403* with the License. You may obtain a copy of the License at 57404* 57405* http://www.apache.org/licenses/LICENSE-2.0 57406* 57407* Unless required by applicable law or agreed to in writing, 57408* software distributed under the License is distributed on an 57409* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57410* KIND, either express or implied. See the License for the 57411* specific language governing permissions and limitations 57412* under the License. 57413*/ 57414 57415 /** 57416 * @file Get initial data and define sankey view's series model 57417 * @author Deqing Li([email protected]) 57418 */ 57419 57420 var SankeySeries = SeriesModel.extend({ 57421 57422 type: 'series.sankey', 57423 57424 layoutInfo: null, 57425 57426 /** 57427 * Init a graph data structure from data in option series 57428 * 57429 * @param {Object} option the object used to config echarts view 57430 * @return {module:echarts/data/List} storage initial data 57431 */ 57432 getInitialData: function (option) { 57433 var links = option.edges || option.links; 57434 var nodes = option.data || option.nodes; 57435 if (nodes && links) {
57436 var graph = createGraphFromNodeEdge(nodes, links, this, true); 57437 return graph.data; 57438 } 57439 }, 57440 57441 setNodePosition: function (dataIndex, localPosition) { 57442 var dataItem = this.option.data[dataIndex]; 57443 dataItem.localX = localPosition[0]; 57444 dataItem.localY = localPosition[1]; 57445 }, 57446 57447 /** 57448 * Return the graphic data structure 57449 * 57450 * @return {module:echarts/data/Graph} graphic data structure 57451 */ 57452 getGraph: function () { 57453 return this.getData().graph; 57454 }, 57455 57456 /** 57457 * Get edge data of graphic data structure 57458 * 57459 * @return {module:echarts/data/List} data structure of list 57460 */ 57461 getEdgeData: function () { 57462 return this.getGraph().edgeData; 57463 }, 57464 57465 /** 57466 * @override 57467 */ 57468 formatTooltip: function (dataIndex, multipleSeries, dataType) { 57469 // dataType === 'node' or empty do not show tooltip by default 57470 if (dataType === 'edge') { 57471 var params = this.getDataParams(dataIndex, dataType); 57472 var rawDataOpt = params.data; 57473 var html = rawDataOpt.source + ' -- ' + rawDataOpt.target; 57474 if (params.value) { 57475 html += ' : ' + params.value; 57476 } 57477 return encodeHTML(html); 57478 } 57479 57480 return SankeySeries.superCall(this, 'formatTooltip', dataIndex, multipleSeries); 57481 }, 57482 57483 defaultOption: { 57484 zlevel: 0, 57485 z: 2, 57486 57487 coordinateSystem: 'view', 57488 57489 layout: null, 57490 57491 // the position of the whole view 57492 left: '5%', 57493 top: '5%', 57494 right: '20%', 57495 bottom: '5%', 57496 57497 // the dx of the node 57498 nodeWidth: 20, 57499 57500 // the vertical distance between two nodes 57501 nodeGap: 8, 57502 57503 // control if the node can move or not 57504 draggable: true, 57505 57506 // the number of iterations to change the position of the node 57507 layoutIterations: 32, 57508 57509 label: { 57510 show: true, 57511 position: 'right', 57512 color: '#000', 57513 fontSize: 12 57514 }, 57515 57516 itemStyle: { 57517 borderWidth: 1, 57518 borderColor: '#333' 57519 }, 57520 57521 lineStyle: { 57522 color: '#314656', 57523 opacity: 0.2, 57524 curveness: 0.5 57525 }, 57526 57527 emphasis: { 57528 label: { 57529 show: true 57530 }, 57531 lineStyle: { 57532 opacity: 0.6 57533 } 57534 }, 57535 57536 animationEasing: 'linear', 57537 57538 animationDuration: 1000 57539 } 57540 57541 }); 57542 57543 /* 57544* Licensed to the Apache Software Foundation (ASF) under one 57545* or more contributor license agreements. See the NOTICE file 57546* distributed with this work for additional information 57547* regarding copyright ownership. The ASF licenses this file 57548* to you under the Apache License, Version 2.0 (the 57549* "License"); you may not use this file except in compliance 57550* with the License. You may obtain a copy of the License at 57551* 57552* http://www.apache.org/licenses/LICENSE-2.0 57553* 57554* Unless required by applicable law or agreed to in writing, 57555* software distributed under the License is distributed on an 57556* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57557* KIND, either express or implied. See the License for the 57558* specific language governing permissions and limitations 57559* under the License. 57560*/ 57561 57562 /** 57563 * @file The file used to draw sankey view 57564 * @author Deqing Li([email protected]) 57565 */ 57566 57567 var SankeyShape = extendShape({ 57568 shape: { 57569 x1: 0, y1: 0, 57570 x2: 0, y2: 0, 57571 cpx1: 0, cpy1: 0, 57572 cpx2: 0, cpy2: 0, 57573 57574 extent: 0 57575 }, 57576 57577 buildPath: function (ctx, shape) { 57578 var halfExtent = shape.extent / 2; 57579 ctx.moveTo(shape.x1, shape.y1 - halfExtent); 57580 ctx.bezierCurveTo( 57581 shape.cpx1, shape.cpy1 - halfExtent, 57582 shape.cpx2, shape.cpy2 - halfExtent, 57583 shape.x2, shape.y2 - halfExtent 57584 ); 57585 ctx.lineTo(shape.x2, shape.y2 + halfExtent); 57586 ctx.bezierCurveTo(
57587 shape.cpx2, shape.cpy2 + halfExtent, 57588 shape.cpx1, shape.cpy1 + halfExtent, 57589 shape.x1, shape.y1 + halfExtent 57590 ); 57591 ctx.closePath(); 57592 } 57593 }); 57594 57595 extendChartView({ 57596 57597 type: 'sankey', 57598 57599 /** 57600 * @private 57601 * @type {module:echarts/chart/sankey/SankeySeries} 57602 */ 57603 _model: null, 57604 57605 render: function (seriesModel, ecModel, api) { 57606 var graph = seriesModel.getGraph(); 57607 var group = this.group; 57608 var layoutInfo = seriesModel.layoutInfo; 57609 // view width 57610 var width = layoutInfo.width; 57611 // view height 57612 var height = layoutInfo.height; 57613 57614 var nodeData = seriesModel.getData(); 57615 var edgeData = seriesModel.getData('edge'); 57616 57617 this._model = seriesModel; 57618 57619 group.removeAll(); 57620 57621 group.attr('position', [layoutInfo.x, layoutInfo.y]); 57622 57623 // generate a bezire Curve for each edge 57624 graph.eachEdge(function (edge) { 57625 var curve = new SankeyShape(); 57626 curve.dataIndex = edge.dataIndex; 57627 curve.seriesIndex = seriesModel.seriesIndex; 57628 curve.dataType = 'edge'; 57629 var lineStyleModel = edge.getModel('lineStyle'); 57630 var curvature = lineStyleModel.get('curveness'); 57631 var n1Layout = edge.node1.getLayout(); 57632 var node1Model =edge.node1.getModel(); 57633 var dragX1 = node1Model.get('localX'); 57634 var dragY1 = node1Model.get('localY'); 57635 var n2Layout = edge.node2.getLayout(); 57636 var node2Model = edge.node2.getModel(); 57637 var dragX2 = node2Model.get('localX'); 57638 var dragY2 = node2Model.get('localY'); 57639 var edgeLayout = edge.getLayout(); 57640 57641 curve.shape.extent = Math.max(1, edgeLayout.dy); 57642 57643 var x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx; 57644 var y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy + edgeLayout.dy / 2; 57645 var x2 = dragX2 != null ? dragX2 * width : n2Layout.x; 57646 var y2 = (dragY2 != null ? dragY2 * height : n2Layout.y) + edgeLayout.ty + edgeLayout.dy / 2; 57647 var cpx1 = x1 * (1 - curvature) + x2 * curvature; 57648 var cpy1 = y1; 57649 var cpx2 = x1 * curvature + x2 * (1 - curvature); 57650 var cpy2 = y2; 57651 57652 curve.setShape({ 57653 x1: x1, 57654 y1: y1, 57655 x2: x2, 57656 y2: y2, 57657 cpx1: cpx1, 57658 cpy1: cpy1, 57659 cpx2: cpx2, 57660 cpy2: cpy2 57661 }); 57662 57663 curve.setStyle(lineStyleModel.getItemStyle()); 57664 // Special color, use source node color or target node color 57665 switch (curve.style.fill) { 57666 case 'source': 57667 curve.style.fill = edge.node1.getVisual('color'); 57668 break; 57669 case 'target': 57670 curve.style.fill = edge.node2.getVisual('color'); 57671 break; 57672 } 57673 57674 setHoverStyle(curve, edge.getModel('emphasis.lineStyle').getItemStyle()); 57675 57676 group.add(curve); 57677 57678 edgeData.setItemGraphicEl(edge.dataIndex, curve); 57679 }); 57680 57681 // generate a rect for each node 57682 graph.eachNode(function (node) { 57683 var layout = node.getLayout(); 57684 var itemModel = node.getModel(); 57685 var dragX = itemModel.get('localX'); 57686 var dragY = itemModel.get('localY'); 57687 var labelModel = itemModel.getModel('label'); 57688 var labelHoverModel = itemModel.getModel('emphasis.label'); 57689 57690 var rect = new Rect({ 57691 shape: { 57692 x: dragX != null ? dragX * width : layout.x, 57693 y: dragY != null ? dragY * height : layout.y, 57694 width: layout.dx, 57695 height: layout.dy 57696 }, 57697 style: itemModel.getModel('itemStyle').getItemStyle() 57698 }); 57699 57700 var hoverStyle = node.getModel('emphasis.itemStyle').getItemStyle(); 57701 57702 setLabelStyle( 57703 rect.style, hoverStyle, labelModel, labelHoverModel, 57704 { 57705 labelFetcher: seriesModel, 57706 labelDataIndex: node.dataIndex, 57707 defaultText: node.id, 57708 isRectText: true 57709 } 57710 ); 57711 57712 rect.setStyle('fill', node.getVisual('color')); 57713 57714 setHoverStyle(rect, hoverStyle); 57715 57716 group.add(rect); 57717 57718 nodeData.setItemGraphicEl(node.dataIndex, rect); 57719 57720 rect.dataType = 'node'; 57721 }); 57722 57723 var draggable = seriesModel.get('draggable'); 57724 if (draggable) { 57725 nodeData.eachItemGraphicEl(function (el, dataIndex) { 57726 el.drift = function (dx, dy) { 57727 this.shape.x += dx; 57728 this.shape.y += dy; 57729 this.dirty(); 57730 api.dispatchAction({ 57731 type: 'dragNode', 57732 seriesId: seriesModel.id, 57733 dataIndex: nodeData.getRawIndex(dataIndex), 57734 localX: this.shape.x / width, 57735 localY: this.shape.y / height 57736 }); 57737 }; 57738 57739 el.draggable = true; 57740 el.cursor = 'move'; 57741 }); 57742 } 57743 57744 if (!this._data && seriesModel.get('animation')) { 57745 group.setClipPath(createGridClipShape$2(group.getBoundingRect
57745(), seriesModel, function () { 57746 group.removeClipPath(); 57747 })); 57748 } 57749 57750 this._data = seriesModel.getData(); 57751 }, 57752 57753 dispose: function () {} 57754 }); 57755 57756// add animation to the view 57757 function createGridClipShape$2(rect, seriesModel, cb) { 57758 var rectEl = new Rect({ 57759 shape: { 57760 x: rect.x - 10, 57761 y: rect.y - 10, 57762 width: 0, 57763 height: rect.height + 20 57764 } 57765 }); 57766 initProps(rectEl, { 57767 shape: { 57768 width: rect.width + 20, 57769 height: rect.height + 20 57770 } 57771 }, seriesModel, cb); 57772 57773 return rectEl; 57774 } 57775 57776 /* 57777* Licensed to the Apache Software Foundation (ASF) under one 57778* or more contributor license agreements. See the NOTICE file 57779* distributed with this work for additional information 57780* regarding copyright ownership. The ASF licenses this file 57781* to you under the Apache License, Version 2.0 (the 57782* "License"); you may not use this file except in compliance 57783* with the License. You may obtain a copy of the License at 57784* 57785* http://www.apache.org/licenses/LICENSE-2.0 57786* 57787* Unless required by applicable law or agreed to in writing, 57788* software distributed under the License is distributed on an 57789* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57790* KIND, either express or implied. See the License for the 57791* specific language governing permissions and limitations 57792* under the License. 57793*/ 57794 57795 registerAction({ 57796 type: 'dragNode', 57797 event: 'dragNode', 57798 // here can only use 'update' now, other value is not support in echarts. 57799 update: 'update' 57800 }, function (payload, ecModel) { 57801 ecModel.eachComponent({mainType: 'series', subType: 'sankey', query: payload}, function (seriesModel) { 57802 seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]); 57803 }); 57804 }); 57805 57806 /* 57807* Licensed to the Apache Software Foundation (ASF) under one 57808* or more contributor license agreements. See the NOTICE file 57809* distributed with this work for additional information 57810* regarding copyright ownership. The ASF licenses this file 57811* to you under the Apache License, Version 2.0 (the 57812* "License"); you may not use this file except in compliance 57813* with the License. You may obtain a copy of the License at 57814* 57815* http://www.apache.org/licenses/LICENSE-2.0 57816* 57817* Unless required by applicable law or agreed to in writing, 57818* software distributed under the License is distributed on an 57819* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57820* KIND, either express or implied. See the License for the 57821* specific language governing permissions and limitations 57822* under the License. 57823*/ 57824 57825 /* 57826* The implementation references to d3.js. The use of the source 57827* code of this file is also subject to the terms and consitions 57828* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>). 57829*/ 57830 57831 57832 /** 57833 * nest helper used to group by the array. 57834 * can specified the keys and sort the keys. 57835 */ 57836 function nest() { 57837 57838 var keysFunction = []; 57839 var sortKeysFunction = []; 57840 57841 /** 57842 * map an Array into the mapObject. 57843 * @param {Array} array 57844 * @param {number} depth 57845 */ 57846 function map$$1(array, depth) { 57847 if (depth >= keysFunction.length) { 57848 return array; 57849 } 57850 var i = -1; 57851 var n = array.length; 57852 var keyFunction = keysFunction[depth++]; 57853 var mapObject = {}; 57854 var valuesByKey = {}; 57855 57856 while (++i < n) { 57857 var keyValue = keyFunction(array[i]); 57858 var values = valuesByKey[keyValue]; 57859 57860 if (values) { 57861 values.push(array[i]); 57862 } 57863 else { 57864 valuesByKey[keyValue] = [array[i]]; 57865 } 57866 } 57867 57868 each$1(valuesByKey, function (value, key) {
57869 mapObject[key] = map$$1(value, depth); 57870 }); 57871 57872 return mapObject; 57873 } 57874 57875 /** 57876 * transform the Map Object to multidimensional Array 57877 * @param {Object} map 57878 * @param {number} depth 57879 */ 57880 function entriesMap(mapObject, depth) { 57881 if (depth >= keysFunction.length) { 57882 return mapObject; 57883 } 57884 var array = []; 57885 var sortKeyFunction = sortKeysFunction[depth++]; 57886 57887 each$1(mapObject, function (value, key) { 57888 array.push({ 57889 key: key, values: entriesMap(value, depth) 57890 }); 57891 }); 57892 57893 if (sortKeyFunction) { 57894 return array.sort(function (a, b) { 57895 return sortKeyFunction(a.key, b.key); 57896 }); 57897 } 57898 else { 57899 return array; 57900 } 57901 } 57902 57903 return { 57904 /** 57905 * specified the key to groupby the arrays. 57906 * users can specified one more keys. 57907 * @param {Function} d 57908 */ 57909 key: function (d) { 57910 keysFunction.push(d); 57911 return this; 57912 }, 57913 57914 /** 57915 * specified the comparator to sort the keys 57916 * @param {Function} order 57917 */ 57918 sortKeys: function (order) { 57919 sortKeysFunction[keysFunction.length - 1] = order; 57920 return this; 57921 }, 57922 57923 /** 57924 * the array to be grouped by. 57925 * @param {Array} array 57926 */ 57927 entries: function (array) { 57928 return entriesMap(map$$1(array, 0), 0); 57929 } 57930 }; 57931 } 57932 57933 /* 57934* Licensed to the Apache Software Foundation (ASF) under one 57935* or more contributor license agreements. See the NOTICE file 57936* distributed with this work for additional information 57937* regarding copyright ownership. The ASF licenses this file 57938* to you under the Apache License, Version 2.0 (the 57939* "License"); you may not use this file except in compliance 57940* with the License. You may obtain a copy of the License at 57941* 57942* http://www.apache.org/licenses/LICENSE-2.0 57943* 57944* Unless required by applicable law or agreed to in writing, 57945* software distributed under the License is distributed on an 57946* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 57947* KIND, either express or implied. See the License for the 57948* specific language governing permissions and limitations 57949* under the License. 57950*/ 57951 57952 /** 57953 * @file The layout algorithm of sankey view 57954 * @author Deqing Li([email protected]) 57955 */ 57956 57957 var sankeyLayout = function (ecModel, api, payload) { 57958 57959 ecModel.eachSeriesByType('sankey', function (seriesModel) { 57960 57961 var nodeWidth = seriesModel.get('nodeWidth'); 57962 var nodeGap = seriesModel.get('nodeGap'); 57963 57964 var layoutInfo = getViewRect$3(seriesModel, api); 57965 57966 seriesModel.layoutInfo = layoutInfo; 57967 57968 var width = layoutInfo.width; 57969 var height = layoutInfo.height; 57970 57971 var graph = seriesModel.getGraph(); 57972 57973 var nodes = graph.nodes; 57974 var edges = graph.edges; 57975 57976 computeNodeValues(nodes); 57977 57978 var filteredNodes = filter(nodes, function (node) { 57979 return node.getLayout().value === 0; 57980 }); 57981 57982 var iterations = filteredNodes.length !== 0 57983 ? 0 : seriesModel.get('layoutIterations'); 57984 57985 layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations); 57986 }); 57987 }; 57988 57989 /** 57990 * Get the layout position of the whole view 57991 * 57992 * @param {module:echarts/model/Series} seriesModel the model object of sankey series 57993 * @param {module:echarts/ExtensionAPI} api provide the API list that the developer can call 57994 * @return {module:zrender/core/BoundingRect} size of rect to draw the sankey view 57995 */ 57996 function getViewRect$3(seriesModel, api) { 57997 return getLayoutRect( 57998 seriesModel.getBoxLayoutParams(), { 57999 width: api.getWidth(), 58000 height: api.getHeight() 58001 } 58002 ); 58003 } 58004 58005 function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations) { 58006 computeNodeBreadths(nodes, edges, nodeWidth, width); 58007 computeNodeDepths(nodes, edges, height, nodeGap, iterations); 58008 computeEdgeDepths(nodes); 58009 } 58010 58011 /** 58012 * Compute the value of each node by summing the associated edge's value 58013 *
58014 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58015 */ 58016 function computeNodeValues(nodes) { 58017 each$1(nodes, function (node) { 58018 var value1 = sum(node.outEdges, getEdgeValue); 58019 var value2 = sum(node.inEdges, getEdgeValue); 58020 var value = Math.max(value1, value2); 58021 node.setLayout({value: value}, true); 58022 }); 58023 } 58024 58025 /** 58026 * Compute the x-position for each node. 58027 * 58028 * Here we use Kahn algorithm to detect cycle when we traverse 58029 * the node to computer the initial x position. 58030 * 58031 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58032 * @param {number} nodeWidth the dx of the node 58033 * @param {number} width the whole width of the area to draw the view 58034 */ 58035 function computeNodeBreadths(nodes, edges, nodeWidth, width) { 58036 // Used to mark whether the edge is deleted. if it is deleted, 58037 // the value is 0, otherwise it is 1. 58038 var remainEdges = []; 58039 // Storage each node's indegree. 58040 var indegreeArr = []; 58041 //Used to storage the node with indegree is equal to 0. 58042 var zeroIndegrees = []; 58043 var nextNode = []; 58044 var x = 0; 58045 var kx = 0; 58046 58047 for (var i = 0; i < edges.length; i++) { 58048 remainEdges[i] = 1; 58049 } 58050 58051 for (var i = 0; i < nodes.length; i++) { 58052 indegreeArr[i] = nodes[i].inEdges.length; 58053 if (indegreeArr[i] === 0) { 58054 zeroIndegrees.push(nodes[i]); 58055 } 58056 } 58057 58058 while (zeroIndegrees.length) { 58059 each$1(zeroIndegrees, function (node) { 58060 node.setLayout({x: x}, true); 58061 node.setLayout({dx: nodeWidth}, true); 58062 each$1(node.outEdges, function (edge) { 58063 var indexEdge = edges.indexOf(edge); 58064 remainEdges[indexEdge] = 0; 58065 var targetNode = edge.node2; 58066 var nodeIndex = nodes.indexOf(targetNode); 58067 if (--indegreeArr[nodeIndex] === 0) { 58068 nextNode.push(targetNode); 58069 } 58070 }); 58071 }); 58072 58073 ++x; 58074 zeroIndegrees = nextNode; 58075 nextNode = []; 58076 } 58077 58078 for (var i = 0; i < remainEdges.length; i++) { 58079 if (__DEV__) { 58080 if (remainEdges[i] === 1) { 58081 throw new Error('Sankey is a DAG, the original data has cycle!'); 58082 } 58083 } 58084 } 58085 58086 moveSinksRight(nodes, x); 58087 kx = (width - nodeWidth) / (x - 1); 58088 58089 scaleNodeBreadths(nodes, kx); 58090 } 58091 58092 /** 58093 * All the node without outEgdes are assigned maximum x-position and 58094 * be aligned in the last column. 58095 * 58096 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58097 * @param {number} x value (x-1) use to assign to node without outEdges 58098 * as x-position 58099 */ 58100 function moveSinksRight(nodes, x) { 58101 each$1(nodes, function (node) { 58102 if (!node.outEdges.length) { 58103 node.setLayout({x: x - 1}, true); 58104 } 58105 }); 58106 } 58107 58108 /** 58109 * Scale node x-position to the width 58110 * 58111 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58112 * @param {number} kx multiple used to scale nodes 58113 */ 58114 function scaleNodeBreadths(nodes, kx) { 58115 each$1(nodes, function (node) { 58116 var nodeX = node.getLayout().x * kx; 58117 node.setLayout({x: nodeX}, true); 58118 }); 58119 } 58120 58121 /** 58122 * Using Gauss-Seidel iterations method to compute the node depth(y-position) 58123 * 58124 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58125 * @param {module:echarts/data/Graph~Edge} edges edge of sankey view 58126 * @param {number} height the whole height of the area to draw the view 58127 * @param {number} nodeGap the vertical distance between two nodes 58128 * in the same column. 58129 * @param {number} iterations the number of iterations for the algorithm 58130 */ 58131 function computeNodeDepths(nodes, edges, height, nodeGap, iterations) { 58132 var nodesByBreadth = nest() 58133 .key(function (d) { 58134 return d.getLayout().x; 58135 }) 58136 .sortKeys(ascending)
58137 .entries(nodes) 58138 .map(function (d) { 58139 return d.values; 58140 }); 58141 58142 initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap); 58143 resolveCollisions(nodesByBreadth, nodeGap, height); 58144 58145 for (var alpha = 1; iterations > 0; iterations--) { 58146 // 0.99 is a experience parameter, ensure that each iterations of 58147 // changes as small as possible. 58148 alpha *= 0.99; 58149 relaxRightToLeft(nodesByBreadth, alpha); 58150 resolveCollisions(nodesByBreadth, nodeGap, height); 58151 relaxLeftToRight(nodesByBreadth, alpha); 58152 resolveCollisions(nodesByBreadth, nodeGap, height); 58153 } 58154 } 58155 58156 /** 58157 * Compute the original y-position for each node 58158 * 58159 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58160 * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth 58161 * group by the array of all sankey nodes based on the nodes x-position. 58162 * @param {module:echarts/data/Graph~Edge} edges edge of sankey view 58163 * @param {number} height the whole height of the area to draw the view 58164 * @param {number} nodeGap the vertical distance between two nodes 58165 */ 58166 function initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap) { 58167 var kyArray = []; 58168 each$1(nodesByBreadth, function (nodes) { 58169 var n = nodes.length; 58170 var sum = 0; 58171 each$1(nodes, function (node) { 58172 sum += node.getLayout().value; 58173 }); 58174 var ky = (height - (n - 1) * nodeGap) / sum; 58175 kyArray.push(ky); 58176 }); 58177 58178 kyArray.sort(function (a, b) { 58179 return a - b; 58180 }); 58181 var ky0 = kyArray[0]; 58182 58183 each$1(nodesByBreadth, function (nodes) { 58184 each$1(nodes, function (node, i) { 58185 node.setLayout({y: i}, true); 58186 var nodeDy = node.getLayout().value * ky0; 58187 node.setLayout({dy: nodeDy}, true); 58188 }); 58189 }); 58190 58191 each$1(edges, function (edge) { 58192 var edgeDy = +edge.getValue() * ky0; 58193 edge.setLayout({dy: edgeDy}, true); 58194 }); 58195 } 58196 58197 /** 58198 * Resolve the collision of initialized depth (y-position) 58199 * 58200 * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth 58201 * group by the array of all sankey nodes based on the nodes x-position. 58202 * @param {number} nodeGap the vertical distance between two nodes 58203 * @param {number} height the whole height of the area to draw the view 58204 */ 58205 function resolveCollisions(nodesByBreadth, nodeGap, height) { 58206 each$1(nodesByBreadth, function (nodes) { 58207 var node; 58208 var dy; 58209 var y0 = 0; 58210 var n = nodes.length; 58211 var i; 58212 58213 nodes.sort(ascendingDepth); 58214 58215 for (i = 0; i < n; i++) { 58216 node = nodes[i]; 58217 dy = y0 - node.getLayout().y; 58218 if (dy > 0) { 58219 var nodeY = node.getLayout().y + dy; 58220 node.setLayout({y: nodeY}, true); 58221 } 58222 y0 = node.getLayout().y + node.getLayout().dy + nodeGap; 58223 } 58224 58225 // If the bottommost node goes outside the bounds, push it back up 58226 dy = y0 - nodeGap - height; 58227 if (dy > 0) { 58228 var nodeY = node.getLayout().y - dy; 58229 node.setLayout({y: nodeY}, true); 58230 y0 = node.getLayout().y; 58231 for (i = n - 2; i >= 0; --i) { 58232 node = nodes[i]; 58233 dy = node.getLayout().y + node.getLayout().dy + nodeGap - y0; 58234 if (dy > 0) { 58235 nodeY = node.getLayout().y - dy; 58236 node.setLayout({y: nodeY}, true); 58237 } 58238 y0 = node.getLayout().y; 58239 } 58240 } 58241 }); 58242 } 58243 58244 /** 58245 * Change the y-position of the nodes, except most the right side nodes 58246 *
58247 * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth 58248 * group by the array of all sankey nodes based on the node x-position. 58249 * @param {number} alpha parameter used to adjust the nodes y-position 58250 */ 58251 function relaxRightToLeft(nodesByBreadth, alpha) { 58252 each$1(nodesByBreadth.slice().reverse(), function (nodes) { 58253 each$1(nodes, function (node) { 58254 if (node.outEdges.length) { 58255 var y = sum(node.outEdges, weightedTarget) / sum(node.outEdges, getEdgeValue); 58256 var nodeY = node.getLayout().y + (y - center$1(node)) * alpha; 58257 node.setLayout({y: nodeY}, true); 58258 } 58259 }); 58260 }); 58261 } 58262 58263 function weightedTarget(edge) { 58264 return center$1(edge.node2) * edge.getValue(); 58265 } 58266 58267 /** 58268 * Change the y-position of the nodes, except most the left side nodes 58269 * 58270 * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth 58271 * group by the array of all sankey nodes based on the node x-position. 58272 * @param {number} alpha parameter used to adjust the nodes y-position 58273 */ 58274 function relaxLeftToRight(nodesByBreadth, alpha) { 58275 each$1(nodesByBreadth, function (nodes) { 58276 each$1(nodes, function (node) { 58277 if (node.inEdges.length) { 58278 var y = sum(node.inEdges, weightedSource) / sum(node.inEdges, getEdgeValue); 58279 var nodeY = node.getLayout().y + (y - center$1(node)) * alpha; 58280 node.setLayout({y: nodeY}, true); 58281 } 58282 }); 58283 }); 58284 } 58285 58286 function weightedSource(edge) { 58287 return center$1(edge.node1) * edge.getValue(); 58288 } 58289 58290 /** 58291 * Compute the depth(y-position) of each edge 58292 * 58293 * @param {module:echarts/data/Graph~Node} nodes node of sankey view 58294 */ 58295 function computeEdgeDepths(nodes) { 58296 each$1(nodes, function (node) { 58297 node.outEdges.sort(ascendingTargetDepth); 58298 node.inEdges.sort(ascendingSourceDepth); 58299 }); 58300 each$1(nodes, function (node) { 58301 var sy = 0; 58302 var ty = 0; 58303 each$1(node.outEdges, function (edge) { 58304 edge.setLayout({sy: sy}, true); 58305 sy += edge.getLayout().dy; 58306 }); 58307 each$1(node.inEdges, function (edge) { 58308 edge.setLayout({ty: ty}, true); 58309 ty += edge.getLayout().dy; 58310 }); 58311 }); 58312 } 58313 58314 function ascendingTargetDepth(a, b) { 58315 return a.node2.getLayout().y - b.node2.getLayout().y; 58316 } 58317 58318 function ascendingSourceDepth(a, b) { 58319 return a.node1.getLayout().y - b.node1.getLayout().y; 58320 } 58321 58322 function sum(array, f) { 58323 var sum = 0; 58324 var len = array.length; 58325 var i = -1; 58326 while (++i < len) { 58327 var value = +f.call(array, array[i], i); 58328 if (!isNaN(value)) { 58329 sum += value; 58330 } 58331 } 58332 return sum; 58333 } 58334 58335 function center$1(node) { 58336 return node.getLayout().y + node.getLayout().dy / 2; 58337 } 58338 58339 function ascendingDepth(a, b) { 58340 return a.getLayout().y - b.getLayout().y; 58341 } 58342 58343 function ascending(a, b) { 58344 return a - b; 58345 } 58346 58347 function getEdgeValue(edge) { 58348 return edge.getValue(); 58349 } 58350 58351 /* 58352* Licensed to the Apache Software Foundation (ASF) under one 58353* or more contributor license agreements. See the NOTICE file 58354* distributed with this work for additional information 58355* regarding copyright ownership. The ASF licenses this file 58356* to you under the Apache License, Version 2.0 (the 58357* "License"); you may not use this file except in compliance 58358* with the License. You may obtain a copy of the License at 58359* 58360* http://www.apache.org/licenses/LICENSE-2.0 58361* 58362* Unless required by applicable law or agreed to in writing, 58363* software distributed under the License is distributed on an 58364* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58365* KIND, either express or implied. See the License for the 58366* specific language governing permissions and limitations 58367* under the License. 58368*/ 58369 58370 /** 58371 * @file Visual encoding for sankey view 58372 * @author Deqing Li([email protected]) 58373 */ 58374 58375 var sankeyVisual = function (ecModel, payload) { 58376 ecModel.eachSeriesByType('sankey', function (seriesModel) { 58377 var graph = seriesModel.getGraph(); 58378 var nodes = graph.nodes; 58379 if (nodes.length) { 58380 var minValue = Infinity; 58381 var maxValue = -Infinity; 58382 each$1(nodes, function (node) { 58383 var nodeValue = node.getLayout().value; 58384 if (nodeValue < minValue) { 58385 minValue = nodeValue; 58386 } 58387 if (nodeValue > maxValue) { 58388 maxValue = nodeValue; 58389 } 58390 }); 58391 58392 each$1(nodes, function (node) { 58393 var mapping = new VisualMapping({ 58394 type: 'color', 58395 mappingMethod: 'linear', 58396 dataExtent: [minValue, maxValue], 58397 visual: seriesModel.get('color') 58398 }); 58399 58400 var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value); 58401 node.setVisual('color', mapValueToColor); 58402 // If set itemStyle.normal.color
58403 var itemModel = node.getModel(); 58404 var customColor = itemModel.get('itemStyle.color'); 58405 if (customColor != null) { 58406 node.setVisual('color', customColor); 58407 } 58408 }); 58409 } 58410 }); 58411 }; 58412 58413 /* 58414* Licensed to the Apache Software Foundation (ASF) under one 58415* or more contributor license agreements. See the NOTICE file 58416* distributed with this work for additional information 58417* regarding copyright ownership. The ASF licenses this file 58418* to you under the Apache License, Version 2.0 (the 58419* "License"); you may not use this file except in compliance 58420* with the License. You may obtain a copy of the License at 58421* 58422* http://www.apache.org/licenses/LICENSE-2.0 58423* 58424* Unless required by applicable law or agreed to in writing, 58425* software distributed under the License is distributed on an 58426* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58427* KIND, either express or implied. See the License for the 58428* specific language governing permissions and limitations 58429* under the License. 58430*/ 58431 58432 registerLayout(sankeyLayout); 58433 registerVisual(sankeyVisual); 58434 58435 /* 58436* Licensed to the Apache Software Foundation (ASF) under one 58437* or more contributor license agreements. See the NOTICE file 58438* distributed with this work for additional information 58439* regarding copyright ownership. The ASF licenses this file 58440* to you under the Apache License, Version 2.0 (the 58441* "License"); you may not use this file except in compliance 58442* with the License. You may obtain a copy of the License at 58443* 58444* http://www.apache.org/licenses/LICENSE-2.0 58445* 58446* Unless required by applicable law or agreed to in writing, 58447* software distributed under the License is distributed on an 58448* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58449* KIND, either express or implied. See the License for the 58450* specific language governing permissions and limitations 58451* under the License. 58452*/ 58453 58454 58455 var seriesModelMixin = { 58456 58457 /** 58458 * @private 58459 * @type {string} 58460 */ 58461 _baseAxisDim: null, 58462 58463 /** 58464 * @override 58465 */ 58466 getInitialData: function (option, ecModel) { 58467 // When both types of xAxis and yAxis are 'value', layout is 58468 // needed to be specified by user. Otherwise, layout can be 58469 // judged by which axis is category. 58470 58471 var ordinalMeta; 58472 58473 var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex')); 58474 var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex')); 58475 var xAxisType = xAxisModel.get('type'); 58476 var yAxisType = yAxisModel.get('type'); 58477 var addOrdinal; 58478 58479 // FIXME 58480 // Ã¨â¬ÆÃ¨â¢âæâ¶éâ´è½´ 58481 58482 if (xAxisType === 'category') { 58483 option.layout = 'horizontal'; 58484 ordinalMeta = xAxisModel.getOrdinalMeta(); 58485 addOrdinal = true; 58486 } 58487 else if (yAxisType === 'category') { 58488 option.layout = 'vertical'; 58489 ordinalMeta = yAxisModel.getOrdinalMeta(); 58490 addOrdinal = true; 58491 } 58492 else { 58493 option.layout = option.layout || 'horizontal'; 58494 } 58495 58496 var coordDims = ['x', 'y']; 58497 var baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1; 58498 var baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex]; 58499 var otherAxisDim = coordDims[1 - baseAxisDimIndex]; 58500 var axisModels = [xAxisModel, yAxisModel]; 58501 var baseAxisType = axisModels[baseAxisDimIndex].get('type'); 58502 var otherAxisType = axisModels[1 - baseAxisDimIndex].get('type'); 58503 var data = option.data; 58504 58505 // ??? FIXME make a stage to perform data transfrom. 58506 // MUST create a new data, consider setOption({}) again. 58507 if (data && addOrdinal) { 58508 var newOptionData = []; 58509 each$1(data, function (item, index) { 58510 var newItem; 58511 if (item.value && isArray(item.value)) { 58512 newItem = item.value.slice(); 58513 item.value.unshift(index); 58514 } 58515 else if (isArray(item)) { 58516 newItem = item.slice(); 58517 item.unshift(index); 58518 } 58519 else { 58520 newItem = item; 58521 } 58522 newOptionData.push(newItem); 58523 }); 58524 option.data = newOptionData; 58525 } 58526 58527 var defaultValueDimensions = this.defaultValueDimensions; 58528 58529 return createListSimply( 58530 this, 58531 { 58532 coordDimensions: [{ 58533 name: baseAxisDim, 58534 type: getDimensionTypeByAxis(baseAxisType), 58535 ordinalMeta: ordinalMeta, 58536 otherDims: { 58537 tooltip: false, 58538 itemName: 0 58539 }, 58540 dimsDef: ['base'] 58541 }, { 58542 name: otherAxisDim, 58543 type: getDimensionTypeByAxis(otherAxisType), 58544 dimsDef: defaultValueDimensions.slice() 58545 }], 58546 dimensionsCount: defaultValueDimensions.length + 1 58547 } 58548 ); 58549 }, 58550 58551 /** 58552 * If horizontal, base axis is x, otherwise y. 58553 * @override 58554 */ 58555 getBaseAxis: function () { 58556 var dim = this._baseAxisDim; 58557 return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis; 58558 } 58559 58560 }; 58561 58562 /* 58563* Licensed to the Apache Software Foundation (ASF) under one 58564* or more contributor license agreements. See the NOTICE file 58565* distributed with this work for additional information 58566* regarding copyright ownership. The ASF licenses this file 58567* to you under the Apache License, Version 2.0 (the 58568* "License");
58568 you may not use this file except in compliance 58569* with the License. You may obtain a copy of the License at 58570* 58571* http://www.apache.org/licenses/LICENSE-2.0 58572* 58573* Unless required by applicable law or agreed to in writing, 58574* software distributed under the License is distributed on an 58575* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58576* KIND, either express or implied. See the License for the 58577* specific language governing permissions and limitations 58578* under the License. 58579*/ 58580 58581 var BoxplotSeries = SeriesModel.extend({ 58582 58583 type: 'series.boxplot', 58584 58585 dependencies: ['xAxis', 'yAxis', 'grid'], 58586 58587 // TODO 58588 // box width represents group size, so dimension should have 'size'. 58589 58590 /** 58591 * @see <https://en.wikipedia.org/wiki/Box_plot> 58592 * The meanings of 'min' and 'max' depend on user, 58593 * and echarts do not need to know it. 58594 * @readOnly 58595 */ 58596 defaultValueDimensions: [ 58597 {name: 'min', defaultTooltip: true}, 58598 {name: 'Q1', defaultTooltip: true}, 58599 {name: 'median', defaultTooltip: true}, 58600 {name: 'Q3', defaultTooltip: true}, 58601 {name: 'max', defaultTooltip: true} 58602 ], 58603 58604 /** 58605 * @type {Array.<string>} 58606 * @readOnly 58607 */ 58608 dimensions: null, 58609 58610 /** 58611 * @override 58612 */ 58613 defaultOption: { 58614 zlevel: 0, // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 58615 z: 2, // äºÅ级å±âå 58616 coordinateSystem: 'cartesian2d', 58617 legendHoverLink: true, 58618 58619 hoverAnimation: true, 58620 58621 // xAxisIndex: 0, 58622 // yAxisIndex: 0, 58623 58624 layout: null, // 'horizontal' or 'vertical' 58625 boxWidth: [7, 50], // [min, max] can be percent of band width. 58626 58627 itemStyle: { 58628 color: '#fff', 58629 borderWidth: 1 58630 }, 58631 58632 emphasis: { 58633 itemStyle: { 58634 borderWidth: 2, 58635 shadowBlur: 5, 58636 shadowOffsetX: 2, 58637 shadowOffsetY: 2, 58638 shadowColor: 'rgba(0,0,0,0.4)' 58639 } 58640 }, 58641 58642 animationEasing: 'elasticOut', 58643 animationDuration: 800 58644 } 58645 }); 58646 58647 mixin(BoxplotSeries, seriesModelMixin, true); 58648 58649 /* 58650* Licensed to the Apache Software Foundation (ASF) under one 58651* or more contributor license agreements. See the NOTICE file 58652* distributed with this work for additional information 58653* regarding copyright ownership. The ASF licenses this file 58654* to you under the Apache License, Version 2.0 (the 58655* "License"); you may not use this file except in compliance 58656* with the License. You may obtain a copy of the License at 58657* 58658* http://www.apache.org/licenses/LICENSE-2.0 58659* 58660* Unless required by applicable law or agreed to in writing, 58661* software distributed under the License is distributed on an 58662* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58663* KIND, either express or implied. See the License for the 58664* specific language governing permissions and limitations 58665* under the License. 58666*/ 58667 58668// Update common properties 58669 var NORMAL_ITEM_STYLE_PATH = ['itemStyle']; 58670 var EMPHASIS_ITEM_STYLE_PATH = ['emphasis', 'itemStyle']; 58671 58672 var BoxplotView = Chart.extend({ 58673 58674 type: 'boxplot', 58675 58676 render: function (seriesModel, ecModel, api) { 58677 var data = seriesModel.getData(); 58678 var group = this.group; 58679 var oldData = this._data; 58680 58681 // There is no old data only when first rendering or switching from 58682 // stream mode to normal mode, where previous elements should be removed. 58683 if (!this._data) { 58684 group.removeAll(); 58685 } 58686 58687 var constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0; 58688 58689 data.diff(oldData) 58690 .add(function (newIdx) { 58691 if (data.hasValue(newIdx)) { 58692 var itemLayout = data.getItemLayout(newIdx); 58693 var symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true); 58694 data.setItemGraphicEl(newIdx, symbolEl); 58695 group.add(symbolEl); 58696 } 58697 }) 58698 .update(function (newIdx, oldIdx) { 58699 var symbolEl = oldData.getItemGraphicEl(oldIdx); 58700 58701 // Empty data 58702 if (!data.hasValue(newIdx)) { 58703 group.remove(symbolEl); 58704 return; 58705 } 58706 58707 var itemLayout = data.getItemLayout(newIdx); 58708 if (!symbolEl) { 58709 symbolEl = createNormalBox(itemLayout, data, newIdx, constDim); 58710 } 58711 else { 58712 updateNormalBoxData(itemLayout, symbolEl, data, newIdx); 58713 } 58714 58715 group.add(symbolEl); 58716 58717 data.setItemGraphicEl(newIdx, symbolEl); 58718 }) 58719 .remove(function (oldIdx) { 58720 var el = oldData.getItemGraphicEl(oldIdx); 58721 el && group.remove(el); 58722 }) 58723 .execute(); 58724 58725 this._data = data; 58726 }, 58727 58728 remove: function (ecModel) { 58729 var group = this.group; 58730 var data = this._data; 58731 this._data = null; 58732 data && data.eachItemGraphicEl(function (el) { 58733 el && group.remove(el); 58734 }); 58735 }, 58736 58737 dispose: noop 58738 58739 });
58740 58741 58742 var BoxPath = Path.extend({ 58743 58744 type: 'boxplotBoxPath', 58745 58746 shape: {}, 58747 58748 buildPath: function (ctx, shape) { 58749 var ends = shape.points; 58750 58751 var i = 0; 58752 ctx.moveTo(ends[i][0], ends[i][1]); 58753 i++; 58754 for (; i < 4; i++) { 58755 ctx.lineTo(ends[i][0], ends[i][1]); 58756 } 58757 ctx.closePath(); 58758 58759 for (; i < ends.length; i++) { 58760 ctx.moveTo(ends[i][0], ends[i][1]); 58761 i++; 58762 ctx.lineTo(ends[i][0], ends[i][1]); 58763 } 58764 } 58765 }); 58766 58767 58768 function createNormalBox(itemLayout, data, dataIndex, constDim, isInit) { 58769 var ends = itemLayout.ends; 58770 58771 var el = new BoxPath({ 58772 shape: { 58773 points: isInit 58774 ? transInit(ends, constDim, itemLayout) 58775 : ends 58776 } 58777 }); 58778 58779 updateNormalBoxData(itemLayout, el, data, dataIndex, isInit); 58780 58781 return el; 58782 } 58783 58784 function updateNormalBoxData(itemLayout, el, data, dataIndex, isInit) { 58785 var seriesModel = data.hostModel; 58786 var updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; 58787 58788 updateMethod( 58789 el, 58790 {shape: {points: itemLayout.ends}}, 58791 seriesModel, 58792 dataIndex 58793 ); 58794 58795 var itemModel = data.getItemModel(dataIndex); 58796 var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH); 58797 var borderColor = data.getItemVisual(dataIndex, 'color'); 58798 58799 // Exclude borderColor. 58800 var itemStyle = normalItemStyleModel.getItemStyle(['borderColor']); 58801 itemStyle.stroke = borderColor; 58802 itemStyle.strokeNoScale = true; 58803 el.useStyle(itemStyle); 58804 58805 el.z2 = 100; 58806 58807 var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH).getItemStyle(); 58808 setHoverStyle(el, hoverStyle); 58809 } 58810 58811 function transInit(points, dim, itemLayout) { 58812 return map(points, function (point) { 58813 point = point.slice(); 58814 point[dim] = itemLayout.initBaseline; 58815 return point; 58816 }); 58817 } 58818 58819 /* 58820* Licensed to the Apache Software Foundation (ASF) under one 58821* or more contributor license agreements. See the NOTICE file 58822* distributed with this work for additional information 58823* regarding copyright ownership. The ASF licenses this file 58824* to you under the Apache License, Version 2.0 (the 58825* "License"); you may not use this file except in compliance 58826* with the License. You may obtain a copy of the License at 58827* 58828* http://www.apache.org/licenses/LICENSE-2.0 58829* 58830* Unless required by applicable law or agreed to in writing, 58831* software distributed under the License is distributed on an 58832* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58833* KIND, either express or implied. See the License for the 58834* specific language governing permissions and limitations 58835* under the License. 58836*/ 58837 58838 58839 var borderColorQuery = ['itemStyle', 'borderColor']; 58840 58841 var boxplotVisual = function (ecModel, api) { 58842 58843 var globalColors = ecModel.get('color'); 58844 58845 ecModel.eachRawSeriesByType('boxplot', function (seriesModel) { 58846 58847 var defaulColor = globalColors[seriesModel.seriesIndex % globalColors.length]; 58848 var data = seriesModel.getData(); 58849 58850 data.setVisual({ 58851 legendSymbol: 'roundRect', 58852 // Use name 'color' but not 'borderColor' for legend usage and 58853 // visual coding from other component like dataRange. 58854 color: seriesModel.get(borderColorQuery) || defaulColor 58855 }); 58856 58857 // Only visible series has each data be visual encoded 58858 if (!ecModel.isSeriesFiltered(seriesModel)) { 58859 data.each(function (idx) { 58860 var itemModel = data.getItemModel(idx); 58861 data.setItemVisual( 58862 idx, 58863 {color: itemModel.get(borderColorQuery, true)} 58864 ); 58865 }); 58866 } 58867 }); 58868 58869 }; 58870 58871 /* 58872* Licensed to the Apache Software Foundation (ASF) under one 58873* or more contributor license agreements. See the NOTICE file 58874* distributed with this work for additional information 58875* regarding copyright ownership. The ASF licenses this file 58876* to you under the Apache License, Version 2.0 (the 58877* "License");
58877 you may not use this file except in compliance 58878* with the License. You may obtain a copy of the License at 58879* 58880* http://www.apache.org/licenses/LICENSE-2.0 58881* 58882* Unless required by applicable law or agreed to in writing, 58883* software distributed under the License is distributed on an 58884* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 58885* KIND, either express or implied. See the License for the 58886* specific language governing permissions and limitations 58887* under the License. 58888*/ 58889 58890 var each$13 = each$1; 58891 58892 var boxplotLayout = function (ecModel) { 58893 58894 var groupResult = groupSeriesByAxis(ecModel); 58895 58896 each$13(groupResult, function (groupItem) { 58897 var seriesModels = groupItem.seriesModels; 58898 58899 if (!seriesModels.length) { 58900 return; 58901 } 58902 58903 calculateBase(groupItem); 58904 58905 each$13(seriesModels, function (seriesModel, idx) { 58906 layoutSingleSeries( 58907 seriesModel, 58908 groupItem.boxOffsetList[idx], 58909 groupItem.boxWidthList[idx] 58910 ); 58911 }); 58912 }); 58913 }; 58914 58915 /** 58916 * Group series by axis. 58917 */ 58918 function groupSeriesByAxis(ecModel) { 58919 var result = []; 58920 var axisList = []; 58921 58922 ecModel.eachSeriesByType('boxplot', function (seriesModel) { 58923 var baseAxis = seriesModel.getBaseAxis(); 58924 var idx = indexOf(axisList, baseAxis); 58925 58926 if (idx < 0) { 58927 idx = axisList.length; 58928 axisList[idx] = baseAxis; 58929 result[idx] = {axis: baseAxis, seriesModels: []}; 58930 } 58931
vendor: 4,931 bytes, lines 58932-59072
58932 result[idx].seriesModels.push(seriesModel); 58933 }); 58934 58935 return result; 58936 } 58937 58938 /** 58939 * Calculate offset and box width for each series. 58940 */ 58941 function calculateBase(groupItem) { 58942 var extent; 58943 var baseAxis = groupItem.axis; 58944 var seriesModels = groupItem.seriesModels; 58945 var seriesCount = seriesModels.length; 58946 58947 var boxWidthList = groupItem.boxWidthList = []; 58948 var boxOffsetList = groupItem.boxOffsetList = []; 58949 var boundList = []; 58950 58951 var bandWidth; 58952 if (baseAxis.type === 'category') { 58953 bandWidth = baseAxis.getBandWidth(); 58954 } 58955 else { 58956 var maxDataCount = 0; 58957 each$13(seriesModels, function (seriesModel) { 58958 maxDataCount = Math.max(maxDataCount, seriesModel.getData().count()); 58959 }); 58960 extent = baseAxis.getExtent(), 58961 Math.abs(extent[1] - extent[0]) / maxDataCount; 58962 } 58963 58964 each$13(seriesModels, function (seriesModel) { 58965 var boxWidthBound = seriesModel.get('boxWidth'); 58966 if (!isArray(boxWidthBound)) { 58967 boxWidthBound = [boxWidthBound, boxWidthBound]; 58968 } 58969 boundList.push([ 58970 parsePercent$1(boxWidthBound[0], bandWidth) || 0, 58971 parsePercent$1(boxWidthBound[1], bandWidth) || 0 58972 ]); 58973 }); 58974 58975 var availableWidth = bandWidth * 0.8 - 2; 58976 var boxGap = availableWidth / seriesCount * 0.3; 58977 var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount; 58978 var base = boxWidth / 2 - availableWidth / 2; 58979 58980 each$13(seriesModels, function (seriesModel, idx) { 58981 boxOffsetList.push(base); 58982 base += boxGap + boxWidth; 58983 58984 boxWidthList.push( 58985 Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1]) 58986 ); 58987 }); 58988 } 58989 58990 /** 58991 * Calculate points location for each series. 58992 */ 58993 function layoutSingleSeries(seriesModel, offset, boxWidth) { 58994 var coordSys = seriesModel.coordinateSystem; 58995 var data = seriesModel.getData(); 58996 var halfWidth = boxWidth / 2; 58997 var cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1; 58998 var vDimIdx = 1 - cDimIdx; 58999 var coordDims = ['x', 'y']; 59000 var cDim = data.mapDimension(coordDims[cDimIdx]); 59001 var vDims = data.mapDimension(coordDims[vDimIdx], true); 59002 59003 if (cDim == null || vDims.length < 5) { 59004 return; 59005 } 59006 59007 for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) { 59008 var axisDimVal = data.get(cDim, dataIndex); 59009 59010 var median = getPoint(axisDimVal, vDims[2], dataIndex); 59011 var end1 = getPoint(axisDimVal, vDims[0], dataIndex); 59012 var end2 = getPoint(axisDimVal, vDims[1], dataIndex); 59013 var end4 = getPoint(axisDimVal, vDims[3], dataIndex); 59014 var end5 = getPoint(axisDimVal, vDims[4], dataIndex); 59015 59016 var ends = []; 59017 addBodyEnd(ends, end2, 0); 59018 addBodyEnd(ends, end4, 1); 59019 59020 ends.push(end1, end2, end5, end4); 59021 layEndLine(ends, end1); 59022 layEndLine(ends, end5); 59023 layEndLine(ends, median); 59024 59025 data.setItemLayout(dataIndex, { 59026 initBaseline: median[vDimIdx], 59027 ends: ends 59028 }); 59029 } 59030 59031 function getPoint(axisDimVal, dimIdx, dataIndex) { 59032 var val = data.get(dimIdx, dataIndex); 59033 var p = []; 59034 p[cDimIdx] = axisDimVal; 59035 p[vDimIdx] = val; 59036 var point; 59037 if (isNaN(axisDimVal) || isNaN(val)) { 59038 point = [NaN, NaN]; 59039 } 59040 else { 59041 point = coordSys.dataToPoint(p); 59042 point[cDimIdx] += offset; 59043 } 59044 return point; 59045 } 59046 59047 function addBodyEnd(ends, point, start) { 59048 var point1 = point.slice(); 59049 var point2 = point.slice(); 59050 point1[cDimIdx] += halfWidth; 59051 point2[cDimIdx] -= halfWidth; 59052 start 59053 ? ends.push(point1, point2) 59054 : ends.push(point2, point1); 59055 } 59056 59057 function layEndLine(ends, endCenter) { 59058 var from = endCenter.slice(); 59059 var to = endCenter.slice(); 59060 from[cDimIdx] -= halfWidth; 59061 to[cDimIdx] += halfWidth; 59062 ends.push(from, to); 59063 } 59064 } 59065 59066 /* 59067* Licensed to the Apache Software Foundation (ASF) under one 59068* or more contributor license agreements. See the NOTICE file 59069* distributed with this work for additional information 59070* regarding copyright ownership. The ASF licenses this file 59071* to you under the Apache License, Version 2.0 (the 59072* "License");
59072 you may not use this file except in compliance 59073* with the License. You may obtain a copy of the License at 59074* 59075* http://www.apache.org/licenses/LICENSE-2.0 59076* 59077* Unless required by applicable law or agreed to in writing, 59078* software distributed under the License is distributed on an 59079* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59080* KIND, either express or implied. See the License for the 59081* specific language governing permissions and limitations 59082* under the License. 59083*/ 59084 59085 registerVisual(boxplotVisual); 59086 registerLayout(boxplotLayout); 59087 59088 /* 59089* Licensed to the Apache Software Foundation (ASF) under one 59090* or more contributor license agreements. See the NOTICE file 59091* distributed with this work for additional information 59092* regarding copyright ownership. The ASF licenses this file 59093* to you under the Apache License, Version 2.0 (the 59094* "License"); you may not use this file except in compliance 59095* with the License. You may obtain a copy of the License at 59096* 59097* http://www.apache.org/licenses/LICENSE-2.0 59098* 59099* Unless required by applicable law or agreed to in writing, 59100* software distributed under the License is distributed on an 59101* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59102* KIND, either express or implied. See the License for the 59103* specific language governing permissions and limitations 59104* under the License. 59105*/ 59106 59107 var CandlestickSeries = SeriesModel.extend({ 59108 59109 type: 'series.candlestick', 59110 59111 dependencies: ['xAxis', 'yAxis', 'grid'], 59112 59113 /** 59114 * @readOnly 59115 */ 59116 defaultValueDimensions: [ 59117 {name: 'open', defaultTooltip: true}, 59118 {name: 'close', defaultTooltip: true}, 59119 {name: 'lowest', defaultTooltip: true}, 59120 {name: 'highest', defaultTooltip: true} 59121 ], 59122 59123 /** 59124 * @type {Array.<string>} 59125 * @readOnly 59126 */ 59127 dimensions: null, 59128 59129 /** 59130 * @override 59131 */ 59132 defaultOption: { 59133 zlevel: 0, 59134 z: 2, 59135 coordinateSystem: 'cartesian2d', 59136 legendHoverLink: true, 59137 59138 hoverAnimation: true, 59139 59140 // xAxisIndex: 0, 59141 // yAxisIndex: 0, 59142 59143 layout: null, // 'horizontal' or 'vertical' 59144 59145 itemStyle: { 59146 color: '#c23531', // é˳线 positive 59147 color0: '#314656', // é˴线 negative '#c23531', '#314656' 59148 borderWidth: 1, 59149 // FIXME 59150 // ec2ä¸Â使çâ¨çšâæË¯lineStyle.color Ã¥âÅ lineStyle.color0 59151 borderColor: '#c23531', 59152 borderColor0: '#314656' 59153 }, 59154 59155 emphasis: { 59156 itemStyle: { 59157 borderWidth: 2 59158 } 59159 }, 59160 59161 barMaxWidth: null, 59162 barMinWidth: null, 59163 barWidth: null, 59164 59165 large: true, 59166 largeThreshold: 600, 59167 59168 progressive: 3e3, 59169 progressiveThreshold: 1e4, 59170 progressiveChunkMode: 'mod', 59171 59172 animationUpdate: false, 59173 animationEasing: 'linear', 59174 animationDuration: 300 59175 }, 59176 59177 /** 59178 * Get dimension for shadow in dataZoom 59179 * @return {string} dimension name 59180 */ 59181 getShadowDim: function () { 59182 return 'open'; 59183 }, 59184 59185 brushSelector: function (dataIndex, data, selectors) { 59186 var itemLayout = data.getItemLayout(dataIndex); 59187 return itemLayout && selectors.rect(itemLayout.brushRect); 59188 } 59189 59190 }); 59191 59192 mixin(CandlestickSeries, seriesModelMixin, true); 59193 59194 /* 59195* Licensed to the Apache Software Foundation (ASF) under one 59196* or more contributor license agreements. See the NOTICE file 59197* distributed with this work for additional information 59198* regarding copyright ownership. The ASF licenses this file 59199* to you under the Apache License, Version 2.0 (the 59200* "License"); you may not use this file except in compliance 59201* with the License. You may obtain a copy of the License at 59202* 59203* http://www.apache.org/licenses/LICENSE-2.0 59204* 59205* Unless required by applicable law or agreed to in writing, 59206* software distributed under the License is distributed on an 59207* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59208* KIND, either express or implied. See the License for the 59209* specific language governing permissions and limitations 59210* under the License. 59211*/ 59212 59213 var NORMAL_ITEM_STYLE_PATH$1 = ['itemStyle']; 59214 var EMPHASIS_ITEM_STYLE_PATH$1 = ['emphasis', 'itemStyle']; 59215 var SKIP_PROPS = ['color', 'color0', 'borderColor', 'borderColor0']; 59216 59217 59218 var CandlestickView = Chart.extend({ 59219 59220 type: 'candlestick', 59221 59222 render: function (seriesModel, ecModel, api) { 59223 this._updateDrawMode(seriesModel); 59224 59225 this._isLargeDraw 59226 ? this._renderLarge(seriesModel) 59227 : this._renderNormal(seriesModel); 59228 }, 59229 59230 incrementalPrepareRender: function (seriesModel, ecModel, api) { 59231 this._clear(); 59232 this._updateDrawMode(seriesModel); 59233 }, 59234 59235 incrementalRender: function (params, seriesModel, ecModel, api) { 59236 this._isLargeDraw 59237 ? this._incrementalRenderLarge(params, seriesModel) 59238 : this._incrementalRenderNormal(params, seriesModel); 59239 }, 59240 59241 _updateDrawMode: function (seriesModel) { 59242 var isLargeDraw = seriesModel.pipelineContext.large; 59243 if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) { 59244 this._isLargeDraw = isLargeDraw; 59245 this._clear(); 59246 } 59247 }, 59248 59249 _renderNormal: function (seriesModel) { 59250 var data = seriesModel.getData(); 59251 var oldData = this._data; 59252 var group = this.group; 59253 var isSimpleBox = data.getLayout('isSimpleBox'); 59254 59255 // There is no old data only when first rendering or switching from 59256 // stream mode to normal mode, where previous elements should be removed. 59257 if (!this._data) { 59258 group.removeAll(); 59259 } 59260 59261 data.diff(oldData) 59262 .add(function (newIdx) { 59263 if (data.hasValue(newIdx)) { 59264 var el; 59265 59266 var itemLayout = data.getItemLayout(newIdx);
59267 el = createNormalBox$1(itemLayout, newIdx, true); 59268 initProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx); 59269 59270 setBoxCommon(el, data, newIdx, isSimpleBox); 59271 59272 group.add(el); 59273 data.setItemGraphicEl(newIdx, el); 59274 } 59275 }) 59276 .update(function (newIdx, oldIdx) { 59277 var el = oldData.getItemGraphicEl(oldIdx); 59278 59279 // Empty data 59280 if (!data.hasValue(newIdx)) { 59281 group.remove(el); 59282 return; 59283 } 59284 59285 var itemLayout = data.getItemLayout(newIdx); 59286 if (!el) { 59287 el = createNormalBox$1(itemLayout, newIdx); 59288 } 59289 else { 59290 updateProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx); 59291 } 59292 59293 setBoxCommon(el, data, newIdx, isSimpleBox); 59294 59295 group.add(el); 59296 data.setItemGraphicEl(newIdx, el); 59297 }) 59298 .remove(function (oldIdx) { 59299 var el = oldData.getItemGraphicEl(oldIdx); 59300 el && group.remove(el); 59301 }) 59302 .execute(); 59303 59304 this._data = data; 59305 }, 59306 59307 _renderLarge: function (seriesModel) { 59308 this._clear();
59309 createLarge$1(seriesModel, this.group); 59310 }, 59311 59312 _incrementalRenderNormal: function (params, seriesModel) { 59313 var data = seriesModel.getData(); 59314 var isSimpleBox = data.getLayout('isSimpleBox'); 59315 59316 var dataIndex; 59317 while ((dataIndex = params.next()) != null) { 59318 var el; 59319 59320 var itemLayout = data.getItemLayout(dataIndex); 59321 el = createNormalBox$1(itemLayout, dataIndex); 59322 setBoxCommon(el, data, dataIndex, isSimpleBox); 59323 59324 el.incremental = true; 59325 this.group.add(el); 59326 } 59327 }, 59328 59329 _incrementalRenderLarge: function (params, seriesModel) { 59330 createLarge$1(seriesModel, this.group, true); 59331 }, 59332 59333 remove: function (ecModel) { 59334 this._clear(); 59335 }, 59336 59337 _clear: function () { 59338 this.group.removeAll(); 59339 this._data = null; 59340 }, 59341 59342 dispose: noop 59343 59344 }); 59345 59346 59347 var NormalBoxPath = Path.extend({ 59348 59349 type: 'normalCandlestickBox', 59350 59351 shape: {}, 59352 59353 buildPath: function (ctx, shape) { 59354 var ends = shape.points; 59355 59356 if (this.__simpleBox) { 59357 ctx.moveTo(ends[4][0], ends[4][1]); 59358 ctx.lineTo(ends[6][0], ends[6][1]); 59359 } 59360 else { 59361 ctx.moveTo(ends[0][0], ends[0][1]); 59362 ctx.lineTo(ends[1][0], ends[1][1]); 59363 ctx.lineTo(ends[2][0], ends[2][1]); 59364 ctx.lineTo(ends[3][0], ends[3][1]); 59365 ctx.closePath(); 59366 59367 ctx.moveTo(ends[4][0], ends[4][1]); 59368 ctx.lineTo(ends[5][0], ends[5][1]); 59369 ctx.moveTo(ends[6][0], ends[6][1]); 59370 ctx.lineTo(ends[7][0], ends[7][1]); 59371 } 59372 } 59373 }); 59374 59375 function createNormalBox$1(itemLayout, dataIndex, isInit) { 59376 var ends = itemLayout.ends; 59377 return new NormalBoxPath({ 59378 shape: { 59379 points: isInit 59380 ? transInit$1(ends, itemLayout) 59381 : ends 59382 }, 59383 z2: 100 59384 }); 59385 } 59386 59387 function setBoxCommon(el, data, dataIndex, isSimpleBox) { 59388 var itemModel = data.getItemModel(dataIndex); 59389 var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH$1); 59390 var color = data.getItemVisual(dataIndex, 'color'); 59391 var borderColor = data.getItemVisual(dataIndex, 'borderColor') || color; 59392 59393 // Color must be excluded. 59394 // Because symbol provide setColor individually to set fill and stroke 59395 var itemStyle = normalItemStyleModel.getItemStyle(SKIP_PROPS); 59396 59397 el.useStyle(itemStyle); 59398 el.style.strokeNoScale = true; 59399 el.style.fill = color; 59400 el.style.stroke = borderColor; 59401 59402 el.__simpleBox = isSimpleBox; 59403 59404 var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH$1).getItemStyle(); 59405 setHoverStyle(el, hoverStyle); 59406 } 59407 59408 function transInit$1(points, itemLayout) { 59409 return map(points, function (point) { 59410 point = point.slice(); 59411 point[1] = itemLayout.initBaseline; 59412 return point; 59413 }); 59414 } 59415 59416 59417 59418 var LargeBoxPath = Path.extend({ 59419 59420 type: 'largeCandlestickBox', 59421 59422 shape: {}, 59423 59424 buildPath: function (ctx, shape) { 59425 // Drawing lines is more efficient than drawing 59426 // a whole line or drawing rects. 59427 var points = shape.points; 59428 for (var i = 0; i < points.length;) { 59429 if (this.__sign === points[i++]) { 59430 var x = points[i++]; 59431 ctx.moveTo(x, points[i++]); 59432 ctx.lineTo(x, points[i++]); 59433 } 59434 else { 59435 i += 3; 59436 } 59437 } 59438 } 59439 }); 59440
59441 function createLarge$1(seriesModel, group, incremental) { 59442 var data = seriesModel.getData(); 59443 var largePoints = data.getLayout('largePoints'); 59444 59445 var elP = new LargeBoxPath({ 59446 shape: {points: largePoints}, 59447 __sign: 1 59448 }); 59449 group.add(elP); 59450 var elN = new LargeBoxPath({ 59451 shape: {points: largePoints}, 59452 __sign: -1 59453 }); 59454 group.add(elN); 59455 59456 setLargeStyle$1(1, elP, seriesModel, data); 59457 setLargeStyle$1(-1, elN, seriesModel, data); 59458 59459 if (incremental) { 59460 elP.incremental = true; 59461 elN.incremental = true; 59462 } 59463 } 59464 59465 function setLargeStyle$1(sign, el, seriesModel, data) { 59466 var suffix = sign > 0 ? 'P' : 'N'; 59467 var borderColor = data.getVisual('borderColor' + suffix) 59468 || data.getVisual('color' + suffix); 59469 59470 // Color must be excluded. 59471 // Because symbol provide setColor individually to set fill and stroke 59472 var itemStyle = seriesModel.getModel(NORMAL_ITEM_STYLE_PATH$1).getItemStyle(SKIP_PROPS); 59473 59474 el.useStyle(itemStyle); 59475 el.style.fill = null; 59476 el.style.stroke = borderColor; 59477 // No different 59478 // el.style.lineWidth = .5; 59479 } 59480 59481 /* 59482* Licensed to the Apache Software Foundation (ASF) under one 59483* or more contributor license agreements. See the NOTICE file 59484* distributed with this work for additional information 59485* regarding copyright ownership. The ASF licenses this file 59486* to you under the Apache License, Version 2.0 (the 59487* "License"); you may not use this file except in compliance 59488* with the License. You may obtain a copy of the License at 59489* 59490* http://www.apache.org/licenses/LICENSE-2.0 59491* 59492* Unless required by applicable law or agreed to in writing, 59493* software distributed under the License is distributed on an 59494* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59495* KIND, either express or implied. See the License for the 59496* specific language governing permissions and limitations 59497* under the License. 59498*/ 59499 59500 var preprocessor = function (option) { 59501 if (!option || !isArray(option.series)) { 59502 return; 59503 } 59504 59505 // Translate 'k' to 'candlestick'. 59506 each$1(option.series, function (seriesItem) { 59507 if (isObject$1(seriesItem) && seriesItem.type === 'k') { 59508 seriesItem.type = 'candlestick'; 59509 } 59510 }); 59511 }; 59512 59513 /* 59514* Licensed to the Apache Software Foundation (ASF) under one 59515* or more contributor license agreements. See the NOTICE file 59516* distributed with this work for additional information 59517* regarding copyright ownership. The ASF licenses this file 59518* to you under the Apache License, Version 2.0 (the 59519* "License"); you may not use this file except in compliance 59520* with the License. You may obtain a copy of the License at 59521* 59522* http://www.apache.org/licenses/LICENSE-2.0 59523* 59524* Unless required by applicable law or agreed to in writing, 59525* software distributed under the License is distributed on an 59526* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59527* KIND, either express or implied. See the License for the 59528* specific language governing permissions and limitations 59529* under the License. 59530*/ 59531 59532 var positiveBorderColorQuery = ['itemStyle', 'borderColor']; 59533 var negativeBorderColorQuery = ['itemStyle', 'borderColor0']; 59534 var positiveColorQuery = ['itemStyle', 'color']; 59535 var negativeColorQuery = ['itemStyle', 'color0']; 59536 59537 var candlestickVisual = { 59538 59539 seriesType: 'candlestick', 59540 59541 plan: createRenderPlanner(), 59542 59543 // For legend. 59544 performRawSeries: true, 59545 59546 reset: function (seriesModel, ecModel) { 59547 59548 var data = seriesModel.getData(); 59549 var isLargeRender = seriesModel.pipelineContext.large; 59550 59551 data.setVisual({ 59552 legendSymbol: 'roundRect', 59553 colorP: getColor(1, seriesModel), 59554 colorN: getColor(-1, seriesModel), 59555 borderColorP: getBorderColor(1, seriesModel), 59556 borderColorN: getBorderColor(-1, seriesModel) 59557 }); 59558 59559 // Only visible series has each data be visual encoded
59560 if (ecModel.isSeriesFiltered(seriesModel)) { 59561 return; 59562 } 59563 59564 return !isLargeRender && {progress: progress}; 59565 59566 59567 function progress(params, data) { 59568 var dataIndex; 59569 while ((dataIndex = params.next()) != null) { 59570 var itemModel = data.getItemModel(dataIndex); 59571 var sign = data.getItemLayout(dataIndex).sign; 59572 59573 data.setItemVisual( 59574 dataIndex, 59575 { 59576 color: getColor(sign, itemModel), 59577 borderColor: getBorderColor(sign, itemModel) 59578 } 59579 ); 59580 } 59581 } 59582 59583 function getColor(sign, model) { 59584 return model.get( 59585 sign > 0 ? positiveColorQuery : negativeColorQuery 59586 ); 59587 } 59588 59589 function getBorderColor(sign, model) { 59590 return model.get( 59591 sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery 59592 ); 59593 } 59594 59595 } 59596 59597 }; 59598 59599 /* 59600* Licensed to the Apache Software Foundation (ASF) under one 59601* or more contributor license agreements. See the NOTICE file 59602* distributed with this work for additional information 59603* regarding copyright ownership. The ASF licenses this file 59604* to you under the Apache License, Version 2.0 (the 59605* "License"); you may not use this file except in compliance 59606* with the License. You may obtain a copy of the License at 59607* 59608* http://www.apache.org/licenses/LICENSE-2.0 59609* 59610* Unless required by applicable law or agreed to in writing, 59611* software distributed under the License is distributed on an 59612* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59613* KIND, either express or implied. See the License for the 59614* specific language governing permissions and limitations 59615* under the License. 59616*/ 59617 59618 var LargeArr$1 = typeof Float32Array !== 'undefined' ? Float32Array : Array; 59619 59620 var candlestickLayout = { 59621 59622 seriesType: 'candlestick', 59623 59624 plan: createRenderPlanner(), 59625 59626 reset: function (seriesModel) { 59627 59628 var coordSys = seriesModel.coordinateSystem; 59629 var data = seriesModel.getData(); 59630 var candleWidth = calculateCandleWidth(seriesModel, data); 59631 var cDimIdx = 0; 59632 var vDimIdx = 1; 59633 var coordDims = ['x', 'y']; 59634 var cDim = data.mapDimension(coordDims[cDimIdx]); 59635 var vDims = data.mapDimension(coordDims[vDimIdx], true); 59636 var openDim = vDims[0]; 59637 var closeDim = vDims[1]; 59638 var lowestDim = vDims[2]; 59639 var highestDim = vDims[3]; 59640 59641 data.setLayout({ 59642 candleWidth: candleWidth, 59643 // The value is experimented visually. 59644 isSimpleBox: candleWidth <= 1.3 59645 }); 59646 59647 if (cDim == null || vDims.length < 4) { 59648 return; 59649 } 59650 59651 return { 59652 progress: seriesModel.pipelineContext.large 59653 ? largeProgress : normalProgress 59654 }; 59655 59656 function normalProgress(params, data) { 59657 var dataIndex; 59658 while ((dataIndex = params.next()) != null) { 59659 59660 var axisDimVal = data.get(cDim, dataIndex); 59661 var openVal = data.get(openDim, dataIndex); 59662 var closeVal = data.get(closeDim, dataIndex); 59663 var lowestVal = data.get(lowestDim, dataIndex); 59664 var highestVal = data.get(highestDim, dataIndex); 59665 59666 var ocLow = Math.min(openVal, closeVal); 59667 var ocHigh = Math.max(openVal, closeVal); 59668 59669 var ocLowPoint = getPoint(ocLow, axisDimVal); 59670 var ocHighPoint = getPoint(ocHigh, axisDimVal); 59671 var lowestPoint = getPoint(lowestVal, axisDimVal); 59672 var highestPoint = getPoint(highestVal, axisDimVal); 59673 59674 var ends = []; 59675 addBodyEnd(ends, ocHighPoint, 0); 59676 addBodyEnd(ends, ocLowPoint, 1); 59677 59678 ends.push( 59679 subPixelOptimizePoint(highestPoint), 59680 subPixelOptimizePoint(ocHighPoint), 59681 subPixelOptimizePoint(lowestPoint), 59682 subPixelOptimizePoint(ocLowPoint) 59683 ); 59684 59685 data.setItemLayout(dataIndex, { 59686 sign: getSign(data, dataIndex, openVal, closeVal, closeDim), 59687 initBaseline: openVal > closeVal 59688 ? ocHighPoint[vDimIdx] : ocLowPoint[vDimIdx], // open point. 59689 ends: ends, 59690 brushRect: makeBrushRect(lowestVal, highestVal, axisDimVal) 59691 }); 59692 } 59693 59694 function getPoint(val, axisDimVal) { 59695 var p = []; 59696 p[cDimIdx] = axisDimVal; 59697 p[vDimIdx] = val; 59698 return (isNaN(axisDimVal) || isNaN(val)) 59699 ? [NaN, NaN] 59700 : coordSys.dataToPoint(p); 59701 } 59702 59703 function addBodyEnd(ends, point, start) { 59704 var point1 = point.slice(); 59705 var point2 = point.slice(); 59706 59707 point1[cDimIdx] = subPixelOptimize( 59708 point1[cDimIdx] + candleWidth / 2, 1, false 59709 ); 59710 point2[cDimIdx] = subPixelOptimize( 59711 point2[cDimIdx] - candleWidth / 2, 1, true 59712 ); 59713 59714 start 59715 ? ends.push(point1, point2) 59716 : ends.push(point2, point1); 59717 } 59718 59719 function makeBrushRect(lowestVal, highestVal, axisDimVal) { 59720 var pmin = getPoint(lowestVal, axisDimVal); 59721 var pmax = getPoint(highestVal, axisDimVal); 59722 59723 pmin[cDimIdx] -= candleWidth / 2; 59724 pmax[cDimIdx] -= candleWidth / 2; 59725 59726 return { 59727 x: pmin[0], 59728 y: pmin[1], 59729 width: vDimIdx ? candleWidth : pmax[0] - pmin[0], 59730 height: vDimIdx ? pmax[1] - pmin[1] : candleWidth 59731 }; 59732 } 59733 59734 function subPixelOptimizePoint(point) { 59735 point[cDimIdx] = subPixelOptimize(point[cDimIdx], 1); 59736 return point; 59737 } 59738 } 59739 59740 function largeProgress(params, data) { 59741 // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]
59742 var points = new LargeArr$1(params.count * 5); 59743 var offset = 0; 59744 var point; 59745 var tmpIn = []; 59746 var tmpOut = []; 59747 var dataIndex; 59748 59749 while ((dataIndex = params.next()) != null) { 59750 var axisDimVal = data.get(cDim, dataIndex); 59751 var openVal = data.get(openDim, dataIndex); 59752 var closeVal = data.get(closeDim, dataIndex); 59753 var lowestVal = data.get(lowestDim, dataIndex); 59754 var highestVal = data.get(highestDim, dataIndex); 59755 59756 if (isNaN(axisDimVal) || isNaN(lowestVal) || isNaN(highestVal)) { 59757 points[offset++] = NaN; 59758 offset += 4; 59759 continue; 59760 } 59761 59762 points[offset++] = getSign(data, dataIndex, openVal, closeVal, closeDim); 59763 59764 tmpIn[cDimIdx] = axisDimVal; 59765 59766 tmpIn[vDimIdx] = lowestVal; 59767 point = coordSys.dataToPoint(tmpIn, null, tmpOut); 59768 points[offset++] = point ? point[0] : NaN; 59769 points[offset++] = point ? point[1] : NaN; 59770 tmpIn[vDimIdx] = highestVal; 59771 point = coordSys.dataToPoint(tmpIn, null, tmpOut); 59772 points[offset++] = point ? point[1] : NaN; 59773 } 59774 59775 data.setLayout('largePoints', points); 59776 } 59777 } 59778 }; 59779 59780 function getSign(data, dataIndex, openVal, closeVal, closeDim) { 59781 var sign; 59782 if (openVal > closeVal) { 59783 sign = -1; 59784 } 59785 else if (openVal < closeVal) { 59786 sign = 1; 59787 } 59788 else { 59789 sign = dataIndex > 0 59790 // If close === open, compare with close of last record 59791 ? (data.get(closeDim, dataIndex - 1) <= closeVal ? 1 : -1) 59792 // No record of previous, set to be positive 59793 : 1; 59794 } 59795 59796 return sign; 59797 } 59798 59799 function calculateCandleWidth(seriesModel, data) { 59800 var baseAxis = seriesModel.getBaseAxis(); 59801 var extent; 59802 59803 var bandWidth = baseAxis.type === 'category' 59804 ? baseAxis.getBandWidth() 59805 : ( 59806 extent = baseAxis.getExtent(), 59807 Math.abs(extent[1] - extent[0]) / data.count() 59808 ); 59809 59810 var barMaxWidth = parsePercent$1( 59811 retrieve2(seriesModel.get('barMaxWidth'), bandWidth), 59812 bandWidth 59813 ); 59814 var barMinWidth = parsePercent$1( 59815 retrieve2(seriesModel.get('barMinWidth'), 1), 59816 bandWidth 59817 ); 59818 var barWidth = seriesModel.get('barWidth'); 59819 59820 return barWidth != null 59821 ? parsePercent$1(barWidth, bandWidth) 59822 // Put max outer to ensure bar visible in spite of overlap. 59823 : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth); 59824 } 59825 59826 /* 59827* Licensed to the Apache Software Foundation (ASF) under one 59828* or more contributor license agreements. See the NOTICE file 59829* distributed with this work for additional information 59830* regarding copyright ownership. The ASF licenses this file 59831* to you under the Apache License, Version 2.0 (the 59832* "License"); you may not use this file except in compliance 59833* with the License. You may obtain a copy of the License at 59834* 59835* http://www.apache.org/licenses/LICENSE-2.0 59836* 59837* Unless required by applicable law or agreed to in writing, 59838* software distributed under the License is distributed on an 59839* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59840* KIND, either express or implied. See the License for the 59841* specific language governing permissions and limitations 59842* under the License. 59843*/ 59844 59845 registerPreprocessor(preprocessor); 59846 registerVisual(candlestickVisual); 59847 registerLayout(candlestickLayout); 59848 59849 /* 59850* Licensed to the Apache Software Foundation (ASF) under one 59851* or more contributor license agreements. See the NOTICE file 59852* distributed with this work for additional information 59853* regarding copyright ownership. The ASF licenses this file 59854* to you under the Apache License, Version 2.0 (the 59855* "License");
59855 you may not use this file except in compliance 59856* with the License. You may obtain a copy of the License at 59857* 59858* http://www.apache.org/licenses/LICENSE-2.0 59859* 59860* Unless required by applicable law or agreed to in writing, 59861* software distributed under the License is distributed on an 59862* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59863* KIND, either express or implied. See the License for the 59864* specific language governing permissions and limitations 59865* under the License. 59866*/ 59867 59868 SeriesModel.extend({ 59869 59870 type: 'series.effectScatter', 59871 59872 dependencies: ['grid', 'polar'], 59873 59874 getInitialData: function (option, ecModel) { 59875 return createListFromArray(this.getSource(), this); 59876 }, 59877 59878 brushSelector: 'point', 59879 59880 defaultOption: { 59881 coordinateSystem: 'cartesian2d', 59882 zlevel: 0, 59883 z: 2, 59884 legendHoverLink: true, 59885 59886 effectType: 'ripple', 59887 59888 progressive: 0, 59889 59890 // When to show the effect, option: 'render'|'emphasis' 59891 showEffectOn: 'render', 59892 59893 // Ripple effect config 59894 rippleEffect: { 59895 period: 4, 59896 // Scale of ripple 59897 scale: 2.5, 59898 // Brush type can be fill or stroke 59899 brushType: 'fill' 59900 }, 59901 59902 // Cartesian coordinate system 59903 // xAxisIndex: 0, 59904 // yAxisIndex: 0, 59905 59906 // Polar coordinate system 59907 // polarIndex: 0, 59908 59909 // Geo coordinate system 59910 // geoIndex: 0, 59911 59912 // symbol: null, // Ã¥âºÂ¾Ã¥Â½Â¢Ã§Â±Â»Ã¥Å¾â¹ 59913 symbolSize: 10 // Ã¥âºÂ¾Ã¥Â½Â¢Ã¥Â¤Â§Ã¥Â°Âï¼ÅåŠ宽ï¼ËåŠå¾âï¼â°Ã¥Ââæâ¢Â°Ã¯Â¼Åå½âÃ¥âºÂ¾Ã¥Â½Â¢Ã¤Â¸ÂºÃ¦â¹åÂâæËâè±形åËâ¢Ã¦â¬Â»Ã¥Â®Â½Ã¥ÂºÂ¦Ã¤Â¸ÂºsymbolSize * 2 59914 // symbolRotate: null, // Ã¥âºÂ¾Ã¥Â½Â¢Ã¦ââ¹Ã¨Â½Â¬Ã¦Å½Â§Ã¥Ë¶ 59915 59916 // large: false, 59917 // Available when large is true 59918 // largeThreshold: 2000, 59919 59920 // itemStyle: { 59921 // opacity: 1 59922 // } 59923 } 59924 59925 }); 59926 59927 /* 59928* Licensed to the Apache Software Foundation (ASF) under one 59929* or more contributor license agreements. See the NOTICE file 59930* distributed with this work for additional information 59931* regarding copyright ownership. The ASF licenses this file 59932* to you under the Apache License, Version 2.0 (the 59933* "License"); you may not use this file except in compliance 59934* with the License. You may obtain a copy of the License at 59935* 59936* http://www.apache.org/licenses/LICENSE-2.0 59937* 59938* Unless required by applicable law or agreed to in writing, 59939* software distributed under the License is distributed on an 59940* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 59941* KIND, either express or implied. See the License for the 59942* specific language governing permissions and limitations 59943* under the License. 59944*/ 59945 59946 /** 59947 * Symbol with ripple effect 59948 * @module echarts/chart/helper/EffectSymbol 59949 */ 59950 59951 var EFFECT_RIPPLE_NUMBER = 3; 59952 59953 function normalizeSymbolSize$1(symbolSize) { 59954 if (!isArray(symbolSize)) { 59955 symbolSize = [+symbolSize, +symbolSize]; 59956 } 59957 return symbolSize; 59958 } 59959 59960 function updateRipplePath(rippleGroup, effectCfg) { 59961 rippleGroup.eachChild(function (ripplePath) { 59962 ripplePath.attr({ 59963 z: effectCfg.z, 59964 zlevel: effectCfg.zlevel, 59965 style: { 59966 stroke: effectCfg.brushType === 'stroke' ? effectCfg.color : null, 59967 fill: effectCfg.brushType === 'fill' ? effectCfg.color : null 59968 } 59969 }); 59970 }); 59971 } 59972 /** 59973 * @constructor 59974 * @param {module:echarts/data/List} data 59975 * @param {number} idx 59976 * @extends {module:zrender/graphic/Group} 59977 */ 59978 function EffectSymbol(data, idx) { 59979 Group.call(this); 59980 59981 var symbol = new SymbolClz$1(data, idx); 59982 var rippleGroup = new Group(); 59983 this.add(symbol); 59984 this.add(rippleGroup); 59985 59986 rippleGroup.beforeUpdate = function () { 59987 this.attr(symbol.getScale()); 59988 }; 59989 this.updateData(data, idx); 59990 } 59991 59992 var effectSymbolProto = EffectSymbol.prototype; 59993 59994 effectSymbolProto.stopEffectAnimation = function () { 59995 this.childAt(1).removeAll(); 59996 }; 59997 59998 effectSymbolProto.startEffectAnimation = function (effectCfg) { 59999 var symbolType = effectCfg.symbolType; 60000 var color = effectCfg.color; 60001 var rippleGroup = this.childAt(1); 60002 60003 for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) { 60004 // var ripplePath = createSymbol( 60005 // symbolType, -0.5, -0.5, 1, 1, color 60006 // ); 60007 // If width/height are set too small (e.g., set to 1) on ios10
60008 // and macOS Sierra, a circle stroke become a rect, no matter what 60009 // the scale is set. So we set width/height as 2. See #4136. 60010 var ripplePath = createSymbol( 60011 symbolType, -1, -1, 2, 2, color 60012 ); 60013 ripplePath.attr({ 60014 style: { 60015 strokeNoScale: true 60016 }, 60017 z2: 99, 60018 silent: true, 60019 scale: [0.5, 0.5] 60020 }); 60021 60022 var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset; 60023 // TODO Configurable effectCfg.period 60024 ripplePath.animate('', true) 60025 .when(effectCfg.period, { 60026 scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2] 60027 }) 60028 .delay(delay) 60029 .start(); 60030 ripplePath.animateStyle(true) 60031 .when(effectCfg.period, { 60032 opacity: 0 60033 }) 60034 .delay(delay) 60035 .start(); 60036 60037 rippleGroup.add(ripplePath); 60038 } 60039 60040 updateRipplePath(rippleGroup, effectCfg); 60041 }; 60042 60043 /** 60044 * Update effect symbol 60045 */ 60046 effectSymbolProto.updateEffectAnimation = function (effectCfg) { 60047 var oldEffectCfg = this._effectCfg; 60048 var rippleGroup = this.childAt(1); 60049 60050 // Must reinitialize effect if following configuration changed 60051 var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale']; 60052 for (var i = 0; i < DIFFICULT_PROPS.length; i++) { 60053 var propName = DIFFICULT_PROPS[i]; 60054 if (oldEffectCfg[propName] !== effectCfg[propName]) { 60055 this.stopEffectAnimation(); 60056 this.startEffectAnimation(effectCfg); 60057 return; 60058 } 60059 } 60060 60061 updateRipplePath(rippleGroup, effectCfg); 60062 }; 60063 60064 /** 60065 * Highlight symbol 60066 */ 60067 effectSymbolProto.highlight = function () { 60068 this.trigger('emphasis'); 60069 }; 60070 60071 /** 60072 * Downplay symbol 60073 */ 60074 effectSymbolProto.downplay = function () { 60075 this.trigger('normal'); 60076 }; 60077 60078 /** 60079 * Update symbol properties 60080 * @param {module:echarts/data/List} data 60081 * @param {number} idx 60082 */ 60083 effectSymbolProto.updateData = function (data, idx) { 60084 var seriesModel = data.hostModel; 60085 60086 this.childAt(0).updateData(data, idx); 60087 60088 var rippleGroup = this.childAt(1); 60089 var itemModel = data.getItemModel(idx); 60090 var symbolType = data.getItemVisual(idx, 'symbol'); 60091 var symbolSize = normalizeSymbolSize$1(data.getItemVisual(idx, 'symbolSize')); 60092 var color = data.getItemVisual(idx, 'color'); 60093 60094 rippleGroup.attr('scale', symbolSize); 60095 60096 rippleGroup.traverse(function (ripplePath) { 60097 ripplePath.attr({ 60098 fill: color 60099 }); 60100 }); 60101 60102 var symbolOffset = itemModel.getShallow('symbolOffset'); 60103 if (symbolOffset) { 60104 var pos = rippleGroup.position; 60105 pos[0] = parsePercent$1(symbolOffset[0], symbolSize[0]); 60106 pos[1] = parsePercent$1(symbolOffset[1], symbolSize[1]); 60107 } 60108 rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0; 60109 60110 var effectCfg = {}; 60111 60112 effectCfg.showEffectOn = seriesModel.get('showEffectOn');
60113 effectCfg.rippleScale = itemModel.get('rippleEffect.scale'); 60114 effectCfg.brushType = itemModel.get('rippleEffect.brushType'); 60115 effectCfg.period = itemModel.get('rippleEffect.period') * 1000; 60116 effectCfg.effectOffset = idx / data.count(); 60117 effectCfg.z = itemModel.getShallow('z') || 0; 60118 effectCfg.zlevel = itemModel.getShallow('zlevel') || 0; 60119 effectCfg.symbolType = symbolType; 60120 effectCfg.color = color; 60121 60122 this.off('mouseover').off('mouseout').off('emphasis').off('normal'); 60123 60124 if (effectCfg.showEffectOn === 'render') { 60125 this._effectCfg 60126 ? this.updateEffectAnimation(effectCfg) 60127 : this.startEffectAnimation(effectCfg); 60128 60129 this._effectCfg = effectCfg; 60130 } 60131 else { 60132 // Not keep old effect config 60133 this._effectCfg = null; 60134 60135 this.stopEffectAnimation(); 60136 var symbol = this.childAt(0); 60137 var onEmphasis = function () { 60138 symbol.highlight(); 60139 if (effectCfg.showEffectOn !== 'render') { 60140 this.startEffectAnimation(effectCfg); 60141 } 60142 }; 60143 var onNormal = function () { 60144 symbol.downplay(); 60145 if (effectCfg.showEffectOn !== 'render') { 60146 this.stopEffectAnimation(); 60147 } 60148 }; 60149 this.on('mouseover', onEmphasis, this) 60150 .on('mouseout', onNormal, this) 60151 .on('emphasis', onEmphasis, this) 60152 .on('normal', onNormal, this); 60153 } 60154 60155 this._effectCfg = effectCfg; 60156 }; 60157 60158 effectSymbolProto.fadeOut = function (cb) { 60159 this.off('mouseover').off('mouseout').off('emphasis').off('normal'); 60160 cb && cb(); 60161 }; 60162 60163 inherits(EffectSymbol, Group); 60164 60165 /* 60166* Licensed to the Apache Software Foundation (ASF) under one 60167* or more contributor license agreements. See the NOTICE file 60168* distributed with this work for additional information 60169* regarding copyright ownership. The ASF licenses this file 60170* to you under the Apache License, Version 2.0 (the 60171* "License"); you may not use this file except in compliance 60172* with the License. You may obtain a copy of the License at 60173* 60174* http://www.apache.org/licenses/LICENSE-2.0 60175* 60176* Unless required by applicable law or agreed to in writing, 60177* software distributed under the License is distributed on an 60178* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60179* KIND, either express or implied. See the License for the 60180* specific language governing permissions and limitations 60181* under the License. 60182*/ 60183 60184 extendChartView({ 60185 60186 type: 'effectScatter', 60187 60188 init: function () { 60189 this._symbolDraw = new SymbolDraw(EffectSymbol); 60190 }, 60191 60192 render: function (seriesModel, ecModel, api) { 60193 var data = seriesModel.getData(); 60194 var effectSymbolDraw = this._symbolDraw; 60195 effectSymbolDraw.updateData(data); 60196 this.group.add(effectSymbolDraw.group); 60197 }, 60198 60199 updateTransform: function (seriesModel, ecModel, api) { 60200 var data = seriesModel.getData(); 60201 60202 this.group.dirty(); 60203 60204 var res = pointsLayout().reset(seriesModel); 60205 if (res.progress) { 60206 res.progress({ start: 0, end: data.count() }, data); 60207 } 60208 60209 this._symbolDraw.updateLayout(data); 60210 }, 60211 60212 _updateGroupTransform: function (seriesModel) { 60213 var coordSys = seriesModel.coordinateSystem; 60214 if (coordSys && coordSys.getRoamTransform) { 60215 this.group.transform = clone$2(coordSys.getRoamTransform()); 60216 this.group.decomposeTransform(); 60217 } 60218 }, 60219 60220 remove: function (ecModel, api) { 60221 this._symbolDraw && this._symbolDraw.remove(api); 60222 }, 60223 60224 dispose: function () {} 60225 }); 60226 60227 /* 60228* Licensed to the Apache Software Foundation (ASF) under one 60229* or more contributor license agreements. See the NOTICE file 60230* distributed with this work for additional information 60231* regarding copyright ownership. The ASF licenses this file 60232* to you under the Apache License, Version 2.0 (the 60233* "License");
60233 you may not use this file except in compliance 60234* with the License. You may obtain a copy of the License at 60235* 60236* http://www.apache.org/licenses/LICENSE-2.0 60237* 60238* Unless required by applicable law or agreed to in writing, 60239* software distributed under the License is distributed on an 60240* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60241* KIND, either express or implied. See the License for the 60242* specific language governing permissions and limitations 60243* under the License. 60244*/ 60245 60246 registerVisual(visualSymbol('effectScatter', 'circle')); 60247 registerLayout(pointsLayout('effectScatter')); 60248 60249 /* 60250* Licensed to the Apache Software Foundation (ASF) under one 60251* or more contributor license agreements. See the NOTICE file 60252* distributed with this work for additional information 60253* regarding copyright ownership. The ASF licenses this file 60254* to you under the Apache License, Version 2.0 (the 60255* "License"); you may not use this file except in compliance 60256* with the License. You may obtain a copy of the License at 60257* 60258* http://www.apache.org/licenses/LICENSE-2.0 60259* 60260* Unless required by applicable law or agreed to in writing, 60261* software distributed under the License is distributed on an 60262* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60263* KIND, either express or implied. See the License for the 60264* specific language governing permissions and limitations 60265* under the License. 60266*/ 60267 60268 var Uint32Arr = typeof Uint32Array === 'undefined' ? Array : Uint32Array; 60269 var Float64Arr = typeof Float64Array === 'undefined' ? Array : Float64Array; 60270 60271 function compatEc2(seriesOpt) { 60272 var data = seriesOpt.data; 60273 if (data && data[0] && data[0][0] && data[0][0].coord) { 60274 if (__DEV__) { 60275 console.warn('Lines data configuration has been changed to' 60276 + ' { coords:[[1,2],[2,3]] }'); 60277 } 60278 seriesOpt.data = map(data, function (itemOpt) { 60279 var coords = [ 60280 itemOpt[0].coord, itemOpt[1].coord 60281 ]; 60282 var target = { 60283 coords: coords 60284 }; 60285 if (itemOpt[0].name) { 60286 target.fromName = itemOpt[0].name; 60287 } 60288 if (itemOpt[1].name) { 60289 target.toName = itemOpt[1].name; 60290 } 60291 return mergeAll([target, itemOpt[0], itemOpt[1]]); 60292 }); 60293 } 60294 } 60295 60296 var LinesSeries = SeriesModel.extend({ 60297 60298 type: 'series.lines', 60299 60300 dependencies: ['grid', 'polar'], 60301 60302 visualColorAccessPath: 'lineStyle.color', 60303 60304 init: function (option) { 60305 // The input data may be null/undefined. 60306 option.data = option.data || []; 60307 60308 // Not using preprocessor because mergeOption may not have series.type 60309 compatEc2(option); 60310 60311 var result = this._processFlatCoordsArray(option.data); 60312 this._flatCoords = result.flatCoords; 60313 this._flatCoordsOffset = result.flatCoordsOffset; 60314 if (result.flatCoords) { 60315 option.data = new Float32Array(result.count); 60316 } 60317 60318 LinesSeries.superApply(this, 'init', arguments); 60319 }, 60320 60321 mergeOption: function (option) { 60322 // The input data may be null/undefined. 60323 option.data = option.data || []; 60324 60325 compatEc2(option); 60326 60327 if (option.data) { 60328 // Only update when have option data to merge. 60329 var result = this._processFlatCoordsArray(option.data); 60330 this._flatCoords = result.flatCoords; 60331 this._flatCoordsOffset = result.flatCoordsOffset; 60332 if (result.flatCoords) { 60333 option.data = new Float32Array(result.count); 60334 } 60335 } 60336 60337 LinesSeries.superApply(this, 'mergeOption', arguments); 60338 }, 60339 60340 appendData: function (params) { 60341 var result = this._processFlatCoordsArray(params.data); 60342 if (result.flatCoords) { 60343 if (!this._flatCoords) { 60344 this._flatCoords = result.flatCoords; 60345 this._flatCoordsOffset = result.flatCoordsOffset; 60346 } 60347 else { 60348 this._flatCoords = concatArray(this._flatCoords, result.flatCoords);
60349 this._flatCoordsOffset = concatArray(this._flatCoordsOffset, result.flatCoordsOffset); 60350 } 60351 params.data = new Float32Array(result.count); 60352 } 60353 60354 this.getRawData().appendData(params.data); 60355 }, 60356 60357 _getCoordsFromItemModel: function (idx) { 60358 var itemModel = this.getData().getItemModel(idx); 60359 var coords = (itemModel.option instanceof Array) 60360 ? itemModel.option : itemModel.getShallow('coords'); 60361 60362 if (__DEV__) { 60363 if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) { 60364 throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.'); 60365 } 60366 } 60367 return coords; 60368 }, 60369 60370 getLineCoordsCount: function (idx) { 60371 if (this._flatCoordsOffset) { 60372 return this._flatCoordsOffset[idx * 2 + 1]; 60373 } 60374 else { 60375 return this._getCoordsFromItemModel(idx).length; 60376 } 60377 }, 60378 60379 getLineCoords: function (idx, out) { 60380 if (this._flatCoordsOffset) { 60381 var offset = this._flatCoordsOffset[idx * 2]; 60382 var len = this._flatCoordsOffset[idx * 2 + 1]; 60383 for (var i = 0; i < len; i++) { 60384 out[i] = out[i] || []; 60385 out[i][0] = this._flatCoords[offset + i * 2]; 60386 out[i][1] = this._flatCoords[offset + i * 2 + 1]; 60387 } 60388 return len; 60389 } 60390 else { 60391 var coords = this._getCoordsFromItemModel(idx); 60392 for (var i = 0; i < coords.length; i++) { 60393 out[i] = out[i] || []; 60394 out[i][0] = coords[i][0]; 60395 out[i][1] = coords[i][1]; 60396 } 60397 return coords.length; 60398 } 60399 }, 60400 60401 _processFlatCoordsArray: function (data) { 60402 var startOffset = 0; 60403 if (this._flatCoords) { 60404 startOffset = this._flatCoords.length; 60405 } 60406 // Stored as a typed array. In format 60407 // Points Count(2) | x | y | x | y | Points Count(3) | x | y | x | y | x | y | 60408 if (typeof data[0] === 'number') { 60409 var len = data.length; 60410 // Store offset and len of each segment 60411 var coordsOffsetAndLenStorage = new Uint32Arr(len); 60412 var coordsStorage = new Float64Arr(len); 60413 var coordsCursor = 0; 60414 var offsetCursor = 0; 60415 var dataCount = 0; 60416 for (var i = 0; i < len;) { 60417 dataCount++; 60418 var count = data[i++]; 60419 // Offset 60420 coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor + startOffset; 60421 // Len 60422 coordsOffsetAndLenStorage[offsetCursor++] = count; 60423 for (var k = 0; k < count; k++) { 60424 var x = data[i++]; 60425 var y = data[i++]; 60426 coordsStorage[coordsCursor++] = x; 60427 coordsStorage[coordsCursor++] = y; 60428 60429 if (i > len) { 60430 if (__DEV__) { 60431 throw new Error('Invalid data format.'); 60432 } 60433 } 60434 } 60435 } 60436 60437 return { 60438 flatCoordsOffset: new Uint32Array(coordsOffsetAndLenStorage.buffer, 0, offsetCursor), 60439 flatCoords: coordsStorage, 60440 count: dataCount 60441 }; 60442 } 60443 60444 return { 60445 flatCoordsOffset: null, 60446 flatCoords: null, 60447 count: data.length 60448 }; 60449 }, 60450 60451 getInitialData: function (option, ecModel) { 60452 if (__DEV__) { 60453 var CoordSys = CoordinateSystemManager.get(option.coordinateSystem); 60454 if (!CoordSys) { 60455 throw new Error('Unkown coordinate system ' + option.coordinateSystem); 60456 } 60457 } 60458 60459 var lineData = new List(['value'], this); 60460 lineData.hasItemOption = false; 60461 60462 lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) { 60463 // dataItem is simply coords 60464 if (dataItem instanceof Array) { 60465 return NaN; 60466 } 60467 else { 60468 lineData.hasItemOption = true; 60469 var value = dataItem.value; 60470 if (value != null) { 60471 return value instanceof Array ? value[dimIndex] : value; 60472 } 60473 } 60474 }); 60475 60476 return lineData; 60477 }, 60478 60479 formatTooltip: function (dataIndex) { 60480 var data = this.getData(); 60481 var itemModel = data.getItemModel(dataIndex); 60482 var name = itemModel.get('name'); 60483 if (name) { 60484 return name; 60485 } 60486 var fromName = itemModel.get('fromName'); 60487 var toName = itemModel.get('toName'); 60488 var html = []; 60489 fromName != null && html.push(fromName); 60490 toName != null && html.push(toName); 60491 60492 return encodeHTML(html.join(' > ')); 60493 }, 60494 60495 preventIncremental: function () { 60496 return !!this.get('effect.show'); 60497 }, 60498 60499 getProgressive: function () { 60500 var progressive = this.option.progressive; 60501 if (progressive == null) { 60502 return this.option.large ? 1e4 : this.get('progressive'); 60503 } 60504 return progressive; 60505 }, 60506 60507 getProgressiveThreshold: function () { 60508 var progressiveThreshold = this.option.progressiveThreshold; 60509 if (progressiveThreshold == null) { 60510 return this.option.large ? 2e4 : this.get('progressiveThreshold'); 60511 } 60512 return progressiveThreshold; 60513 }, 60514 60515 defaultOption: { 60516 coordinateSystem: 'geo', 60517 zlevel: 0, 60518 z: 2, 60519 legendHoverLink: true, 60520 60521 hoverAnimation: true, 60522 // Cartesian coordinate system 60523 xAxisIndex: 0, 60524 yAxisIndex: 0, 60525 60526 symbol: ['none', 'none'], 60527 symbolSize: [10, 10], 60528 // Geo coordinate system 60529 geoIndex: 0, 60530 60531 effect: { 60532 show: false, 60533 period: 4, 60534 // Animation delay. support callback 60535 // delay: 0, 60536 // If move with constant speed px/sec 60537 // period will be ignored if this property is > 0, 60538 constantSpeed: 0, 60539 symbol: 'circle', 60540 symbolSize: 3, 60541 loop: true, 60542 // Length of trail, 0 - 1 60543 trailLength: 0.2 60544 // Same with lineStyle.color 60545 // color 60546 }, 60547 60548 large: false, 60549 // Available when large is true 60550 largeThreshold: 2000, 60551 60552 // If lines are polyline 60553 // polyline not support curveness, label, animation 60554 polyline: false, 60555 60556 label: { 60557 show: false, 60558 position: 'end' 60559 // distance: 5, 60560 // formatter: æ â¡Ã§Â¾æââ¡Ã¦Å¾ ¼å¼ÂÃ¥â¢Â¨Ã¯Â¼ÅÃ¥ÂÅTooltip.formatterï¼Åä¸Âæâ¯æÅÂå¼âæÂ¥åâºÅ¾Ã¨Â°Æ 60561 }, 60562 60563 lineStyle: { 60564 opacity: 0.5 60565 } 60566 } 60567 });
60568 60569 /* 60570* Licensed to the Apache Software Foundation (ASF) under one 60571* or more contributor license agreements. See the NOTICE file 60572* distributed with this work for additional information 60573* regarding copyright ownership. The ASF licenses this file 60574* to you under the Apache License, Version 2.0 (the 60575* "License"); you may not use this file except in compliance 60576* with the License. You may obtain a copy of the License at 60577* 60578* http://www.apache.org/licenses/LICENSE-2.0 60579* 60580* Unless required by applicable law or agreed to in writing, 60581* software distributed under the License is distributed on an 60582* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60583* KIND, either express or implied. See the License for the 60584* specific language governing permissions and limitations 60585* under the License. 60586*/ 60587 60588 /** 60589 * Provide effect for line 60590 * @module echarts/chart/helper/EffectLine 60591 */ 60592 60593 /** 60594 * @constructor 60595 * @extends {module:zrender/graphic/Group} 60596 * @alias {module:echarts/chart/helper/Line} 60597 */ 60598 function EffectLine(lineData, idx, seriesScope) { 60599 Group.call(this); 60600 60601 this.add(this.createLine(lineData, idx, seriesScope)); 60602 60603 this._updateEffectSymbol(lineData, idx); 60604 } 60605 60606 var effectLineProto = EffectLine.prototype; 60607 60608 effectLineProto.createLine = function (lineData, idx, seriesScope) { 60609 return new Line$1(lineData, idx, seriesScope); 60610 }; 60611 60612 effectLineProto._updateEffectSymbol = function (lineData, idx) { 60613 var itemModel = lineData.getItemModel(idx); 60614 var effectModel = itemModel.getModel('effect'); 60615 var size = effectModel.get('symbolSize'); 60616 var symbolType = effectModel.get('symbol'); 60617 if (!isArray(size)) { 60618 size = [size, size]; 60619 } 60620 var color = effectModel.get('color') || lineData.getItemVisual(idx, 'color'); 60621 var symbol = this.childAt(1); 60622 60623 if (this._symbolType !== symbolType) { 60624 // Remove previous 60625 this.remove(symbol); 60626 60627 symbol = createSymbol( 60628 symbolType, -0.5, -0.5, 1, 1, color 60629 ); 60630 symbol.z2 = 100; 60631 symbol.culling = true; 60632 60633 this.add(symbol); 60634 } 60635 60636 // Symbol may be removed if loop is false 60637 if (!symbol) { 60638 return; 60639 } 60640 60641 // Shadow color is same with color in default 60642 symbol.setStyle('shadowColor', color); 60643 symbol.setStyle(effectModel.getItemStyle(['color'])); 60644 60645 symbol.attr('scale', size); 60646 60647 symbol.setColor(color); 60648 symbol.attr('scale', size); 60649 60650 this._symbolType = symbolType; 60651 60652 this._updateEffectAnimation(lineData, effectModel, idx); 60653 }; 60654 60655 effectLineProto._updateEffectAnimation = function (lineData, effectModel, idx) { 60656 60657 var symbol = this.childAt(1); 60658 if (!symbol) { 60659 return; 60660 } 60661 60662 var self = this; 60663 60664 var points = lineData.getItemLayout(idx); 60665 60666 var period = effectModel.get('period') * 1000; 60667 var loop = effectModel.get('loop'); 60668 var constantSpeed = effectModel.get('constantSpeed'); 60669 var delayExpr = retrieve(effectModel.get('delay'), function (idx) { 60670 return idx / lineData.count() * period / 3; 60671 }); 60672 var isDelayFunc = typeof delayExpr === 'function'; 60673 60674 // Ignore when updating 60675 symbol.ignore = true; 60676 60677 this.updateAnimationPoints(symbol, points); 60678 60679 if (constantSpeed > 0) { 60680 period = this.getLineLength(symbol) / constantSpeed * 1000; 60681 } 60682 60683 if (period !== this._period || loop !== this._loop) { 60684 60685 symbol.stopAnimation(); 60686 60687 var delay = delayExpr; 60688 if (isDelayFunc) { 60689 delay = delayExpr(idx); 60690 } 60691 if (symbol.__t > 0) { 60692 delay = -period * symbol.__t; 60693 } 60694 symbol.__t = 0; 60695 var animator = symbol.animate('', loop) 60696 .when(period, { 60697 __t: 1 60698 }) 60699 .delay(delay) 60700 .during(function () { 60701 self.updateSymbolPosition(symbol); 60702 }); 60703 if (!loop) { 60704 animator.done(function () { 60705 self.remove(symbol); 60706 }); 60707 } 60708 animator.start(); 60709 } 60710 60711 this._period = period; 60712 this._loop = loop; 60713 }; 60714 60715 effectLineProto.getLineLength = function (symbol) { 60716 // Not so accurate 60717 return (dist(symbol.__p1, symbol.__cp1) 60718 + dist(symbol.__cp1, symbol.__p2)); 60719 }; 60720 60721 effectLineProto.updateAnimationPoints = function (symbol, points) { 60722 symbol.__p1 = points[0]; 60723 symbol.__p2 = points[1]; 60724 symbol.__cp1 = points[2] || [ 60725 (points[0][0] + points[1][0]) / 2, 60726 (points[0][1] + points[1][1]) / 2 60727 ]; 60728 }; 60729 60730 effectLineProto.updateData = function (lineData, idx, seriesScope) { 60731 this.childAt(0).updateData(lineData, idx, seriesScope); 60732 this._updateEffectSymbol(lineData, idx); 60733 }; 60734 60735 effectLineProto.updateSymbolPosition = function (symbol) { 60736 var p1 = symbol.__p1; 60737 var p2 = symbol.__p2; 60738 var cp1 = symbol.__cp1; 60739 var t = symbol.__t; 60740 var pos = symbol.position; 60741 var quadraticAt$$1 = quadraticAt; 60742 var quadraticDerivativeAt$$1 = quadraticDerivativeAt; 60743 pos[0] = quadraticAt$$1(p1[0], cp1[0], p2[0], t); 60744 pos[1] = quadraticAt$$1(p1[1], cp1[1], p2[1], t); 60745 60746 // Tangent 60747 var tx = quadraticDerivativeAt$$1(p1[0], cp1[0], p2[0], t); 60748 var ty = quadraticDerivativeAt$$1(p1[1], cp1[1], p2[1], t); 60749
60750 symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; 60751 60752 symbol.ignore = false; 60753 }; 60754 60755 60756 effectLineProto.updateLayout = function (lineData, idx) { 60757 this.childAt(0).updateLayout(lineData, idx); 60758 60759 var effectModel = lineData.getItemModel(idx).getModel('effect'); 60760 this._updateEffectAnimation(lineData, effectModel, idx); 60761 }; 60762 60763 inherits(EffectLine, Group); 60764 60765 /* 60766* Licensed to the Apache Software Foundation (ASF) under one 60767* or more contributor license agreements. See the NOTICE file 60768* distributed with this work for additional information 60769* regarding copyright ownership. The ASF licenses this file 60770* to you under the Apache License, Version 2.0 (the 60771* "License"); you may not use this file except in compliance 60772* with the License. You may obtain a copy of the License at 60773* 60774* http://www.apache.org/licenses/LICENSE-2.0 60775* 60776* Unless required by applicable law or agreed to in writing, 60777* software distributed under the License is distributed on an 60778* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60779* KIND, either express or implied. See the License for the 60780* specific language governing permissions and limitations 60781* under the License. 60782*/ 60783 60784 /** 60785 * @module echarts/chart/helper/Line 60786 */ 60787 60788 /** 60789 * @constructor 60790 * @extends {module:zrender/graphic/Group} 60791 * @alias {module:echarts/chart/helper/Polyline} 60792 */ 60793 function Polyline$2(lineData, idx, seriesScope) { 60794 Group.call(this); 60795 60796 this._createPolyline(lineData, idx, seriesScope); 60797 } 60798 60799 var polylineProto = Polyline$2.prototype; 60800 60801 polylineProto._createPolyline = function (lineData, idx, seriesScope) { 60802 // var seriesModel = lineData.hostModel; 60803 var points = lineData.getItemLayout(idx); 60804 60805 var line = new Polyline({ 60806 shape: { 60807 points: points 60808 } 60809 }); 60810 60811 this.add(line); 60812 60813 this._updateCommonStl(lineData, idx, seriesScope); 60814 }; 60815 60816 polylineProto.updateData = function (lineData, idx, seriesScope) { 60817 var seriesModel = lineData.hostModel; 60818 60819 var line = this.childAt(0); 60820 var target = { 60821 shape: { 60822 points: lineData.getItemLayout(idx) 60823 } 60824 }; 60825 updateProps(line, target, seriesModel, idx); 60826 60827 this._updateCommonStl(lineData, idx, seriesScope); 60828 }; 60829 60830 polylineProto._updateCommonStl = function (lineData, idx, seriesScope) { 60831 var line = this.childAt(0); 60832 var itemModel = lineData.getItemModel(idx); 60833 60834 var visualColor = lineData.getItemVisual(idx, 'color'); 60835 60836 var lineStyle = seriesScope && seriesScope.lineStyle; 60837 var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle; 60838 60839 if (!seriesScope || lineData.hasItemOption) { 60840 lineStyle = itemModel.getModel('lineStyle').getLineStyle(); 60841 hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle(); 60842 } 60843 line.useStyle(defaults( 60844 { 60845 strokeNoScale: true, 60846 fill: 'none', 60847 stroke: visualColor 60848 }, 60849 lineStyle 60850 )); 60851 line.hoverStyle = hoverLineStyle; 60852 60853 setHoverStyle(this); 60854 }; 60855 60856 polylineProto.updateLayout = function (lineData, idx) { 60857 var polyline = this.childAt(0); 60858 polyline.setShape('points', lineData.getItemLayout(idx)); 60859 }; 60860 60861 inherits(Polyline$2, Group); 60862 60863 /* 60864* Licensed to the Apache Software Foundation (ASF) under one 60865* or more contributor license agreements. See the NOTICE file 60866* distributed with this work for additional information 60867* regarding copyright ownership. The ASF licenses this file 60868* to you under the Apache License, Version 2.0 (the 60869* "License"); you may not use this file except in compliance 60870* with the License. You may obtain a copy of the License at 60871* 60872* http://www.apache.org/licenses/LICENSE-2.0 60873* 60874* Unless required by applicable law or agreed to in writing, 60875* software distributed under the License is distributed on an 60876* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60877* KIND, either express or implied. See the License for the 60878* specific language governing permissions and limitations 60879* under the License. 60880*/ 60881 60882 /** 60883 * Provide effect for line 60884 * @module echarts/chart/helper/EffectLine 60885 */ 60886 60887 /** 60888 * @constructor 60889 * @extends {module:echarts/chart/helper/EffectLine} 60890 * @alias {module:echarts/chart/helper/Polyline} 60891 */ 60892 function EffectPolyline(lineData, idx, seriesScope) {
60893 EffectLine.call(this, lineData, idx, seriesScope); 60894 this._lastFrame = 0; 60895 this._lastFramePercent = 0; 60896 } 60897 60898 var effectPolylineProto = EffectPolyline.prototype; 60899 60900// Overwrite 60901 effectPolylineProto.createLine = function (lineData, idx, seriesScope) { 60902 return new Polyline$2(lineData, idx, seriesScope); 60903 }; 60904 60905// Overwrite 60906 effectPolylineProto.updateAnimationPoints = function (symbol, points) { 60907 this._points = points; 60908 var accLenArr = [0]; 60909 var len$$1 = 0; 60910 for (var i = 1; i < points.length; i++) { 60911 var p1 = points[i - 1]; 60912 var p2 = points[i]; 60913 len$$1 += dist(p1, p2); 60914 accLenArr.push(len$$1); 60915 } 60916 if (len$$1 === 0) { 60917 return; 60918 } 60919 60920 for (var i = 0; i < accLenArr.length; i++) { 60921 accLenArr[i] /= len$$1; 60922 } 60923 this._offsets = accLenArr; 60924 this._length = len$$1; 60925 }; 60926 60927// Overwrite 60928 effectPolylineProto.getLineLength = function (symbol) { 60929 return this._length; 60930 }; 60931 60932// Overwrite 60933 effectPolylineProto.updateSymbolPosition = function (symbol) { 60934 var t = symbol.__t; 60935 var points = this._points; 60936 var offsets = this._offsets; 60937 var len$$1 = points.length; 60938 60939 if (!offsets) { 60940 // Has length 0 60941 return; 60942 } 60943 60944 var lastFrame = this._lastFrame; 60945 var frame; 60946 60947 if (t < this._lastFramePercent) { 60948 // Start from the next frame 60949 // PENDING start from lastFrame ? 60950 var start = Math.min(lastFrame + 1, len$$1 - 1); 60951 for (frame = start; frame >= 0; frame--) { 60952 if (offsets[frame] <= t) { 60953 break; 60954 } 60955 } 60956 // PENDING really need to do this ? 60957 frame = Math.min(frame, len$$1 - 2); 60958 } 60959 else { 60960 for (var frame = lastFrame; frame < len$$1; frame++) { 60961 if (offsets[frame] > t) { 60962 break; 60963 } 60964 } 60965 frame = Math.min(frame - 1, len$$1 - 2); 60966 } 60967 60968 lerp( 60969 symbol.position, points[frame], points[frame + 1], 60970 (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]) 60971 ); 60972 60973 var tx = points[frame + 1][0] - points[frame][0]; 60974 var ty = points[frame + 1][1] - points[frame][1]; 60975 symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; 60976 60977 this._lastFrame = frame; 60978 this._lastFramePercent = t; 60979 60980 symbol.ignore = false; 60981 }; 60982 60983 inherits(EffectPolyline, EffectLine); 60984 60985 /* 60986* Licensed to the Apache Software Foundation (ASF) under one 60987* or more contributor license agreements. See the NOTICE file 60988* distributed with this work for additional information 60989* regarding copyright ownership. The ASF licenses this file 60990* to you under the Apache License, Version 2.0 (the 60991* "License"); you may not use this file except in compliance 60992* with the License. You may obtain a copy of the License at 60993* 60994* http://www.apache.org/licenses/LICENSE-2.0 60995* 60996* Unless required by applicable law or agreed to in writing, 60997* software distributed under the License is distributed on an 60998* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 60999* KIND, either express or implied. See the License for the 61000* specific language governing permissions and limitations 61001* under the License. 61002*/ 61003 61004// TODO Batch by color 61005 61006 var LargeLineShape = extendShape({ 61007 61008 shape: { 61009 polyline: false, 61010 curveness: 0, 61011 segs: [] 61012 }, 61013 61014 buildPath: function (path, shape) { 61015 var segs = shape.segs; 61016 var curveness = shape.curveness; 61017 61018 if (shape.polyline) { 61019 for (var i = 0; i < segs.length;) { 61020 var count = segs[i++]; 61021 if (count > 0) { 61022 path.moveTo(segs[i++], segs[i++]); 61023 for (var k = 1; k < count;
61023 k++) { 61024 path.lineTo(segs[i++], segs[i++]); 61025 } 61026 } 61027 } 61028 } 61029 else { 61030 for (var i = 0; i < segs.length;) { 61031 var x0 = segs[i++]; 61032 var y0 = segs[i++]; 61033 var x1 = segs[i++]; 61034 var y1 = segs[i++]; 61035 path.moveTo(x0, y0); 61036 if (curveness > 0) { 61037 var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness; 61038 var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness; 61039 path.quadraticCurveTo(x2, y2, x1, y1); 61040 } 61041 else { 61042 path.lineTo(x1, y1); 61043 } 61044 } 61045 } 61046 }, 61047 61048 findDataIndex: function (x, y) { 61049 61050 var shape = this.shape; 61051 var segs = shape.segs; 61052 var curveness = shape.curveness; 61053 61054 if (shape.polyline) { 61055 var dataIndex = 0; 61056 for (var i = 0; i < segs.length;) { 61057 var count = segs[i++]; 61058 if (count > 0) { 61059 var x0 = segs[i++]; 61060 var y0 = segs[i++]; 61061 for (var k = 1; k < count; k++) { 61062 var x1 = segs[i++]; 61063 var y1 = segs[i++]; 61064 if (containStroke$1(x0, y0, x1, y1)) { 61065 return dataIndex; 61066 } 61067 } 61068 } 61069 61070 dataIndex++; 61071 } 61072 } 61073 else { 61074 var dataIndex = 0; 61075 for (var i = 0; i < segs.length;) { 61076 var x0 = segs[i++]; 61077 var y0 = segs[i++]; 61078 var x1 = segs[i++]; 61079 var y1 = segs[i++]; 61080 if (curveness > 0) { 61081 var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness; 61082 var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness; 61083 61084 if (containStroke$3(x0, y0, x2, y2, x1, y1)) { 61085 return dataIndex; 61086 } 61087 } 61088 else { 61089 if (containStroke$1(x0, y0, x1, y1)) { 61090 return dataIndex; 61091 } 61092 } 61093 61094 dataIndex++; 61095 } 61096 } 61097 61098 return -1; 61099 } 61100 }); 61101 61102 function LargeLineDraw() { 61103 this.group = new Group(); 61104 } 61105 61106 var largeLineProto = LargeLineDraw.prototype; 61107 61108 largeLineProto.isPersistent = function () { 61109 return !this._incremental; 61110 }; 61111 61112 /** 61113 * Update symbols draw by new data 61114 * @param {module:echarts/data/List} data 61115 */ 61116 largeLineProto.updateData = function (data) { 61117 this.group.removeAll(); 61118 61119 var lineEl = new LargeLineShape({ 61120 rectHover: true, 61121 cursor: 'default' 61122 }); 61123 lineEl.setShape({ 61124 segs: data.getLayout('linesPoints') 61125 }); 61126 61127 this._setCommon(lineEl, data); 61128 61129 // Add back 61130 this.group.add(lineEl); 61131 61132 this._incremental = null; 61133 }; 61134 61135 /** 61136 * @override 61137 */ 61138 largeLineProto.incrementalPrepareUpdate = function (data) { 61139 this.group.removeAll(); 61140 61141 this._clearIncremental(); 61142 61143 if (data.count() > 5e5) { 61144 if (!this._incremental) { 61145 this._incremental = new IncrementalDisplayble({ 61146 silent: true 61147 }); 61148 } 61149 this.group.add(this._incremental); 61150 } 61151 else { 61152 this._incremental = null; 61153 } 61154 }; 61155 61156 /** 61157 * @override 61158 */ 61159 largeLineProto.incrementalUpdate = function (taskParams, data) { 61160 var lineEl = new LargeLineShape(); 61161 lineEl.setShape({ 61162 segs: data.getLayout('linesPoints') 61163 }); 61164
61165 this._setCommon(lineEl, data, !!this._incremental); 61166 61167 if (!this._incremental) { 61168 lineEl.rectHover = true; 61169 lineEl.cursor = 'default'; 61170 lineEl.__startIndex = taskParams.start; 61171 this.group.add(lineEl); 61172 } 61173 else { 61174 this._incremental.addDisplayable(lineEl, true); 61175 } 61176 }; 61177 61178 /** 61179 * @override 61180 */ 61181 largeLineProto.remove = function () { 61182 this._clearIncremental(); 61183 this._incremental = null; 61184 this.group.removeAll(); 61185 }; 61186 61187 largeLineProto._setCommon = function (lineEl, data, isIncremental) { 61188 var hostModel = data.hostModel; 61189 61190 lineEl.setShape({ 61191 polyline: hostModel.get('polyline'), 61192 curveness: hostModel.get('lineStyle.curveness') 61193 }); 61194 61195 lineEl.useStyle( 61196 hostModel.getModel('lineStyle').getLineStyle() 61197 ); 61198 lineEl.style.strokeNoScale = true; 61199 61200 var visualColor = data.getVisual('color'); 61201 if (visualColor) { 61202 lineEl.setStyle('stroke', visualColor); 61203 } 61204 lineEl.setStyle('fill'); 61205 61206 if (!isIncremental) { 61207 // Enable tooltip 61208 // PENDING May have performance issue when path is extremely large 61209 lineEl.seriesIndex = hostModel.seriesIndex; 61210 lineEl.on('mousemove', function (e) { 61211 lineEl.dataIndex = null; 61212 var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY); 61213 if (dataIndex > 0) { 61214 // Provide dataIndex for tooltip 61215 lineEl.dataIndex = dataIndex + lineEl.__startIndex; 61216 } 61217 }); 61218 } 61219 }; 61220 61221 largeLineProto._clearIncremental = function () { 61222 var incremental = this._incremental; 61223 if (incremental) { 61224 incremental.clearDisplaybles(); 61225 } 61226 }; 61227 61228 /* 61229* Licensed to the Apache Software Foundation (ASF) under one 61230* or more contributor license agreements. See the NOTICE file 61231* distributed with this work for additional information 61232* regarding copyright ownership. The ASF licenses this file 61233* to you under the Apache License, Version 2.0 (the 61234* "License"); you may not use this file except in compliance 61235* with the License. You may obtain a copy of the License at 61236* 61237* http://www.apache.org/licenses/LICENSE-2.0 61238* 61239* Unless required by applicable law or agreed to in writing, 61240* software distributed under the License is distributed on an 61241* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61242* KIND, either express or implied. See the License for the 61243* specific language governing permissions and limitations 61244* under the License. 61245*/ 61246 61247 var linesLayout = { 61248 seriesType: 'lines', 61249 61250 plan: createRenderPlanner(), 61251 61252 reset: function (seriesModel) { 61253 var coordSys = seriesModel.coordinateSystem; 61254 var isPolyline = seriesModel.get('polyline'); 61255 var isLarge = seriesModel.pipelineContext.large; 61256 61257 function progress(params, lineData) { 61258 var lineCoords = []; 61259 if (isLarge) { 61260 var points; 61261 var segCount = params.end - params.start; 61262 if (isPolyline) { 61263 var totalCoordsCount = 0; 61264 for (var i = params.start; i < params.end; i++) { 61265 totalCoordsCount += seriesModel.getLineCoordsCount(i); 61266 } 61267 points = new Float32Array(segCount + totalCoordsCount * 2); 61268 } 61269 else { 61270 points = new Float32Array(segCount * 4); 61271 } 61272 61273 var offset = 0; 61274 var pt = []; 61275 for (var i = params.start; i < params.end; i++) { 61276 var len = seriesModel.getLineCoords(i, lineCoords); 61277 if (isPolyline) { 61278 points[offset++] = len; 61279 } 61280 for (var k = 0; k < len; k++) { 61281 pt = coordSys.dataToPoint(lineCoords[k], false, pt); 61282 points[offset++] = pt[0]; 61283 points[offset++] = pt[1]; 61284 } 61285 } 61286 61287 lineData.setLayout('linesPoints', points); 61288 } 61289 else { 61290 for (var i = params.start; i < params.end; i++) { 61291 var itemModel = lineData.getItemModel(i); 61292 var len = seriesModel.getLineCoords(i, lineCoords);
61293 61294 var pts = []; 61295 if (isPolyline) { 61296 for (var j = 0; j < len; j++) { 61297 pts.push(coordSys.dataToPoint(lineCoords[j])); 61298 } 61299 } 61300 else { 61301 pts[0] = coordSys.dataToPoint(lineCoords[0]); 61302 pts[1] = coordSys.dataToPoint(lineCoords[1]); 61303 61304 var curveness = itemModel.get('lineStyle.curveness'); 61305 if (+curveness) { 61306 pts[2] = [ 61307 (pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness, 61308 (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness 61309 ]; 61310 } 61311 } 61312 lineData.setItemLayout(i, pts); 61313 } 61314 } 61315 } 61316 61317 return { progress: progress }; 61318 } 61319 }; 61320 61321 /* 61322* Licensed to the Apache Software Foundation (ASF) under one 61323* or more contributor license agreements. See the NOTICE file 61324* distributed with this work for additional information 61325* regarding copyright ownership. The ASF licenses this file 61326* to you under the Apache License, Version 2.0 (the 61327* "License"); you may not use this file except in compliance 61328* with the License. You may obtain a copy of the License at 61329* 61330* http://www.apache.org/licenses/LICENSE-2.0 61331* 61332* Unless required by applicable law or agreed to in writing, 61333* software distributed under the License is distributed on an 61334* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61335* KIND, either express or implied. See the License for the 61336* specific language governing permissions and limitations 61337* under the License. 61338*/ 61339 61340 extendChartView({ 61341 61342 type: 'lines', 61343 61344 init: function () {}, 61345 61346 render: function (seriesModel, ecModel, api) { 61347 var data = seriesModel.getData(); 61348 61349 var lineDraw = this._updateLineDraw(data, seriesModel); 61350 61351 var zlevel = seriesModel.get('zlevel'); 61352 var trailLength = seriesModel.get('effect.trailLength'); 61353 61354 var zr = api.getZr(); 61355 // Avoid the drag cause ghost shadow 61356 // FIXME Better way ? 61357 // SVG doesn't support 61358 var isSvg = zr.painter.getType() === 'svg'; 61359 if (!isSvg) { 61360 zr.painter.getLayer(zlevel).clear(true); 61361 } 61362 // Config layer with motion blur 61363 if (this._lastZlevel != null && !isSvg) { 61364 zr.configLayer(this._lastZlevel, { 61365 motionBlur: false 61366 }); 61367 } 61368 if (this._showEffect(seriesModel) && trailLength) { 61369 if (__DEV__) { 61370 var notInIndividual = false; 61371 ecModel.eachSeries(function (otherSeriesModel) { 61372 if (otherSeriesModel !== seriesModel && otherSeriesModel.get('zlevel') === zlevel) { 61373 notInIndividual = true; 61374 } 61375 }); 61376 notInIndividual && console.warn('Lines with trail effect should have an individual zlevel'); 61377 } 61378 61379 if (!isSvg) { 61380 zr.configLayer(zlevel, { 61381 motionBlur: true, 61382 lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0) 61383 }); 61384 } 61385 } 61386 61387 lineDraw.updateData(data); 61388 61389 this._lastZlevel = zlevel; 61390 61391 this._finished = true; 61392 }, 61393 61394 incrementalPrepareRender: function (seriesModel, ecModel, api) { 61395 var data = seriesModel.getData(); 61396 61397 var lineDraw = this._updateLineDraw(data, seriesModel); 61398 61399 lineDraw.incrementalPrepareUpdate(data); 61400 61401 this._clearLayer(api); 61402 61403 this._finished = false; 61404 }, 61405 61406 incrementalRender: function (taskParams, seriesModel, ecModel) { 61407 this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData()); 61408 61409 this._finished = taskParams.end === seriesModel.getData().count(); 61410 }, 61411 61412 updateTransform: function (seriesModel, ecModel, api) { 61413 var data = seriesModel.getData(); 61414 var pipelineContext = seriesModel.pipelineContext; 61415 61416 if (!this._finished || pipelineContext.large || pipelineContext.progressiveRen
61416der) { 61417 // TODO Don't have to do update in large mode. Only do it when there are millions of data. 61418 return { 61419 update: true 61420 }; 61421 } 61422 else { 61423 // TODO Use same logic with ScatterView. 61424 // Manually update layout 61425 var res = linesLayout.reset(seriesModel); 61426 if (res.progress) { 61427 res.progress({ start: 0, end: data.count() }, data); 61428 } 61429 this._lineDraw.updateLayout(); 61430 this._clearLayer(api); 61431 } 61432 }, 61433 61434 _updateLineDraw: function (data, seriesModel) { 61435 var lineDraw = this._lineDraw; 61436 var hasEffect = this._showEffect(seriesModel); 61437 var isPolyline = !!seriesModel.get('polyline'); 61438 var pipelineContext = seriesModel.pipelineContext; 61439 var isLargeDraw = pipelineContext.large; 61440 61441 if (__DEV__) { 61442 if (hasEffect && isLargeDraw) { 61443 console.warn('Large lines not support effect'); 61444 } 61445 } 61446 if (!lineDraw 61447 || hasEffect !== this._hasEffet 61448 || isPolyline !== this._isPolyline 61449 || isLargeDraw !== this._isLargeDraw 61450 ) { 61451 if (lineDraw) { 61452 lineDraw.remove(); 61453 } 61454 lineDraw = this._lineDraw = isLargeDraw 61455 ? new LargeLineDraw() 61456 : new LineDraw( 61457 isPolyline 61458 ? (hasEffect ? EffectPolyline : Polyline$2) 61459 : (hasEffect ? EffectLine : Line$1) 61460 ); 61461 this._hasEffet = hasEffect; 61462 this._isPolyline = isPolyline; 61463 this._isLargeDraw = isLargeDraw; 61464 this.group.removeAll(); 61465 } 61466 61467 this.group.add(lineDraw.group); 61468 61469 return lineDraw; 61470 }, 61471 61472 _showEffect: function (seriesModel) { 61473 return !!seriesModel.get('effect.show'); 61474 }, 61475 61476 _clearLayer: function (api) { 61477 // Not use motion when dragging or zooming 61478 var zr = api.getZr(); 61479 var isSvg = zr.painter.getType() === 'svg'; 61480 if (!isSvg && this._lastZlevel != null) { 61481 zr.painter.getLayer(this._lastZlevel).clear(true); 61482 } 61483 }, 61484 61485 remove: function (ecModel, api) { 61486 this._lineDraw && this._lineDraw.remove(); 61487 this._lineDraw = null; 61488 // Clear motion when lineDraw is removed 61489 this._clearLayer(api); 61490 }, 61491 61492 dispose: function () {} 61493 }); 61494 61495 /* 61496* Licensed to the Apache Software Foundation (ASF) under one 61497* or more contributor license agreements. See the NOTICE file 61498* distributed with this work for additional information 61499* regarding copyright ownership. The ASF licenses this file 61500* to you under the Apache License, Version 2.0 (the 61501* "License"); you may not use this file except in compliance 61502* with the License. You may obtain a copy of the License at 61503* 61504* http://www.apache.org/licenses/LICENSE-2.0 61505* 61506* Unless required by applicable law or agreed to in writing, 61507* software distributed under the License is distributed on an 61508* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61509* KIND, either express or implied. See the License for the 61510* specific language governing permissions and limitations 61511* under the License. 61512*/ 61513 61514 61515 function normalize$2(a) { 61516 if (!(a instanceof Array)) { 61517 a = [a, a]; 61518 } 61519 return a; 61520 } 61521 61522 var opacityQuery = 'lineStyle.opacity'.split('.'); 61523 61524 var linesVisual = { 61525 seriesType: 'lines', 61526 reset: function (seriesModel, ecModel, api) { 61527 var symbolType = normalize$2(seriesModel.get('symbol')); 61528 var symbolSize = normalize$2(seriesModel.get('symbolSize')); 61529 var data = seriesModel.getData(); 61530 61531 data.setVisual('fromSymbol', symbolType && symbolType[0]); 61532 data.setVisual('toSymbol', symbolType && symbolType[1]); 61533 data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); 61534 data.setVisual('toSymbolSize', symbolSize && symbolSize[1]); 61535 data.setVisual('opacity', seriesModel.get(opacityQuery)); 61536 61537 function dataEach(data, idx) { 61538 var itemModel = data.getItemModel(idx); 61539 var symbolType = normalize$2(itemModel.getShallow('symbol', true)); 61540 var symbolSize = normalize$2(itemModel.getShallow('symbolSize', true)); 61541 var opacity = itemModel.get(opacityQuery); 61542 61543 symbolType[0] && data.setItemVisual(idx, 'fromSymbol', symbolType[0]); 61544 symbolType[1] && data.setItemVisual(idx, 'toSymbol', symbolType[1]); 61545 symbolSize[0] && data.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]); 61546 symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]); 61547 61548 data.setItemVisual(idx, 'opacity', opacity); 61549 } 61550 61551 return {dataEach: data.hasItemOption ? dataEach : null}; 61552 } 61553 }; 61554 61555 /* 61556* Licensed to the Apache Software Foundation (ASF) under one 61557* or more contributor license agreements. See the NOTICE file 61558* distributed with this work for additional information 61559* regarding copyright ownership. The ASF licenses this file 61560* to you under the Apache License, Version 2.0 (the 61561* "License");
61561 you may not use this file except in compliance 61562* with the License. You may obtain a copy of the License at 61563* 61564* http://www.apache.org/licenses/LICENSE-2.0 61565* 61566* Unless required by applicable law or agreed to in writing, 61567* software distributed under the License is distributed on an 61568* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61569* KIND, either express or implied. See the License for the 61570* specific language governing permissions and limitations 61571* under the License. 61572*/ 61573 61574 registerLayout(linesLayout); 61575 registerVisual(linesVisual); 61576 61577 /* 61578* Licensed to the Apache Software Foundation (ASF) under one 61579* or more contributor license agreements. See the NOTICE file 61580* distributed with this work for additional information 61581* regarding copyright ownership. The ASF licenses this file 61582* to you under the Apache License, Version 2.0 (the 61583* "License"); you may not use this file except in compliance 61584* with the License. You may obtain a copy of the License at 61585* 61586* http://www.apache.org/licenses/LICENSE-2.0 61587* 61588* Unless required by applicable law or agreed to in writing, 61589* software distributed under the License is distributed on an 61590* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61591* KIND, either express or implied. See the License for the 61592* specific language governing permissions and limitations 61593* under the License. 61594*/ 61595 61596 SeriesModel.extend({ 61597 type: 'series.heatmap', 61598 61599 getInitialData: function (option, ecModel) { 61600 return createListFromArray(this.getSource(), this, { 61601 generateCoord: 'value' 61602 }); 61603 }, 61604 61605 preventIncremental: function () { 61606 var coordSysCreator = CoordinateSystemManager.get(this.get('coordinateSystem')); 61607 if (coordSysCreator && coordSysCreator.dimensions) { 61608 return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat'; 61609 } 61610 }, 61611 61612 defaultOption: { 61613 61614 // Cartesian2D or geo 61615 coordinateSystem: 'cartesian2d', 61616 61617 zlevel: 0, 61618 61619 z: 2, 61620 61621 // Cartesian coordinate system 61622 // xAxisIndex: 0, 61623 // yAxisIndex: 0, 61624 61625 // Geo coordinate system 61626 geoIndex: 0, 61627 61628 blurSize: 30, 61629 61630 pointSize: 20, 61631 61632 maxOpacity: 1, 61633 61634 minOpacity: 0 61635 } 61636 }); 61637 61638 /* 61639* Licensed to the Apache Software Foundation (ASF) under one 61640* or more contributor license agreements. See the NOTICE file 61641* distributed with this work for additional information 61642* regarding copyright ownership. The ASF licenses this file 61643* to you under the Apache License, Version 2.0 (the 61644* "License"); you may not use this file except in compliance 61645* with the License. You may obtain a copy of the License at 61646* 61647* http://www.apache.org/licenses/LICENSE-2.0 61648* 61649* Unless required by applicable law or agreed to in writing, 61650* software distributed under the License is distributed on an 61651* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61652* KIND, either express or implied. See the License for the 61653* specific language governing permissions and limitations 61654* under the License. 61655*/ 61656 61657 /** 61658 * @file defines echarts Heatmap Chart 61659 * @author Ovilia ([email protected]) 61660 * Inspired by https://github.com/mourner/simpleheat 61661 * 61662 * @module 61663 */ 61664 61665 var GRADIENT_LEVELS = 256; 61666 61667 /** 61668 * Heatmap Chart 61669 * 61670 * @class 61671 */ 61672 function Heatmap() { 61673 var canvas = createCanvas(); 61674 this.canvas = canvas; 61675 61676 this.blurSize = 30; 61677 this.pointSize = 20; 61678 61679 this.maxOpacity = 1; 61680 this.minOpacity = 0; 61681 61682 this._gradientPixels = {}; 61683 } 61684 61685 Heatmap.prototype = { 61686 /** 61687 * Renders Heatmap and returns the rendered canvas 61688 * @param {Array} data array of data, each has x, y, value 61689 * @param {number} width canvas width 61690 * @param {number} height canvas height 61691 */ 61692 update: function(data, width, height, normalize, colorFunc, isInRange) { 61693 var brush = this._getBrush(); 61694 var gradientInRange = this._getGradient(data, colorFunc, 'inRange'); 61695 var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange'); 61696 var r = this.pointSize + this.blurSize; 61697 61698 var canvas = this.canvas; 61699 var ctx = canvas.getContext('2d'); 61700 var len = data.length; 61701 canvas.width = width; 61702 canvas.height = height; 61703 for (var i = 0; i < len; ++i) { 61704 var p = data[i]; 61705 var x = p[0]; 61706 var y = p[1]; 61707 var value = p[2]; 61708 61709 // calculate alpha using value 61710 var alpha = normalize(value); 61711 61712 // draw with the circle brush with alpha 61713 ctx.globalAlpha = alpha; 61714 ctx.drawImage(brush, x - r, y - r); 61715 } 61716 61717 if (!canvas.width || !canvas.height) { 61718 // Avoid "Uncaught DOMException: Failed to execute 'getImageData' on 61719 // 'CanvasRenderingContext2D': The source height is 0." 61720 return canvas; 61721 } 61722 61723 // colorize the canvas using alpha value and set with gradient 61724 var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); 61725 61726 var pixels = imageData.data; 61727 var offset = 0; 61728 var pixelLen = pixels.length; 61729 var minOpacity = this.minOpacity; 61730 var maxOpacity = this.maxOpacity; 61731 var diffOpacity = maxOpacity - minOpacity; 61732 61733 while(offset < pixelLen) { 61734 var alpha = pixels[offset + 3] / 256;
vendor: 5,991 bytes, lines 61735-61886
61735 var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; 61736 // Simple optimize to ignore the empty data 61737 if (alpha > 0) { 61738 var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; 61739 // Any alpha > 0 will be mapped to [minOpacity, maxOpacity] 61740 alpha > 0 && (alpha = alpha * diffOpacity + minOpacity); 61741 pixels[offset++] = gradient[gradientOffset]; 61742 pixels[offset++] = gradient[gradientOffset + 1]; 61743 pixels[offset++] = gradient[gradientOffset + 2]; 61744 pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256; 61745 } 61746 else { 61747 offset += 4; 61748 } 61749 } 61750 ctx.putImageData(imageData, 0, 0); 61751 61752 return canvas; 61753 }, 61754 61755 /** 61756 * get canvas of a black circle brush used for canvas to draw later 61757 * @private 61758 * @returns {Object} circle brush canvas 61759 */ 61760 _getBrush: function() { 61761 var brushCanvas = this._brushCanvas || (this._brushCanvas = createCanvas()); 61762 // set brush size 61763 var r = this.pointSize + this.blurSize; 61764 var d = r * 2; 61765 brushCanvas.width = d; 61766 brushCanvas.height = d; 61767 61768 var ctx = brushCanvas.getContext('2d'); 61769 ctx.clearRect(0, 0, d, d); 61770 61771 // in order to render shadow without the distinct circle, 61772 // draw the distinct circle in an invisible place, 61773 // and use shadowOffset to draw shadow in the center of the canvas 61774 ctx.shadowOffsetX = d; 61775 ctx.shadowBlur = this.blurSize; 61776 // draw the shadow in black, and use alpha and shadow blur to generate 61777 // color in color map 61778 ctx.shadowColor = '#000'; 61779 61780 // draw circle in the left to the canvas 61781 ctx.beginPath(); 61782 ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true); 61783 ctx.closePath(); 61784 ctx.fill(); 61785 return brushCanvas; 61786 }, 61787 61788 /** 61789 * get gradient color map 61790 * @private 61791 */ 61792 _getGradient: function (data, colorFunc, state) { 61793 var gradientPixels = this._gradientPixels; 61794 var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4)); 61795 var color = [0, 0, 0, 0]; 61796 var off = 0; 61797 for (var i = 0; i < 256; i++) { 61798 colorFunc[state](i / 255, true, color); 61799 pixelsSingleState[off++] = color[0]; 61800 pixelsSingleState[off++] = color[1]; 61801 pixelsSingleState[off++] = color[2]; 61802 pixelsSingleState[off++] = color[3]; 61803 } 61804 return pixelsSingleState; 61805 } 61806 }; 61807 61808 /* 61809* Licensed to the Apache Software Foundation (ASF) under one 61810* or more contributor license agreements. See the NOTICE file 61811* distributed with this work for additional information 61812* regarding copyright ownership. The ASF licenses this file 61813* to you under the Apache License, Version 2.0 (the 61814* "License"); you may not use this file except in compliance 61815* with the License. You may obtain a copy of the License at 61816* 61817* http://www.apache.org/licenses/LICENSE-2.0 61818* 61819* Unless required by applicable law or agreed to in writing, 61820* software distributed under the License is distributed on an 61821* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 61822* KIND, either express or implied. See the License for the 61823* specific language governing permissions and limitations 61824* under the License. 61825*/ 61826 61827 function getIsInPiecewiseRange(dataExtent, pieceList, selected) { 61828 var dataSpan = dataExtent[1] - dataExtent[0]; 61829 pieceList = map(pieceList, function (piece) { 61830 return { 61831 interval: [ 61832 (piece.interval[0] - dataExtent[0]) / dataSpan, 61833 (piece.interval[1] - dataExtent[0]) / dataSpan 61834 ] 61835 }; 61836 }); 61837 var len = pieceList.length; 61838 var lastIndex = 0; 61839 61840 return function (val) { 61841 // Try to find in the location of the last found 61842 for (var i = lastIndex; i < len; i++) { 61843 var interval = pieceList[i].interval; 61844 if (interval[0] <= val && val <= interval[1]) { 61845 lastIndex = i; 61846 break; 61847 } 61848 } 61849 if (i === len) { // Not found, back interation 61850 for (var i = lastIndex - 1; i >= 0; i--) { 61851 var interval = pieceList[i].interval; 61852 if (interval[0] <= val && val <= interval[1]) { 61853 lastIndex = i; 61854 break; 61855 } 61856 } 61857 } 61858 return i >= 0 && i < len && selected[i]; 61859 }; 61860 } 61861 61862 function getIsInContinuousRange(dataExtent, range) { 61863 var dataSpan = dataExtent[1] - dataExtent[0]; 61864 range = [ 61865 (range[0] - dataExtent[0]) / dataSpan, 61866 (range[1] - dataExtent[0]) / dataSpan 61867 ]; 61868 return function (val) { 61869 return val >= range[0] && val <= range[1]; 61870 }; 61871 } 61872 61873 function isGeoCoordSys(coordSys) { 61874 var dimensions = coordSys.dimensions; 61875 // Not use coorSys.type === 'geo' because coordSys maybe extended 61876 return dimensions[0] === 'lng' && dimensions[1] === 'lat'; 61877 } 61878 61879 extendChartView({ 61880 61881 type: 'heatmap', 61882 61883 render: function (seriesModel, ecModel, api) { 61884 var visualMapOfThisSeries; 61885 ecModel.eachComponent('visualMap', function (visualMap) { 61886 visualMap.eachTargetSeries(function (targetSeries) {
61887 if (targetSeries === seriesModel) { 61888 visualMapOfThisSeries = visualMap; 61889 } 61890 }); 61891 }); 61892 61893 if (__DEV__) { 61894 if (!visualMapOfThisSeries) { 61895 throw new Error('Heatmap must use with visualMap'); 61896 } 61897 } 61898 61899 this.group.removeAll(); 61900 61901 this._incrementalDisplayable = null; 61902 61903 var coordSys = seriesModel.coordinateSystem; 61904 if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') { 61905 this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count()); 61906 } 61907 else if (isGeoCoordSys(coordSys)) { 61908 this._renderOnGeo( 61909 coordSys, seriesModel, visualMapOfThisSeries, api 61910 ); 61911 } 61912 }, 61913 61914 incrementalPrepareRender: function (seriesModel, ecModel, api) { 61915 this.group.removeAll(); 61916 }, 61917 61918 incrementalRender: function (params, seriesModel, ecModel, api) { 61919 var coordSys = seriesModel.coordinateSystem; 61920 if (coordSys) { 61921 this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true); 61922 } 61923 }, 61924 61925 _renderOnCartesianAndCalendar: function (seriesModel, api, start, end, incremental) { 61926 61927 var coordSys = seriesModel.coordinateSystem; 61928 var width; 61929 var height; 61930 61931 if (coordSys.type === 'cartesian2d') { 61932 var xAxis = coordSys.getAxis('x'); 61933 var yAxis = coordSys.getAxis('y'); 61934 61935 if (__DEV__) { 61936 if (!(xAxis.type === 'category' && yAxis.type === 'category')) { 61937 throw new Error('Heatmap on cartesian must have two category axes'); 61938 } 61939 if (!(xAxis.onBand && yAxis.onBand)) { 61940 throw new Error('Heatmap on cartesian must have two axes with boundaryGap true'); 61941 } 61942 } 61943 61944 width = xAxis.getBandWidth(); 61945 height = yAxis.getBandWidth(); 61946 } 61947 61948 var group = this.group; 61949 var data = seriesModel.getData(); 61950 61951 var itemStyleQuery = 'itemStyle'; 61952 var hoverItemStyleQuery = 'emphasis.itemStyle'; 61953 var labelQuery = 'label'; 61954 var hoverLabelQuery = 'emphasis.label'; 61955 var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']); 61956 var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle(); 61957 var labelModel = seriesModel.getModel(labelQuery); 61958 var hoverLabelModel = seriesModel.getModel(hoverLabelQuery); 61959 var coordSysType = coordSys.type; 61960 61961 61962 var dataDims = coordSysType === 'cartesian2d' 61963 ? [ 61964 data.mapDimension('x'), 61965 data.mapDimension('y'), 61966 data.mapDimension('value') 61967 ] 61968 : [ 61969 data.mapDimension('time'), 61970 data.mapDimension('value') 61971 ]; 61972 61973 for (var idx = start; idx < end; idx++) { 61974 var rect; 61975 61976 if (coordSysType === 'cartesian2d') { 61977 // Ignore empty data 61978 if (isNaN(data.get(dataDims[2], idx))) { 61979 continue; 61980 } 61981 61982 var point = coordSys.dataToPoint([ 61983 data.get(dataDims[0], idx), 61984 data.get(dataDims[1], idx) 61985 ]); 61986 61987 rect = new Rect({ 61988 shape: { 61989 x: point[0] - width / 2, 61990 y: point[1] - height / 2, 61991 width: width, 61992 height: height 61993 }, 61994 style: { 61995 fill: data.getItemVisual(idx, 'color'), 61996 opacity: data.getItemVisual(idx, 'opacity') 61997 } 61998 }); 61999 } 62000 else { 62001 // Ignore empty data 62002 if (isNaN(data.get(dataDims[1], idx))) { 62003 continue; 62004 } 62005 62006 rect = new Rect({ 62007 z2: 1, 62008 shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape, 62009 style: { 62010 fill: data.getItemVisual(idx, 'color'), 62011 opacity: data.getItemVisual(idx, 'opacity') 62012 } 62013 }); 62014 } 62015 62016 var itemModel = data.getItemModel(idx); 62017 62018 // Optimization for large datset 62019 if (data.hasItemOption) { 62020 style = itemModel.getModel(itemStyleQuery).getItemStyle(['color']); 62021 hoverStl = itemModel.getModel(hoverItemStyleQuery).getItemStyle(); 62022 labelModel = itemModel.getModel(labelQuery); 62023 hoverLabelModel = itemModel.getModel(hoverLabelQuery); 62024 } 62025 62026 var rawValue = seriesModel.getRawValue(idx); 62027 var defaultText = '-'; 62028 if (rawValue && rawValue[2] != null) { 62029 defaultText = rawValue[2]; 62030 } 62031 62032 setLabelStyle( 62033 style, hoverStl, labelModel, hoverLabelModel, 62034 { 62035 labelFetcher: seriesModel,
62036 labelDataIndex: idx, 62037 defaultText: defaultText, 62038 isRectText: true 62039 } 62040 ); 62041 62042 rect.setStyle(style); 62043 setHoverStyle(rect, data.hasItemOption ? hoverStl : extend({}, hoverStl)); 62044 62045 rect.incremental = incremental; 62046 // PENDING 62047 if (incremental) { 62048 // Rect must use hover layer if it's incremental. 62049 rect.useHoverLayer = true; 62050 } 62051 62052 group.add(rect); 62053 data.setItemGraphicEl(idx, rect); 62054 } 62055 }, 62056 62057 _renderOnGeo: function (geo, seriesModel, visualMapModel, api) { 62058 var inRangeVisuals = visualMapModel.targetVisuals.inRange; 62059 var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; 62060 // if (!visualMapping) { 62061 // throw new Error('Data range must have color visuals'); 62062 // } 62063 62064 var data = seriesModel.getData(); 62065 var hmLayer = this._hmLayer || (this._hmLayer || new Heatmap()); 62066 hmLayer.blurSize = seriesModel.get('blurSize'); 62067 hmLayer.pointSize = seriesModel.get('pointSize'); 62068 hmLayer.minOpacity = seriesModel.get('minOpacity'); 62069 hmLayer.maxOpacity = seriesModel.get('maxOpacity'); 62070 62071 var rect = geo.getViewRect().clone(); 62072 var roamTransform = geo.getRoamTransform(); 62073 rect.applyTransform(roamTransform); 62074 62075 // Clamp on viewport 62076 var x = Math.max(rect.x, 0); 62077 var y = Math.max(rect.y, 0); 62078 var x2 = Math.min(rect.width + rect.x, api.getWidth()); 62079 var y2 = Math.min(rect.height + rect.y, api.getHeight()); 62080 var width = x2 - x; 62081 var height = y2 - y; 62082 62083 var dims = [ 62084 data.mapDimension('lng'), 62085 data.mapDimension('lat'), 62086 data.mapDimension('value') 62087 ]; 62088 62089 var points = data.mapArray(dims, function (lng, lat, value) { 62090 var pt = geo.dataToPoint([lng, lat]); 62091 pt[0] -= x; 62092 pt[1] -= y; 62093 pt.push(value); 62094 return pt; 62095 }); 62096 62097 var dataExtent = visualMapModel.getExtent(); 62098 var isInRange = visualMapModel.type === 'visualMap.continuous' 62099 ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) 62100 : getIsInPiecewiseRange( 62101 dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected 62102 ); 62103 62104 hmLayer.update( 62105 points, width, height, 62106 inRangeVisuals.color.getNormalizer(), 62107 { 62108 inRange: inRangeVisuals.color.getColorMapper(), 62109 outOfRange: outOfRangeVisuals.color.getColorMapper() 62110 }, 62111 isInRange 62112 ); 62113 var img = new ZImage({ 62114 style: { 62115 width: width, 62116 height: height, 62117 x: x, 62118 y: y, 62119 image: hmLayer.canvas 62120 }, 62121 silent: true 62122 }); 62123 this.group.add(img); 62124 }, 62125 62126 dispose: function () {} 62127 }); 62128 62129 /* 62130* Licensed to the Apache Software Foundation (ASF) under one 62131* or more contributor license agreements. See the NOTICE file 62132* distributed with this work for additional information 62133* regarding copyright ownership. The ASF licenses this file 62134* to you under the Apache License, Version 2.0 (the 62135* "License"); you may not use this file except in compliance 62136* with the License. You may obtain a copy of the License at 62137* 62138* http://www.apache.org/licenses/LICENSE-2.0 62139* 62140* Unless required by applicable law or agreed to in writing, 62141* software distributed under the License is distributed on an 62142* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 62143* KIND, either express or implied. See the License for the 62144* specific language governing permissions and limitations 62145* under the License. 62146*/ 62147 62148 /* 62149* Licensed to the Apache Software Foundation (ASF) under one 62150* or more contributor license agreements. See the NOTICE file 62151* distributed with this work for additional information 62152* regarding copyright ownership. The ASF licenses this file 62153* to you under the Apache License, Version 2.0 (the 62154* "License");
62154 you may not use this file except in compliance 62155* with the License. You may obtain a copy of the License at 62156* 62157* http://www.apache.org/licenses/LICENSE-2.0 62158* 62159* Unless required by applicable law or agreed to in writing, 62160* software distributed under the License is distributed on an 62161* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 62162* KIND, either express or implied. See the License for the 62163* specific language governing permissions and limitations 62164* under the License. 62165*/ 62166 62167 var PictorialBarSeries = BaseBarSeries.extend({ 62168 62169 type: 'series.pictorialBar', 62170 62171 dependencies: ['grid'], 62172 62173 defaultOption: { 62174 symbol: 'circle', // Customized bar shape 62175 symbolSize: null, // Can be ['100%', '100%'], null means auto. 62176 symbolRotate: null, 62177 62178 symbolPosition: null, // 'start' or 'end' or 'center', null means auto. 62179 symbolOffset: null, 62180 symbolMargin: null, // start margin and end margin. Can be a number or a percent string. 62181 // Auto margin by defualt. 62182 symbolRepeat: false, // false/null/undefined, means no repeat. 62183 // Can be true, means auto calculate repeat times and cut by data. 62184 // Can be a number, specifies repeat times, and do not cut by data. 62185 // Can be 'fixed', means auto calculate repeat times but do not cut by data. 62186 symbolRepeatDirection: 'end', // 'end' means from 'start' to 'end'. 62187 62188 symbolClip: false, 62189 symbolBoundingData: null, // Can be 60 or -40 or [-40, 60] 62190 symbolPatternSize: 400, // 400 * 400 px 62191 62192 barGap: '-100%', // In most case, overlap is needed. 62193 62194 // z can be set in data item, which is z2 actually. 62195 62196 // Disable progressive 62197 progressive: 0, 62198 hoverAnimation: false // Open only when needed. 62199 }, 62200 62201 getInitialData: function (option) { 62202 // Disable stack. 62203 option.stack = null; 62204 return PictorialBarSeries.superApply(this, 'getInitialData', arguments); 62205 } 62206 }); 62207 62208 /* 62209* Licensed to the Apache Software Foundation (ASF) under one 62210* or more contributor license agreements. See the NOTICE file 62211* distributed with this work for additional information 62212* regarding copyright ownership. The ASF licenses this file 62213* to you under the Apache License, Version 2.0 (the 62214* "License"); you may not use this file except in compliance 62215* with the License. You may obtain a copy of the License at 62216* 62217* http://www.apache.org/licenses/LICENSE-2.0 62218* 62219* Unless required by applicable law or agreed to in writing, 62220* software distributed under the License is distributed on an 62221* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 62222* KIND, either express or implied. See the License for the 62223* specific language governing permissions and limitations 62224* under the License. 62225*/ 62226 62227 var BAR_BORDER_WIDTH_QUERY$1 = ['itemStyle', 'borderWidth']; 62228 62229// index: +isHorizontal 62230 var LAYOUT_ATTRS = [ 62231 {xy: 'x', wh: 'width', index: 0, posDesc: ['left', 'right']}, 62232 {xy: 'y', wh: 'height', index: 1, posDesc: ['top', 'bottom']} 62233 ]; 62234 62235 var pathForLineWidth = new Circle(); 62236 62237 var BarView$1 = extendChartView({ 62238 62239 type: 'pictorialBar', 62240 62241 render: function (seriesModel, ecModel, api) { 62242 var group = this.group; 62243 var data = seriesModel.getData(); 62244 var oldData = this._data; 62245 62246 var cartesian = seriesModel.coordinateSystem; 62247 var baseAxis = cartesian.getBaseAxis(); 62248 var isHorizontal = !!baseAxis.isHorizontal(); 62249 var coordSysRect = cartesian.grid.getRect(); 62250 62251 var opt = { 62252 ecSize: {width: api.getWidth(), height: api.getHeight()}, 62253 seriesModel: seriesModel, 62254 coordSys: cartesian, 62255 coordSysExtent: [ 62256 [coordSysRect.x, coordSysRect.x + coordSysRect.width], 62257 [coordSysRect.y, coordSysRect.y + coordSysRect.height] 62258 ], 62259 isHorizontal: isHorizontal, 62260 valueDim: LAYOUT_ATTRS[+isHorizontal], 62261 categoryDim: LAYOUT_ATTRS[1 - isHorizontal] 62262 }; 62263 62264 data.diff(oldData) 62265 .add(function (dataIndex) { 62266 if (!data.hasValue(dataIndex)) { 62267 return; 62268 } 62269 62270 var itemModel = getItemModel(data, dataIndex); 62271 var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt); 62272 62273 var bar = createBar(data, opt, symbolMeta); 62274 62275 data.setItemGraphicEl(dataIndex, bar); 62276 group.add(bar); 62277 62278 updateCommon$1(bar, opt, symbolMeta); 62279 }) 62280 .update(function (newIndex, oldIndex) { 62281 var bar = oldData.getItemGraphicEl(oldIndex); 62282 62283 if (!data.hasValue(newIndex)) { 62284 group.remove(bar); 62285 return; 62286 } 62287 62288 var itemModel = getItemModel(data, newIndex); 62289 var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt); 62290 62291 var pictorialShapeStr = getShapeStr(data, symbolMeta); 62292 if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) { 62293 group.remove(bar); 62294 data.setItemGraphicEl(newIndex, null); 62295 bar = null; 62296 } 62297 62298 if (bar) { 62299 updateBar(bar, opt, symbolMeta); 62300 } 62301 else { 62302 bar = createBar(data, opt, symbolMeta, true); 62303 } 62304 62305 data.setItemGraphicEl(newIndex, bar); 62306 bar.__pictorialSymbolMeta = symbolMeta; 62307 // Add back 62308 group.add(bar); 62309 62310 updateCommon$1(bar, opt, symbolMeta); 62311 }) 62312 .remove(function (dataIndex) { 62313 var bar = oldData.getItemGraphicEl(dataIndex); 62314 bar && removeBar(oldData, dataIndex, bar.__pictorialSymbolMeta.animationModel, bar); 62315 }) 62316 .execute(); 62317 62318 this._data = data; 62319 62320 return this.group; 62321 }, 62322 62323 dispose: noop, 62324 62325 remove: function (ecModel, api) { 62326 var group = this.group; 62327 var data = this._data; 62328 if (ecModel.get('animation')) { 62329 if (data) { 62330 data.eachItemGraphicEl(function (bar) { 62331 removeBar(data, bar.dataIndex, ecModel, bar); 62332 }); 62333 } 62334 } 62335 else { 62336 group.removeAll(); 62337 } 62338 } 62339 });
62340 62341 62342// Set or calculate default value about symbol, and calculate layout info. 62343 function getSymbolMeta(data, dataIndex, itemModel, opt) { 62344 var layout = data.getItemLayout(dataIndex); 62345 var symbolRepeat = itemModel.get('symbolRepeat'); 62346 var symbolClip = itemModel.get('symbolClip'); 62347 var symbolPosition = itemModel.get('symbolPosition') || 'start'; 62348 var symbolRotate = itemModel.get('symbolRotate'); 62349 var rotation = (symbolRotate || 0) * Math.PI / 180 || 0; 62350 var symbolPatternSize = itemModel.get('symbolPatternSize') || 2; 62351 var isAnimationEnabled = itemModel.isAnimationEnabled(); 62352 62353 var symbolMeta = { 62354 dataIndex: dataIndex, 62355 layout: layout, 62356 itemModel: itemModel, 62357 symbolType: data.getItemVisual(dataIndex, 'symbol') || 'circle', 62358 color: data.getItemVisual(dataIndex, 'color'), 62359 symbolClip: symbolClip, 62360 symbolRepeat: symbolRepeat, 62361 symbolRepeatDirection: itemModel.get('symbolRepeatDirection'), 62362 symbolPatternSize: symbolPatternSize, 62363 rotation: rotation, 62364 animationModel: isAnimationEnabled ? itemModel : null, 62365 hoverAnimation: isAnimationEnabled && itemModel.get('hoverAnimation'), 62366 z2: itemModel.getShallow('z', true) || 0 62367 }; 62368 62369 prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta); 62370 62371 prepareSymbolSize( 62372 data, dataIndex, layout, symbolRepeat, symbolClip, symbolMeta.boundingLength, 62373 symbolMeta.pxSign, symbolPatternSize, opt, symbolMeta 62374 ); 62375 62376 prepareLineWidth(itemModel, symbolMeta.symbolScale, rotation, opt, symbolMeta); 62377 62378 var symbolSize = symbolMeta.symbolSize; 62379 var symbolOffset = itemModel.get('symbolOffset'); 62380 if (isArray(symbolOffset)) { 62381 symbolOffset = [ 62382 parsePercent$1(symbolOffset[0], symbolSize[0]), 62383 parsePercent$1(symbolOffset[1], symbolSize[1]) 62384 ]; 62385 } 62386 62387 prepareLayoutInfo( 62388 itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, 62389 symbolPosition, symbolMeta.valueLineWidth, symbolMeta.boundingLength, symbolMeta.repeatCutLength, 62390 opt, symbolMeta 62391 ); 62392 62393 return symbolMeta; 62394 } 62395 62396// bar length can be negative. 62397 function prepareBarLength(itemModel, symbolRepeat, layout, opt, output) { 62398 var valueDim = opt.valueDim; 62399 var symbolBoundingData = itemModel.get('symbolBoundingData'); 62400 var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis()); 62401 var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)); 62402 var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0); 62403 var boundingLength; 62404 62405 if (isArray(symbolBoundingData)) { 62406 var symbolBoundingExtent = [ 62407 convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx, 62408 convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx 62409 ]; 62410 symbolBoundingExtent[1] < symbolBoundingExtent[0] && (symbolBoundingExtent.reverse()); 62411 boundingLength = symbolBoundingExtent[pxSignIdx]; 62412 } 62413 else if (symbolBoundingData != null) { 62414 boundingLength = convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx; 62415 } 62416 else if (symbolRepeat) { 62417 boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx; 62418 } 62419 else { 62420 boundingLength = layout[valueDim.wh]; 62421 } 62422 62423 output.boundingLength = boundingLength; 62424 62425 if (symbolRepeat) { 62426 output.repeatCutLength = layout[valueDim.wh]; 62427 } 62428 62429 output.pxSign = boundingLength > 0 ? 1 : boundingLength < 0 ? -1 : 0; 62430 } 62431 62432 function convertToCoordOnAxis(axis, value) { 62433 return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value))); 62434 } 62435 62436// Support ['100%', '100%'] 62437 function prepareSymbolSize( 62438 data, dataIndex, layout, symbolRepeat, symbolClip, boundingLength, 62439 pxSign, symbolPatternSize, opt, output 62440 ) { 62441 var valueDim = opt.valueDim; 62442 var categoryDim = opt.categoryDim; 62443 var categorySize = Math.abs(layout[categoryDim.wh]); 62444 62445 var symbolSize = data.getItemVisual(dataIndex, 'symbolSize'); 62446 if (isArray(symbolSize)) { 62447 symbolSize = symbolSize.slice(); 62448 } 62449 else { 62450 if (symbolSize == null) { 62451 symbolSize = '100%'; 62452 } 62453 symbolSize = [symbolSize, symbolSize]; 62454 } 62455 62456 // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is 62457 // to complicated to calculate real percent value if considering scaled lineWidth. 62458 // So the actual size will bigger than layout size if lineWidth is bigger than zero, 62459 // which can be tolerated in pictorial chart. 62460 62461 symbolSize[categoryDim.index] = parsePercent$1( 62462 symbolSize[categoryDim.index], 62463 categorySize 62464 ); 62465 symbolSize[valueDim.index] = parsePercent$1( 62466 symbolSize[valueDim.index], 62467 symbolRepeat ? categorySize : Math.abs(boundingLength) 62468 ); 62469 62470 output.symbolSize = symbolSize; 62471 62472 // If x or y is less than zero, show reversed shape. 62473 var symbolScale = output.symbolScale = [ 62474 symbolSize[0] / symbolPatternSize, 62475 symbolSize[1] / symbolPatternSize 62476 ]; 62477 // Follow convention, 'right' and 'top' is the normal scale. 62478 symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign; 62479 } 62480 62481 function prepareLineWidth(itemModel, symbolScale, rotation, opt, output) { 62482 // In symbols are drawn with scale, so do not need to care about the case that width 62483 // or height are too small. But symbol use strokeNoScale, where acture lineWidth should 62484 // be calculated. 62485 var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY$1) || 0; 62486 62487 if (valueLineWidth) { 62488 pathForLineWidth.attr({ 62489 scale: symbolScale.slice(), 62490 rotation: rotation 62491 }); 62492 pathForLineWidth.updateTransform(); 62493 valueLineWidth /= pathForLineWidth.getLineScale(); 62494 valueLineWidth *= symbolScale[opt.valueDim.index]; 62495 } 62496 62497 output.valueLineWidth = valueLineWidth; 62498 } 62499 62500 function prepareLayoutInfo( 62501 itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, 62502 symbolPosition, valueLineWidth, boundingLength, repeatCutLength, opt, output 62503 ) { 62504 var categoryDim = opt.categoryDim; 62505 var valueDim = opt.valueDim; 62506 var pxSign = output.pxSign; 62507 62508 var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0); 62509 var pathLen = unitLength; 62510 62511 // Note: rotation will not effect the layout of symbols, because user may 62512 // want symbols to rotate on its center, which should not be translated 62513 // when rotating. 62514 62515 if (symbolRepeat) { 62516 var absBoundingLength = Math.abs(boundingLength); 62517 62518 var symbolMargin = retrieve(itemModel.get('symbolMargin'), '15%') + ''; 62519 var hasEndGap = false; 62520 if (symbolMargin.lastIndexOf('!') === symbolMargin.length - 1) { 62521 hasEndGap = true; 62522 symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1); 62523 } 62524 symbolMargin = parsePercent$1(symbolMargin, symbolSize[valueDim.index]); 62525 62526 var uLenWithMargin = Math.max(unitLength + symbolMargin * 2, 0); 62527 62528 // When symbol margin is less than 0, margin at both ends will be subtracted 62529 // to ensure that all of the symbols will not be overflow the given area. 62530 var endFix = hasEndGap ? 0 : symbolMargin * 2; 62531 62532 // Both final repeatTimes and final symbolMargin area calculated based on 62533 // boundingLength. 62534 var repeatSpecified = isNumeric(symbolRepeat); 62535 var repeatTimes = repeatSpecified 62536 ? symbolRepeat 62537 : toIntTimes((absBoundingLength + endFix) / uLenWithMargin); 62538 62539 // Adjust calculate margin, to ensure each symbol is displayed 62540 // entirely in the given layout area. 62541 var mDiff = absBoundingLength - repeatTimes * unitLength; 62542 symbolMargin = mDiff / 2 / (hasEndGap ? repeatTimes : repeatTimes - 1); 62543 uLenWithMargin = unitLength + symbolMargin * 2; 62544 endFix = hasEndGap ? 0 : symbolMargin * 2; 62545 62546 // Update repeatTimes when not all symbol will be shown. 62547 if (!repeatSpecified && symbolRepeat !== 'fixed') { 62548 repeatTimes = repeatCutLength 62549 ? toIntTimes((Math.abs(repeatCutLength) + endFix) / uLenWithMargin) 62550 : 0; 62551 } 62552 62553 pathLen = repeatTimes * uLenWithMargin - endFix;
62554 output.repeatTimes = repeatTimes; 62555 output.symbolMargin = symbolMargin; 62556 } 62557 62558 var sizeFix = pxSign * (pathLen / 2); 62559 var pathPosition = output.pathPosition = []; 62560 pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2; 62561 pathPosition[valueDim.index] = symbolPosition === 'start' 62562 ? sizeFix 62563 : symbolPosition === 'end' 62564 ? boundingLength - sizeFix 62565 : boundingLength / 2; // 'center' 62566 if (symbolOffset) { 62567 pathPosition[0] += symbolOffset[0]; 62568 pathPosition[1] += symbolOffset[1]; 62569 } 62570 62571 var bundlePosition = output.bundlePosition = []; 62572 bundlePosition[categoryDim.index] = layout[categoryDim.xy]; 62573 bundlePosition[valueDim.index] = layout[valueDim.xy]; 62574 62575 var barRectShape = output.barRectShape = extend({}, layout); 62576 barRectShape[valueDim.wh] = pxSign * Math.max( 62577 Math.abs(layout[valueDim.wh]), Math.abs(pathPosition[valueDim.index] + sizeFix) 62578 ); 62579 barRectShape[categoryDim.wh] = layout[categoryDim.wh]; 62580 62581 var clipShape = output.clipShape = {}; 62582 // Consider that symbol may be overflow layout rect. 62583 clipShape[categoryDim.xy] = -layout[categoryDim.xy]; 62584 clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh]; 62585 clipShape[valueDim.xy] = 0; 62586 clipShape[valueDim.wh] = layout[valueDim.wh]; 62587 } 62588 62589 function createPath(symbolMeta) { 62590 var symbolPatternSize = symbolMeta.symbolPatternSize; 62591 var path = createSymbol( 62592 // Consider texture img, make a big size. 62593 symbolMeta.symbolType, 62594 -symbolPatternSize / 2, 62595 -symbolPatternSize / 2, 62596 symbolPatternSize, 62597 symbolPatternSize, 62598 symbolMeta.color 62599 ); 62600 path.attr({ 62601 culling: true 62602 }); 62603 path.type !== 'image' && path.setStyle({ 62604 strokeNoScale: true 62605 }); 62606 62607 return path; 62608 } 62609 62610 function createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) { 62611 var bundle = bar.__pictorialBundle; 62612 var symbolSize = symbolMeta.symbolSize; 62613 var valueLineWidth = symbolMeta.valueLineWidth; 62614 var pathPosition = symbolMeta.pathPosition; 62615 var valueDim = opt.valueDim; 62616 var repeatTimes = symbolMeta.repeatTimes || 0; 62617 62618 var index = 0; 62619 var unit = symbolSize[opt.valueDim.index] + valueLineWidth + symbolMeta.symbolMargin * 2; 62620 62621 eachPath(bar, function (path) { 62622 path.__pictorialAnimationIndex = index; 62623 path.__pictorialRepeatTimes = repeatTimes; 62624 if (index < repeatTimes) { 62625 updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate); 62626 } 62627 else { 62628 updateAttr(path, null, {scale: [0, 0]}, symbolMeta, isUpdate, function () { 62629 bundle.remove(path); 62630 }); 62631 } 62632 62633 updateHoverAnimation(path, symbolMeta); 62634 62635 index++; 62636 }); 62637 62638 for (; index < repeatTimes; index++) { 62639 var path = createPath(symbolMeta); 62640 path.__pictorialAnimationIndex = index; 62641 path.__pictorialRepeatTimes = repeatTimes; 62642 bundle.add(path); 62643 62644 var target = makeTarget(index); 62645 62646 updateAttr( 62647 path, 62648 { 62649 position: target.position, 62650 scale: [0, 0] 62651 }, 62652 { 62653 scale: target.scale, 62654 rotation: target.rotation 62655 }, 62656 symbolMeta, 62657 isUpdate 62658 ); 62659 62660 // FIXME 62661 // If all emphasis/normal through action. 62662 path 62663 .on('mouseover', onMouseOver) 62664 .on('mouseout', onMouseOut); 62665 62666 updateHoverAnimation(path, symbolMeta); 62667 } 62668 62669 function makeTarget(index) { 62670 var position = pathPosition.slice(); 62671 // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index 62672 // Otherwise: i = index; 62673 var pxSign = symbolMeta.pxSign; 62674 var i = index; 62675 if (symbolMeta.symbolRepeatDirection === 'start' ? pxSign > 0 : pxSign < 0) { 62676 i = repeatTimes - 1 - index; 62677 } 62678 position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index]; 62679 62680 return { 62681 position: position, 62682 scale: symbolMeta.symbolScale.slice(), 62683 rotation: symbolMeta.rotation 62684 }; 62685 } 62686 62687 function onMouseOver() { 62688 eachPath(bar, function (path) { 62689 path.trigger('emphasis'); 62690 }); 62691 } 62692 62693 function onMouseOut() { 62694 eachPath(bar, function (path) { 62695 path.trigger('normal'); 62696 }); 62697 } 62698 } 62699 62700 function createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) { 62701 var bundle = bar.__pictorialBundle; 62702 var mainPath = bar.__pictorialMainPath; 62703 62704 if (!mainPath) { 62705 mainPath = bar.__pictorialMainPath = createPath(symbolMeta); 62706 bundle.add(mainPath); 62707 62708 updateAttr( 62709 mainPath, 62710 { 62711 position: symbolMeta.pathPosition.slice(), 62712 scale: [0, 0], 62713 rotation: symbolMeta.rotation 62714 }, 62715 { 62716 scale: symbolMeta.symbolScale.slice() 62717 }, 62718 symbolMeta, 62719 isUpdate 62720 ); 62721 62722 mainPath 62723 .on('mouseover', onMouseOver) 62724 .on('mouseout', onMouseOut); 62725 } 62726 else { 62727 updateAttr( 62728 mainPath, 62729 null, 62730 { 62731 position: symbolMeta.pathPosition.slice(), 62732 scale: symbolMeta.symbolScale.slice(), 62733 rotation: symbolMeta.rotation 62734 }, 62735 symbolMeta, 62736 isUpdate 62737 ); 62738 } 62739 62740 updateHoverAnimation(mainPath, symbolMeta); 62741 62742 function onMouseOver() { 62743 this.trigger('emphasis'); 62744 } 62745 62746 function onMouseOut() { 62747 this.trigger('normal'); 62748 } 62749 } 62750 62751// bar rect is used for label. 62752 function createOrUpdateBarRect(bar, symbolMeta, isUpdate) { 62753 var rectShape = extend({}, symbolMeta.barRectShape); 62754 62755 var barRect = bar.__pictorialBarRect; 62756 if (!barRect) { 62757 barRect = bar.__pictorialBarRect = new Rect({ 62758 z2: 2, 62759 shape: rectShape, 62760 silent: true, 62761 style: { 62762 stroke: 'transparent', 62763 fill: 'transparent', 62764 lineWidth: 0 62765 } 62766 }); 62767 62768 bar.add(barRect); 62769 } 62770 else { 62771 updateAttr(barRect, null, {shape: rectShape}, symbolMeta, isUpdate); 62772 } 62773 } 62774 62775 function createOrUpdateClip(bar, opt, symbolMeta, isUpdate) { 62776 // If not clip, symbol will be remove and rebuilt.
62777 if (symbolMeta.symbolClip) { 62778 var clipPath = bar.__pictorialClipPath; 62779 var clipShape = extend({}, symbolMeta.clipShape); 62780 var valueDim = opt.valueDim; 62781 var animationModel = symbolMeta.animationModel; 62782 var dataIndex = symbolMeta.dataIndex; 62783 62784 if (clipPath) { 62785 updateProps( 62786 clipPath, {shape: clipShape}, animationModel, dataIndex 62787 ); 62788 } 62789 else { 62790 clipShape[valueDim.wh] = 0; 62791 clipPath = new Rect({shape: clipShape}); 62792 bar.__pictorialBundle.setClipPath(clipPath); 62793 bar.__pictorialClipPath = clipPath; 62794 62795 var target = {}; 62796 target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh]; 62797 62798 graphic[isUpdate ? 'updateProps' : 'initProps']( 62799 clipPath, {shape: target}, animationModel, dataIndex 62800 ); 62801 } 62802 } 62803 } 62804 62805 function getItemModel(data, dataIndex) { 62806 var itemModel = data.getItemModel(dataIndex); 62807 itemModel.getAnimationDelayParams = getAnimationDelayParams; 62808 itemModel.isAnimationEnabled = isAnimationEnabled; 62809 return itemModel; 62810 } 62811 62812 function getAnimationDelayParams(path) { 62813 // The order is the same as the z-order, see `symbolRepeatDiretion`. 62814 return { 62815 index: path.__pictorialAnimationIndex, 62816 count: path.__pictorialRepeatTimes 62817 }; 62818 } 62819 62820 function isAnimationEnabled() { 62821 // `animation` prop can be set on itemModel in pictorial bar chart. 62822 return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation'); 62823 } 62824 62825 function updateHoverAnimation(path, symbolMeta) { 62826 path.off('emphasis').off('normal'); 62827 62828 var scale = symbolMeta.symbolScale.slice(); 62829 62830 symbolMeta.hoverAnimation && path 62831 .on('emphasis', function() { 62832 this.animateTo({ 62833 scale: [scale[0] * 1.1, scale[1] * 1.1] 62834 }, 400, 'elasticOut'); 62835 }) 62836 .on('normal', function() { 62837 this.animateTo({ 62838 scale: scale.slice() 62839 }, 400, 'elasticOut'); 62840 }); 62841 } 62842 62843 function createBar(data, opt, symbolMeta, isUpdate) { 62844 // bar is the main element for each data. 62845 var bar = new Group(); 62846 // bundle is used for location and clip. 62847 var bundle = new Group(); 62848 bar.add(bundle); 62849 bar.__pictorialBundle = bundle; 62850 bundle.attr('position', symbolMeta.bundlePosition.slice()); 62851 62852 if (symbolMeta.symbolRepeat) { 62853 createOrUpdateRepeatSymbols(bar, opt, symbolMeta); 62854 } 62855 else { 62856 createOrUpdateSingleSymbol(bar, opt, symbolMeta); 62857 } 62858 62859 createOrUpdateBarRect(bar, symbolMeta, isUpdate); 62860 62861 createOrUpdateClip(bar, opt, symbolMeta, isUpdate); 62862 62863 bar.__pictorialShapeStr = getShapeStr(data, symbolMeta); 62864 bar.__pictorialSymbolMeta = symbolMeta; 62865 62866 return bar; 62867 } 62868 62869 function updateBar(bar, opt, symbolMeta) { 62870 var animationModel = symbolMeta.animationModel; 62871 var dataIndex = symbolMeta.dataIndex; 62872 var bundle = bar.__pictorialBundle; 62873 62874 updateProps( 62875 bundle, {position: symbolMeta.bundlePosition.slice()}, animationModel, dataIndex 62876 ); 62877 62878 if (symbolMeta.symbolRepeat) { 62879 createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true); 62880 } 62881 else { 62882 createOrUpdateSingleSymbol(bar, opt, symbolMeta, true); 62883 } 62884 62885 createOrUpdateBarRect(bar, symbolMeta, true); 62886 62887 createOrUpdateClip(bar, opt, symbolMeta, true); 62888 } 62889 62890 function removeBar(data, dataIndex, animationModel, bar) { 62891 // Not show text when animating 62892 var labelRect = bar.__pictorialBarRect; 62893 labelRect && (labelRect.style.text = null); 62894 62895 var pathes = []; 62896 eachPath(bar, function (path) { 62897 pathes.push(path);
62898 }); 62899 bar.__pictorialMainPath && pathes.push(bar.__pictorialMainPath); 62900 62901 // I do not find proper remove animation for clip yet. 62902 bar.__pictorialClipPath && (animationModel = null); 62903 62904 each$1(pathes, function (path) { 62905 updateProps( 62906 path, {scale: [0, 0]}, animationModel, dataIndex, 62907 function () { 62908 bar.parent && bar.parent.remove(bar); 62909 } 62910 ); 62911 }); 62912 62913 data.setItemGraphicEl(dataIndex, null); 62914 } 62915 62916 function getShapeStr(data, symbolMeta) { 62917 return [ 62918 data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none', 62919 !!symbolMeta.symbolRepeat, 62920 !!symbolMeta.symbolClip 62921 ].join(':'); 62922 } 62923 62924 function eachPath(bar, cb, context) { 62925 // Do not use Group#eachChild, because it do not support remove. 62926 each$1(bar.__pictorialBundle.children(), function (el) { 62927 el !== bar.__pictorialBarRect && cb.call(context, el); 62928 }); 62929 } 62930 62931 function updateAttr(el, immediateAttrs, animationAttrs, symbolMeta, isUpdate, cb) { 62932 immediateAttrs && el.attr(immediateAttrs); 62933 // when symbolCip used, only clip path has init animation, otherwise it would be weird effect. 62934 if (symbolMeta.symbolClip && !isUpdate) { 62935 animationAttrs && el.attr(animationAttrs); 62936 } 62937 else { 62938 animationAttrs && graphic[isUpdate ? 'updateProps' : 'initProps']( 62939 el, animationAttrs, symbolMeta.animationModel, symbolMeta.dataIndex, cb 62940 ); 62941 } 62942 } 62943 62944 function updateCommon$1(bar, opt, symbolMeta) { 62945 var color = symbolMeta.color; 62946 var dataIndex = symbolMeta.dataIndex; 62947 var itemModel = symbolMeta.itemModel; 62948 // Color must be excluded. 62949 // Because symbol provide setColor individually to set fill and stroke 62950 var normalStyle = itemModel.getModel('itemStyle').getItemStyle(['color']); 62951 var hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); 62952 var cursorStyle = itemModel.getShallow('cursor'); 62953 62954 eachPath(bar, function (path) { 62955 // PENDING setColor should be before setStyle!!! 62956 path.setColor(color); 62957 path.setStyle(defaults( 62958 { 62959 fill: color, 62960 opacity: symbolMeta.opacity 62961 }, 62962 normalStyle 62963 )); 62964 setHoverStyle(path, hoverStyle); 62965 62966 cursorStyle && (path.cursor = cursorStyle); 62967 path.z2 = symbolMeta.z2; 62968 }); 62969 62970 var barRectHoverStyle = {}; 62971 var barPositionOutside = opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)]; 62972 var barRect = bar.__pictorialBarRect; 62973 62974 setLabel( 62975 barRect.style, barRectHoverStyle, itemModel, 62976 color, opt.seriesModel, dataIndex, barPositionOutside 62977 ); 62978 62979 setHoverStyle(barRect, barRectHoverStyle); 62980 } 62981 62982 function toIntTimes(times) { 62983 var roundedTimes = Math.round(times); 62984 // Escapse accurate error 62985 return Math.abs(times - roundedTimes) < 1e-4 62986 ? roundedTimes 62987 : Math.ceil(times); 62988 } 62989 62990 /* 62991* Licensed to the Apache Software Foundation (ASF) under one 62992* or more contributor license agreements. See the NOTICE file 62993* distributed with this work for additional information 62994* regarding copyright ownership. The ASF licenses this file 62995* to you under the Apache License, Version 2.0 (the 62996* "License"); you may not use this file except in compliance 62997* with the License. You may obtain a copy of the License at 62998* 62999* http://www.apache.org/licenses/LICENSE-2.0 63000* 63001* Unless required by applicable law or agreed to in writing, 63002* software distributed under the License is distributed on an 63003* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63004* KIND, either express or implied. See the License for the 63005* specific language governing permissions and limitations 63006* under the License. 63007*/ 63008 63009// In case developer forget to include grid component 63010 registerLayout(curry( 63011 layout, 'pictorialBar' 63012 )); 63013 registerVisual(visualSymbol('pictorialBar', 'roundRect')); 63014 63015 /* 63016* Licensed to the Apache Software Foundation (ASF) under one 63017* or more contributor license agreements. See the NOTICE file 63018* distributed with this work for additional information 63019* regarding copyright ownership. The ASF licenses this file 63020* to you under the Apache License, Version 2.0 (the 63021* "License");
63021 you may not use this file except in compliance 63022* with the License. You may obtain a copy of the License at 63023* 63024* http://www.apache.org/licenses/LICENSE-2.0 63025* 63026* Unless required by applicable law or agreed to in writing, 63027* software distributed under the License is distributed on an 63028* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63029* KIND, either express or implied. See the License for the 63030* specific language governing permissions and limitations 63031* under the License. 63032*/ 63033 63034 /** 63035 * @constructor module:echarts/coord/single/SingleAxis 63036 * @extends {module:echarts/coord/Axis} 63037 * @param {string} dim 63038 * @param {*} scale 63039 * @param {Array.<number>} coordExtent 63040 * @param {string} axisType 63041 * @param {string} position 63042 */ 63043 var SingleAxis = function (dim, scale, coordExtent, axisType, position) { 63044 63045 Axis.call(this, dim, scale, coordExtent); 63046 63047 /** 63048 * Axis type 63049 * - 'category' 63050 * - 'value' 63051 * - 'time' 63052 * - 'log' 63053 * @type {string} 63054 */ 63055 this.type = axisType || 'value'; 63056 63057 /** 63058 * Axis position 63059 * - 'top' 63060 * - 'bottom' 63061 * - 'left' 63062 * - 'right' 63063 * @type {string} 63064 */ 63065 this.position = position || 'bottom'; 63066 63067 /** 63068 * Axis orient 63069 * - 'horizontal' 63070 * - 'vertical' 63071 * @type {[type]} 63072 */ 63073 this.orient = null; 63074 63075 }; 63076 63077 SingleAxis.prototype = { 63078 63079 constructor: SingleAxis, 63080 63081 /** 63082 * Axis model 63083 * @type {module:echarts/coord/single/AxisModel} 63084 */ 63085 model: null, 63086 63087 /** 63088 * Judge the orient of the axis. 63089 * @return {boolean} 63090 */ 63091 isHorizontal: function () { 63092 var position = this.position; 63093 return position === 'top' || position === 'bottom'; 63094 63095 }, 63096 63097 /** 63098 * @override 63099 */ 63100 pointToData: function (point, clamp) { 63101 return this.coordinateSystem.pointToData(point, clamp)[0]; 63102 }, 63103 63104 /** 63105 * Convert the local coord(processed by dataToCoord()) 63106 * to global coord(concrete pixel coord). 63107 * designated by module:echarts/coord/single/Single. 63108 * @type {Function} 63109 */ 63110 toGlobalCoord: null, 63111 63112 /** 63113 * Convert the global coord to local coord. 63114 * designated by module:echarts/coord/single/Single. 63115 * @type {Function} 63116 */ 63117 toLocalCoord: null 63118 63119 }; 63120 63121 inherits(SingleAxis, Axis); 63122 63123 /* 63124* Licensed to the Apache Software Foundation (ASF) under one 63125* or more contributor license agreements. See the NOTICE file 63126* distributed with this work for additional information 63127* regarding copyright ownership. The ASF licenses this file 63128* to you under the Apache License, Version 2.0 (the 63129* "License"); you may not use this file except in compliance 63130* with the License. You may obtain a copy of the License at 63131* 63132* http://www.apache.org/licenses/LICENSE-2.0 63133* 63134* Unless required by applicable law or agreed to in writing, 63135* software distributed under the License is distributed on an 63136* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63137* KIND, either express or implied. See the License for the 63138* specific language governing permissions and limitations 63139* under the License. 63140*/ 63141 63142 /** 63143 * Single coordinates system. 63144 */ 63145 63146 /** 63147 * Create a single coordinates system. 63148 * 63149 * @param {module:echarts/coord/single/AxisModel} axisModel 63150 * @param {module:echarts/model/Global} ecModel 63151 * @param {module:echarts/ExtensionAPI} api 63152 */ 63153 function Single(axisModel, ecModel, api) { 63154 63155 /** 63156 * @type {string} 63157 * @readOnly 63158 */ 63159 this.dimension = 'single'; 63160 63161 /** 63162 * Add it just for draw tooltip. 63163 * 63164 * @type {Array.<string>} 63165 * @readOnly 63166 */ 63167 this.dimensions = ['single']; 63168 63169 /** 63170 * @private 63171 * @type {module:echarts/coord/single/SingleAxis}. 63172 */ 63173 this._axis = null; 63174 63175 /** 63176 * @private 63177 * @type {module:zrender/core/BoundingRect} 63178 */ 63179 this._rect; 63180 63181 this._init(axisModel, ecModel, api); 63182 63183 /** 63184 * @type {module:echarts/coord/single/AxisModel} 63185 */ 63186 this.model = axisModel; 63187 } 63188 63189 Single.prototype = { 63190 63191 type: 'singleAxis', 63192 63193 axisPointerEnabled: true, 63194 63195 constructor: Single, 63196 63197 /** 63198 * Initialize single coordinate system. 63199 * 63200 * @param {module:echarts/coord/single/AxisModel} axisModel 63201 * @param {module:echarts/model/Global} ecModel 63202 * @param {module:echarts/ExtensionAPI} api 63203 * @private 63204 */ 63205 _init: function (axisModel, ecModel, api) { 63206 63207 var dim = this.dimension; 63208 63209 var axis = new SingleAxis( 63210 dim, 63211 createScaleByModel(axisModel), 63212 [0, 0], 63213 axisModel.get('type'), 63214 axisModel.get('position') 63215 ); 63216 63217 var isCategory = axis.type === 'category'; 63218 axis.onBand = isCategory && axisModel.get('boundaryGap'); 63219 axis.inverse = axisModel.get('inverse'); 63220 axis.orient = axisModel.get('orient'); 63221 63222 axisModel.axis = axis; 63223 axis.model = axisModel; 63224 axis.coordinateSystem = this; 63225 this._axis = axis; 63226 }, 63227 63228 /** 63229 * Update axis scale after data processed 63230 * @param {module:echarts/model/Global} ecModel 63231 * @param {module:echarts/ExtensionAPI} api 63232 */ 63233 update: function (ecModel, api) { 63234 ecModel.eachSeries(function (seriesModel) { 63235 if (seriesModel.coordinateSystem === this) { 63236 var data = seriesModel.getData(); 63237 each$1(data.mapDimension(this.dimension, true), function (dim) { 63238 this._axis.scale.unionExtentFromData(data, dim); 63239 }, this); 63240 niceScaleExtent(this._axis.scale, this._axis.model); 63241 } 63242 }, this); 63243 }, 63244 63245 /** 63246 * Resize the single coordinate system. 63247 * 63248 * @param {module:echarts/coord/single/AxisModel} axisModel 63249 * @param {module:echarts/ExtensionAPI} api 63250 */ 63251 resize: function (axisModel, api) { 63252 this._rect = getLayoutRect( 63253 { 63254 left: axisModel.get('left'), 63255 top: axisModel.get('top'),
63256 right: axisModel.get('right'), 63257 bottom: axisModel.get('bottom'), 63258 width: axisModel.get('width'), 63259 height: axisModel.get('height') 63260 }, 63261 { 63262 width: api.getWidth(), 63263 height: api.getHeight() 63264 } 63265 ); 63266 63267 this._adjustAxis(); 63268 }, 63269 63270 /** 63271 * @return {module:zrender/core/BoundingRect} 63272 */ 63273 getRect: function () { 63274 return this._rect; 63275 }, 63276 63277 /** 63278 * @private 63279 */ 63280 _adjustAxis: function () { 63281 63282 var rect = this._rect; 63283 var axis = this._axis; 63284 63285 var isHorizontal = axis.isHorizontal(); 63286 var extent = isHorizontal ? [0, rect.width] : [0, rect.height]; 63287 var idx = axis.reverse ? 1 : 0; 63288 63289 axis.setExtent(extent[idx], extent[1 - idx]); 63290 63291 this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y); 63292 63293 }, 63294 63295 /** 63296 * @param {module:echarts/coord/single/SingleAxis} axis 63297 * @param {number} coordBase 63298 */ 63299 _updateAxisTransform: function (axis, coordBase) { 63300 63301 var axisExtent = axis.getExtent(); 63302 var extentSum = axisExtent[0] + axisExtent[1]; 63303 var isHorizontal = axis.isHorizontal(); 63304 63305 axis.toGlobalCoord = isHorizontal 63306 ? function (coord) { 63307 return coord + coordBase; 63308 } 63309 : function (coord) { 63310 return extentSum - coord + coordBase; 63311 }; 63312 63313 axis.toLocalCoord = isHorizontal 63314 ? function (coord) { 63315 return coord - coordBase; 63316 } 63317 : function (coord) { 63318 return extentSum - coord + coordBase; 63319 }; 63320 }, 63321 63322 /** 63323 * Get axis. 63324 * 63325 * @return {module:echarts/coord/single/SingleAxis} 63326 */ 63327 getAxis: function () { 63328 return this._axis; 63329 }, 63330 63331 /** 63332 * Get axis, add it just for draw tooltip. 63333 * 63334 * @return {[type]} [description] 63335 */ 63336 getBaseAxis: function () { 63337 return this._axis; 63338 }, 63339 63340 /** 63341 * @return {Array.<module:echarts/coord/Axis>} 63342 */ 63343 getAxes: function () { 63344 return [this._axis]; 63345 }, 63346 63347 /** 63348 * @return {Object} {baseAxes: [], otherAxes: []} 63349 */ 63350 getTooltipAxes: function () { 63351 return {baseAxes: [this.getAxis()]}; 63352 }, 63353 63354 /** 63355 * If contain point. 63356 * 63357 * @param {Array.<number>} point 63358 * @return {boolean} 63359 */ 63360 containPoint: function (point) { 63361 var rect = this.getRect(); 63362 var axis = this.getAxis(); 63363 var orient = axis.orient; 63364 if (orient === 'horizontal') { 63365 return axis.contain(axis.toLocalCoord(point[0])) 63366 && (point[1] >= rect.y && point[1] <= (rect.y + rect.height)); 63367 } 63368 else { 63369 return axis.contain(axis.toLocalCoord(point[1])) 63370 && (point[0] >= rect.y && point[0] <= (rect.y + rect.height)); 63371 } 63372 }, 63373 63374 /** 63375 * @param {Array.<number>} point 63376 * @return {Array.<number>} 63377 */ 63378 pointToData: function (point) { 63379 var axis = this.getAxis(); 63380 return [axis.coordToData(axis.toLocalCoord( 63381 point[axis.orient === 'horizontal' ? 0 : 1] 63382 ))]; 63383 }, 63384 63385 /** 63386 * Convert the series data to concrete point. 63387 * 63388 * @param {number|Array.<number>} val 63389 * @return {Array.<number>} 63390 */ 63391 dataToPoint: function (val) { 63392 var axis = this.getAxis(); 63393 var rect = this.getRect(); 63394 var pt = []; 63395 var idx = axis.orient === 'horizontal' ? 0 : 1; 63396 63397 if (val instanceof Array) { 63398 val = val[0]; 63399 } 63400 63401 pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val)); 63402 pt[1 - idx] = idx === 0 ? (rect.y + rect.height / 2) : (rect.x + rect.width / 2); 63403 return pt; 63404 } 63405 63406 }; 63407 63408 /* 63409* Licensed to the Apache Software Foundation (ASF) under one 63410* or more contributor license agreements. See the NOTICE file 63411* distributed with this work for additional information 63412* regarding copyright ownership. The ASF licenses this file 63413* to you under the Apache License, Version 2.0 (the 63414* "License");
63414 you may not use this file except in compliance 63415* with the License. You may obtain a copy of the License at 63416* 63417* http://www.apache.org/licenses/LICENSE-2.0 63418* 63419* Unless required by applicable law or agreed to in writing, 63420* software distributed under the License is distributed on an 63421* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63422* KIND, either express or implied. See the License for the 63423* specific language governing permissions and limitations 63424* under the License. 63425*/ 63426 63427 /** 63428 * Single coordinate system creator. 63429 */ 63430 63431 /** 63432 * Create single coordinate system and inject it into seriesModel. 63433 * 63434 * @param {module:echarts/model/Global} ecModel 63435 * @param {module:echarts/ExtensionAPI} api 63436 * @return {Array.<module:echarts/coord/single/Single>} 63437 */ 63438 function create$3(ecModel, api) { 63439 var singles = []; 63440 63441 ecModel.eachComponent('singleAxis', function(axisModel, idx) { 63442 63443 var single = new Single(axisModel, ecModel, api); 63444 single.name = 'single_' + idx; 63445 single.resize(axisModel, api); 63446 axisModel.coordinateSystem = single; 63447 singles.push(single); 63448 63449 }); 63450 63451 ecModel.eachSeries(function (seriesModel) { 63452 if (seriesModel.get('coordinateSystem') === 'singleAxis') { 63453 var singleAxisModel = ecModel.queryComponents({ 63454 mainType: 'singleAxis', 63455 index: seriesModel.get('singleAxisIndex'), 63456 id: seriesModel.get('singleAxisId') 63457 })[0]; 63458 seriesModel.coordinateSystem = singleAxisModel && singleAxisModel.coordinateSystem; 63459 } 63460 }); 63461 63462 return singles; 63463 } 63464 63465 CoordinateSystemManager.register('single', { 63466 create: create$3, 63467 dimensions: Single.prototype.dimensions 63468 }); 63469 63470 /* 63471* Licensed to the Apache Software Foundation (ASF) under one 63472* or more contributor license agreements. See the NOTICE file 63473* distributed with this work for additional information 63474* regarding copyright ownership. The ASF licenses this file 63475* to you under the Apache License, Version 2.0 (the 63476* "License"); you may not use this file except in compliance 63477* with the License. You may obtain a copy of the License at 63478* 63479* http://www.apache.org/licenses/LICENSE-2.0 63480* 63481* Unless required by applicable law or agreed to in writing, 63482* software distributed under the License is distributed on an 63483* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63484* KIND, either express or implied. See the License for the 63485* specific language governing permissions and limitations 63486* under the License. 63487*/ 63488 63489 /** 63490 * @param {Object} opt {labelInside} 63491 * @return {Object} { 63492 * position, rotation, labelDirection, labelOffset, 63493 * tickDirection, labelRotate, z2 63494 * } 63495 */ 63496 function layout$2(axisModel, opt) { 63497 opt = opt || {}; 63498 var single = axisModel.coordinateSystem; 63499 var axis = axisModel.axis; 63500 var layout = {}; 63501 63502 var axisPosition = axis.position; 63503 var orient = axis.orient; 63504 63505 var rect = single.getRect(); 63506 var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; 63507 63508 var positionMap = { 63509 horizontal: {top: rectBound[2], bottom: rectBound[3]}, 63510 vertical: {left: rectBound[0], right: rectBound[1]} 63511 }; 63512 63513 layout.position = [ 63514 orient === 'vertical' 63515 ? positionMap.vertical[axisPosition] 63516 : rectBound[0], 63517 orient === 'horizontal' 63518 ? positionMap.horizontal[axisPosition] 63519 : rectBound[3] 63520 ]; 63521 63522 var r = {horizontal: 0, vertical: 1}; 63523 layout.rotation = Math.PI / 2 * r[orient]; 63524 63525 var directionMap = {top: -1, bottom: 1, right: 1, left: -1}; 63526 63527 layout.labelDirection = layout.tickDirection 63528 = layout.nameDirection 63529 = directionMap[axisPosition]; 63530 63531 if (axisModel.get('axisTick.inside')) { 63532 layout.tickDirection = -layout.tickDirection; 63533 } 63534 63535 if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) { 63536 layout.labelDirection = -layout.labelDirection; 63537 } 63538 63539 var labelRotation = opt.rotate; 63540 labelRotation == null && (labelRotation = axisModel.get('axisLabel.rotate'));
63541 layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; 63542 63543 layout.z2 = 1; 63544 63545 return layout; 63546 } 63547 63548 /* 63549* Licensed to the Apache Software Foundation (ASF) under one 63550* or more contributor license agreements. See the NOTICE file 63551* distributed with this work for additional information 63552* regarding copyright ownership. The ASF licenses this file 63553* to you under the Apache License, Version 2.0 (the 63554* "License"); you may not use this file except in compliance 63555* with the License. You may obtain a copy of the License at 63556* 63557* http://www.apache.org/licenses/LICENSE-2.0 63558* 63559* Unless required by applicable law or agreed to in writing, 63560* software distributed under the License is distributed on an 63561* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63562* KIND, either express or implied. See the License for the 63563* specific language governing permissions and limitations 63564* under the License. 63565*/ 63566 63567 63568 var axisBuilderAttrs$2 = [ 63569 'axisLine', 'axisTickLabel', 'axisName' 63570 ]; 63571 63572 var selfBuilderAttr = 'splitLine'; 63573 63574 var SingleAxisView = AxisView.extend({ 63575 63576 type: 'singleAxis', 63577 63578 axisPointerClass: 'SingleAxisPointer', 63579 63580 render: function (axisModel, ecModel, api, payload) { 63581 63582 var group = this.group; 63583 63584 group.removeAll(); 63585 63586 var layout = layout$2(axisModel); 63587 63588 var axisBuilder = new AxisBuilder(axisModel, layout); 63589 63590 each$1(axisBuilderAttrs$2, axisBuilder.add, axisBuilder); 63591 63592 group.add(axisBuilder.getGroup()); 63593 63594 if (axisModel.get(selfBuilderAttr + '.show')) { 63595 this['_' + selfBuilderAttr](axisModel); 63596 } 63597 63598 SingleAxisView.superCall(this, 'render', axisModel, ecModel, api, payload); 63599 }, 63600 63601 _splitLine: function(axisModel) { 63602 var axis = axisModel.axis; 63603 63604 if (axis.scale.isBlank()) { 63605 return; 63606 } 63607 63608 var splitLineModel = axisModel.getModel('splitLine'); 63609 var lineStyleModel = splitLineModel.getModel('lineStyle'); 63610 var lineWidth = lineStyleModel.get('width'); 63611 var lineColors = lineStyleModel.get('color'); 63612 63613 lineColors = lineColors instanceof Array ? lineColors : [lineColors]; 63614 63615 var gridRect = axisModel.coordinateSystem.getRect(); 63616 var isHorizontal = axis.isHorizontal(); 63617 63618 var splitLines = []; 63619 var lineCount = 0; 63620 63621 var ticksCoords = axis.getTicksCoords({ 63622 tickModel: splitLineModel 63623 }); 63624 63625 var p1 = []; 63626 var p2 = []; 63627 63628 for (var i = 0; i < ticksCoords.length; ++i) { 63629 var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); 63630 if (isHorizontal) { 63631 p1[0] = tickCoord; 63632 p1[1] = gridRect.y; 63633 p2[0] = tickCoord; 63634 p2[1] = gridRect.y + gridRect.height; 63635 } 63636 else { 63637 p1[0] = gridRect.x; 63638 p1[1] = tickCoord;
63639 p2[0] = gridRect.x + gridRect.width; 63640 p2[1] = tickCoord; 63641 } 63642 var colorIndex = (lineCount++) % lineColors.length; 63643 splitLines[colorIndex] = splitLines[colorIndex] || []; 63644 splitLines[colorIndex].push(new Line( 63645 subPixelOptimizeLine({ 63646 shape: { 63647 x1: p1[0], 63648 y1: p1[1], 63649 x2: p2[0], 63650 y2: p2[1] 63651 }, 63652 style: { 63653 lineWidth: lineWidth 63654 }, 63655 silent: true 63656 }))); 63657 } 63658 63659 for (var i = 0; i < splitLines.length; ++i) { 63660 this.group.add(mergePath(splitLines[i], { 63661 style: { 63662 stroke: lineColors[i % lineColors.length], 63663 lineDash: lineStyleModel.getLineDash(lineWidth), 63664 lineWidth: lineWidth 63665 }, 63666 silent: true 63667 })); 63668 } 63669 } 63670 }); 63671 63672 /* 63673* Licensed to the Apache Software Foundation (ASF) under one 63674* or more contributor license agreements. See the NOTICE file 63675* distributed with this work for additional information 63676* regarding copyright ownership. The ASF licenses this file 63677* to you under the Apache License, Version 2.0 (the 63678* "License"); you may not use this file except in compliance 63679* with the License. You may obtain a copy of the License at 63680* 63681* http://www.apache.org/licenses/LICENSE-2.0 63682* 63683* Unless required by applicable law or agreed to in writing, 63684* software distributed under the License is distributed on an 63685* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63686* KIND, either express or implied. See the License for the 63687* specific language governing permissions and limitations 63688* under the License. 63689*/ 63690 63691 var AxisModel$4 = ComponentModel.extend({ 63692 63693 type: 'singleAxis', 63694 63695 layoutMode: 'box', 63696 63697 /** 63698 * @type {module:echarts/coord/single/SingleAxis} 63699 */ 63700 axis: null, 63701 63702 /** 63703 * @type {module:echarts/coord/single/Single} 63704 */ 63705 coordinateSystem: null, 63706 63707 /** 63708 * @override 63709 */ 63710 getCoordSysModel: function () { 63711 return this; 63712 } 63713 63714 }); 63715 63716 var defaultOption$2 = { 63717 63718 left: '5%', 63719 top: '5%', 63720 right: '5%', 63721 bottom: '5%', 63722 63723 type: 'value', 63724 63725 position: 'bottom', 63726 63727 orient: 'horizontal', 63728 63729 axisLine: { 63730 show: true, 63731 lineStyle: { 63732 width: 2, 63733 type: 'solid' 63734 } 63735 }, 63736 63737 // Single coordinate system and single axis is the, 63738 // which is used as the parent tooltip model. 63739 // same model, so we set default tooltip show as true. 63740 tooltip: { 63741 show: true 63742 }, 63743 63744 axisTick: { 63745 show: true, 63746 length: 6, 63747 lineStyle: { 63748 width: 2 63749 } 63750 }, 63751 63752 axisLabel: { 63753 show: true, 63754 interval: 'auto' 63755 }, 63756 63757 splitLine: { 63758 show: true, 63759 lineStyle: { 63760 type: 'dashed', 63761 opacity: 0.2 63762 } 63763 } 63764 }; 63765 63766 function getAxisType$2(axisName, option) { 63767 return option.type || (option.data ? 'category' : 'value'); 63768 } 63769 63770 merge(AxisModel$4.prototype, axisModelCommonMixin); 63771 63772 axisModelCreator('single', AxisModel$4, getAxisType$2, defaultOption$2); 63773 63774 /* 63775* Licensed to the Apache Software Foundation (ASF) under one 63776* or more contributor license agreements. See the NOTICE file 63777* distributed with this work for additional information 63778* regarding copyright ownership. The ASF licenses this file 63779* to you under the Apache License, Version 2.0 (the 63780* "License");
63780 you may not use this file except in compliance 63781* with the License. You may obtain a copy of the License at 63782* 63783* http://www.apache.org/licenses/LICENSE-2.0 63784* 63785* Unless required by applicable law or agreed to in writing, 63786* software distributed under the License is distributed on an 63787* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63788* KIND, either express or implied. See the License for the 63789* specific language governing permissions and limitations 63790* under the License. 63791*/ 63792 63793 /** 63794 * @param {Object} finder contains {seriesIndex, dataIndex, dataIndexInside} 63795 * @param {module:echarts/model/Global} ecModel 63796 * @return {Object} {point: [x, y], el: ...} point Will not be null. 63797 */ 63798 var findPointFromSeries = function (finder, ecModel) { 63799 var point = []; 63800 var seriesIndex = finder.seriesIndex; 63801 var seriesModel; 63802 if (seriesIndex == null || !( 63803 seriesModel = ecModel.getSeriesByIndex(seriesIndex) 63804 )) { 63805 return {point: []}; 63806 } 63807 63808 var data = seriesModel.getData(); 63809 var dataIndex = queryDataIndex(data, finder); 63810 if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) { 63811 return {point: []}; 63812 } 63813 63814 var el = data.getItemGraphicEl(dataIndex); 63815 var coordSys = seriesModel.coordinateSystem; 63816 63817 if (seriesModel.getTooltipPosition) { 63818 point = seriesModel.getTooltipPosition(dataIndex) || []; 63819 } 63820 else if (coordSys && coordSys.dataToPoint) { 63821 point = coordSys.dataToPoint( 63822 data.getValues( 63823 map(coordSys.dimensions, function (dim) { 63824 return data.mapDimension(dim); 63825 }), dataIndex, true 63826 ) 63827 ) || []; 63828 } 63829 else if (el) { 63830 // Use graphic bounding rect 63831 var rect = el.getBoundingRect().clone(); 63832 rect.applyTransform(el.transform); 63833 point = [ 63834 rect.x + rect.width / 2, 63835 rect.y + rect.height / 2 63836 ]; 63837 } 63838 63839 return {point: point, el: el}; 63840 }; 63841 63842 /* 63843* Licensed to the Apache Software Foundation (ASF) under one 63844* or more contributor license agreements. See the NOTICE file 63845* distributed with this work for additional information 63846* regarding copyright ownership. The ASF licenses this file 63847* to you under the Apache License, Version 2.0 (the 63848* "License"); you may not use this file except in compliance 63849* with the License. You may obtain a copy of the License at 63850* 63851* http://www.apache.org/licenses/LICENSE-2.0 63852* 63853* Unless required by applicable law or agreed to in writing, 63854* software distributed under the License is distributed on an 63855* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 63856* KIND, either express or implied. See the License for the 63857* specific language governing permissions and limitations 63858* under the License. 63859*/ 63860 63861 var each$14 = each$1; 63862 var curry$3 = curry; 63863 var inner$7 = makeInner(); 63864 63865 /** 63866 * Basic logic: check all axis, if they do not demand show/highlight, 63867 * then hide/downplay them. 63868 * 63869 * @param {Object} coordSysAxesInfo 63870 * @param {Object} payload 63871 * @param {string} [payload.currTrigger] 'click' | 'mousemove' | 'leave' 63872 * @param {Array.<number>} [payload.x] x and y, which are mandatory, specify a point to 63873 * trigger axisPointer and tooltip. 63874 * @param {Array.<number>} [payload.y] x and y, which are mandatory, specify a point to 63875 * trigger axisPointer and tooltip. 63876 * @param {Object} [payload.seriesIndex] finder, optional, restrict target axes. 63877 * @param {Object} [payload.dataIndex] finder, restrict target axes. 63878 * @param {Object} [payload.axesInfo] finder, restrict target axes. 63879 * [{ 63880 * axisDim: 'x'|'y'|'angle'|..., 63881 * axisIndex: ..., 63882 * value: ... 63883 * }, ...] 63884 * @param {Function} [payload.dispatchAction] 63885 * @param {Object} [payload.tooltipOption] 63886 * @param {Object|Array.<number>|Function} [payload.position] Tooltip position, 63887 * which can be specified in dispatchAction 63888 * @param {module:echarts/model/Global} ecModel 63889 * @param {module:echarts/ExtensionAPI} api 63890 * @return {Object} content of event obj for echarts.connect. 63891 */ 63892 var axisTrigger = function (payload, ecModel, api) { 63893 var currTrigger = payload.currTrigger; 63894 var point = [payload.x, payload.y];
vendor: 6,257 bytes, lines 63895-64035
63895 var finder = payload; 63896 var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api); 63897 var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; 63898 63899 // Pending 63900 // See #6121. But we are not able to reproduce it yet. 63901 if (!coordSysAxesInfo) { 63902 return; 63903 } 63904 63905 if (illegalPoint(point)) { 63906 // Used in the default behavior of `connection`: use the sample seriesIndex 63907 // and dataIndex. And also used in the tooltipView trigger. 63908 point = findPointFromSeries({ 63909 seriesIndex: finder.seriesIndex, 63910 // Do not use dataIndexInside from other ec instance. 63911 // FIXME: auto detect it? 63912 dataIndex: finder.dataIndex 63913 }, ecModel).point; 63914 } 63915 var isIllegalPoint = illegalPoint(point); 63916 63917 // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}). 63918 // Notice: In this case, it is difficult to get the `point` (which is necessary to show 63919 // tooltip, so if point is not given, we just use the point found by sample seriesIndex 63920 // and dataIndex. 63921 var inputAxesInfo = finder.axesInfo; 63922 63923 var axesInfo = coordSysAxesInfo.axesInfo; 63924 var shouldHide = currTrigger === 'leave' || illegalPoint(point); 63925 var outputFinder = {}; 63926 63927 var showValueMap = {}; 63928 var dataByCoordSys = {list: [], map: {}}; 63929 var updaters = { 63930 showPointer: curry$3(showPointer, showValueMap), 63931 showTooltip: curry$3(showTooltip, dataByCoordSys) 63932 }; 63933 63934 // Process for triggered axes. 63935 each$14(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) { 63936 // If a point given, it must be contained by the coordinate system. 63937 var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point); 63938 63939 each$14(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) { 63940 var axis = axisInfo.axis; 63941 var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); 63942 // If no inputAxesInfo, no axis is restricted. 63943 if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) { 63944 var val = inputAxisInfo && inputAxisInfo.value; 63945 if (val == null && !isIllegalPoint) { 63946 val = axis.pointToData(point); 63947 } 63948 val != null && processOnAxis(axisInfo, val, updaters, false, outputFinder); 63949 } 63950 }); 63951 }); 63952 63953 // Process for linked axes. 63954 var linkTriggers = {}; 63955 each$14(axesInfo, function (tarAxisInfo, tarKey) { 63956 var linkGroup = tarAxisInfo.linkGroup; 63957 63958 // If axis has been triggered in the previous stage, it should not be triggered by link. 63959 if (linkGroup && !showValueMap[tarKey]) { 63960 each$14(linkGroup.axesInfo, function (srcAxisInfo, srcKey) { 63961 var srcValItem = showValueMap[srcKey]; 63962 // If srcValItem exist, source axis is triggered, so link to target axis. 63963 if (srcAxisInfo !== tarAxisInfo && srcValItem) { 63964 var val = srcValItem.value; 63965 linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper( 63966 val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo) 63967 ))); 63968 linkTriggers[tarAxisInfo.key] = val; 63969 } 63970 }); 63971 } 63972 }); 63973 each$14(linkTriggers, function (val, tarKey) { 63974 processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder); 63975 }); 63976 63977 updateModelActually(showValueMap, axesInfo, outputFinder); 63978 dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction); 63979 dispatchHighDownActually(axesInfo, dispatchAction, api); 63980 63981 return outputFinder; 63982 }; 63983 63984 function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) { 63985 var axis = axisInfo.axis; 63986 63987 if (axis.scale.isBlank() || !axis.containData(newValue)) { 63988 return; 63989 } 63990 63991 if (!axisInfo.involveSeries) { 63992 updaters.showPointer(axisInfo, newValue); 63993 return; 63994 } 63995 63996 // Heavy calculation. So put it after axis.containData checking. 63997 var payloadInfo = buildPayloadsBySeries(newValue, axisInfo); 63998 var payloadBatch = payloadInfo.payloadBatch; 63999 var snapToValue = payloadInfo.snapToValue; 64000 64001 // Fill content of event obj for echarts.connect. 64002 // By defualt use the first involved series data as a sample to connect. 64003 if (payloadBatch[0] && outputFinder.seriesIndex == null) { 64004 extend(outputFinder, payloadBatch[0]); 64005 } 64006 64007 // If no linkSource input, this process is for collecting link 64008 // target, where snap should not be accepted. 64009 if (!dontSnap && axisInfo.snap) { 64010 if (axis.containData(snapToValue) && snapToValue != null) { 64011 newValue = snapToValue; 64012 } 64013 } 64014 64015 updaters.showPointer(axisInfo, newValue, payloadBatch, outputFinder); 64016 // Tooltip should always be snapToValue, otherwise there will be 64017 // incorrect "axis value ~ series value" mapping displayed in tooltip. 64018 updaters.showTooltip(axisInfo, payloadInfo, snapToValue); 64019 } 64020 64021 function buildPayloadsBySeries(value, axisInfo) { 64022 var axis = axisInfo.axis; 64023 var dim = axis.dim; 64024 var snapToValue = value; 64025 var payloadBatch = []; 64026 var minDist = Number.MAX_VALUE; 64027 var minDiff = -1; 64028 64029 each$14(axisInfo.seriesModels, function (series, idx) { 64030 var dataDim = series.getData().mapDimension(dim, true); 64031 var seriesNestestValue; 64032 var dataIndices; 64033 64034 if (series.getAxisTooltipData) { 64035 var result = series.getAxisTooltipData(dataDim, value, axis);
vendor: 9,151 bytes, lines 64036-64258
64036 dataIndices = result.dataIndices; 64037 seriesNestestValue = result.nestestValue; 64038 } 64039 else { 64040 dataIndices = series.getData().indicesOfNearest( 64041 dataDim[0], 64042 value, 64043 // Add a threshold to avoid find the wrong dataIndex 64044 // when data length is not same. 64045 // false, 64046 axis.type === 'category' ? 0.5 : null 64047 ); 64048 if (!dataIndices.length) { 64049 return; 64050 } 64051 seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]); 64052 } 64053 64054 if (seriesNestestValue == null || !isFinite(seriesNestestValue)) { 64055 return; 64056 } 64057 64058 var diff = value - seriesNestestValue; 64059 var dist = Math.abs(diff); 64060 // Consider category case 64061 if (dist <= minDist) { 64062 if (dist < minDist || (diff >= 0 && minDiff < 0)) { 64063 minDist = dist; 64064 minDiff = diff; 64065 snapToValue = seriesNestestValue; 64066 payloadBatch.length = 0; 64067 } 64068 each$14(dataIndices, function (dataIndex) { 64069 payloadBatch.push({ 64070 seriesIndex: series.seriesIndex, 64071 dataIndexInside: dataIndex, 64072 dataIndex: series.getData().getRawIndex(dataIndex) 64073 }); 64074 }); 64075 } 64076 }); 64077 64078 return { 64079 payloadBatch: payloadBatch, 64080 snapToValue: snapToValue 64081 }; 64082 } 64083 64084 function showPointer(showValueMap, axisInfo, value, payloadBatch) { 64085 showValueMap[axisInfo.key] = {value: value, payloadBatch: payloadBatch}; 64086 } 64087 64088 function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) { 64089 var payloadBatch = payloadInfo.payloadBatch; 64090 var axis = axisInfo.axis; 64091 var axisModel = axis.model; 64092 var axisPointerModel = axisInfo.axisPointerModel; 64093 64094 // If no data, do not create anything in dataByCoordSys, 64095 // whose length will be used to judge whether dispatch action. 64096 if (!axisInfo.triggerTooltip || !payloadBatch.length) { 64097 return; 64098 } 64099 64100 var coordSysModel = axisInfo.coordSys.model; 64101 var coordSysKey = makeKey(coordSysModel); 64102 var coordSysItem = dataByCoordSys.map[coordSysKey]; 64103 if (!coordSysItem) { 64104 coordSysItem = dataByCoordSys.map[coordSysKey] = { 64105 coordSysId: coordSysModel.id, 64106 coordSysIndex: coordSysModel.componentIndex, 64107 coordSysType: coordSysModel.type, 64108 coordSysMainType: coordSysModel.mainType, 64109 dataByAxis: [] 64110 }; 64111 dataByCoordSys.list.push(coordSysItem); 64112 } 64113 64114 coordSysItem.dataByAxis.push({ 64115 axisDim: axis.dim, 64116 axisIndex: axisModel.componentIndex, 64117 axisType: axisModel.type, 64118 axisId: axisModel.id, 64119 value: value, 64120 // Caustion: viewHelper.getValueLabel is actually on "view stage", which 64121 // depends that all models have been updated. So it should not be performed 64122 // here. Considering axisPointerModel used here is volatile, which is hard 64123 // to be retrieve in TooltipView, we prepare parameters here. 64124 valueLabelOpt: { 64125 precision: axisPointerModel.get('label.precision'), 64126 formatter: axisPointerModel.get('label.formatter') 64127 }, 64128 seriesDataIndices: payloadBatch.slice() 64129 }); 64130 } 64131 64132 function updateModelActually(showValueMap, axesInfo, outputFinder) { 64133 var outputAxesInfo = outputFinder.axesInfo = []; 64134 // Basic logic: If no 'show' required, 'hide' this axisPointer. 64135 each$14(axesInfo, function (axisInfo, key) { 64136 var option = axisInfo.axisPointerModel.option; 64137 var valItem = showValueMap[key]; 64138 64139 if (valItem) { 64140 !axisInfo.useHandle && (option.status = 'show'); 64141 option.value = valItem.value; 64142 // For label formatter param and highlight. 64143 option.seriesDataIndices = (valItem.payloadBatch || []).slice(); 64144 } 64145 // When always show (e.g., handle used), remain 64146 // original value and status. 64147 else { 64148 // If hide, value still need to be set, consider 64149 // click legend to toggle axis blank. 64150 !axisInfo.useHandle && (option.status = 'hide'); 64151 } 64152 64153 // If status is 'hide', should be no info in payload. 64154 option.status === 'show' && outputAxesInfo.push({ 64155 axisDim: axisInfo.axis.dim, 64156 axisIndex: axisInfo.axis.model.componentIndex, 64157 value: option.value 64158 }); 64159 }); 64160 } 64161 64162 function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) { 64163 // Basic logic: If no showTip required, hideTip will be dispatched. 64164 if (illegalPoint(point) || !dataByCoordSys.list.length) { 64165 dispatchAction({type: 'hideTip'}); 64166 return; 64167 } 64168 64169 // In most case only one axis (or event one series is used). It is 64170 // convinient to fetch payload.seriesIndex and payload.dataIndex 64171 // dirtectly. So put the first seriesIndex and dataIndex of the first 64172 // axis on the payload. 64173 var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {}; 64174 64175 dispatchAction({ 64176 type: 'showTip', 64177 escapeConnect: true, 64178 x: point[0], 64179 y: point[1], 64180 tooltipOption: payload.tooltipOption, 64181 position: payload.position, 64182 dataIndexInside: sampleItem.dataIndexInside, 64183 dataIndex: sampleItem.dataIndex, 64184 seriesIndex: sampleItem.seriesIndex, 64185 dataByCoordSys: dataByCoordSys.list 64186 }); 64187 } 64188 64189 function dispatchHighDownActually(axesInfo, dispatchAction, api) { 64190 // FIXME 64191 // highlight status modification shoule be a stage of main process? 64192 // (Consider confilct (e.g., legend and axisPointer) and setOption) 64193 64194 var zr = api.getZr(); 64195 var highDownKey = 'axisPointerLastHighlights'; 64196 var lastHighlights = inner$7(zr)[highDownKey] || {}; 64197 var newHighlights = inner$7(zr)[highDownKey] = {}; 64198 64199 // Update highlight/downplay status according to axisPointer model. 64200 // Build hash map and remove duplicate incidentally. 64201 each$14(axesInfo, function (axisInfo, key) { 64202 var option = axisInfo.axisPointerModel.option; 64203 option.status === 'show' && each$14(option.seriesDataIndices, function (batchItem) { 64204 var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex; 64205 newHighlights[key] = batchItem; 64206 }); 64207 }); 64208 64209 // Diff. 64210 var toHighlight = []; 64211 var toDownplay = []; 64212 each$1(lastHighlights, function (batchItem, key) { 64213 !newHighlights[key] && toDownplay.push(batchItem); 64214 }); 64215 each$1(newHighlights, function (batchItem, key) { 64216 !lastHighlights[key] && toHighlight.push(batchItem); 64217 }); 64218 64219 toDownplay.length && api.dispatchAction({ 64220 type: 'downplay', escapeConnect: true, batch: toDownplay 64221 }); 64222 toHighlight.length && api.dispatchAction({ 64223 type: 'highlight', escapeConnect: true, batch: toHighlight 64224 }); 64225 } 64226 64227 function findInputAxisInfo(inputAxesInfo, axisInfo) { 64228 for (var i = 0; i < (inputAxesInfo || []).length; i++) { 64229 var inputAxisInfo = inputAxesInfo[i]; 64230 if (axisInfo.axis.dim === inputAxisInfo.axisDim 64231 && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex 64232 ) { 64233 return inputAxisInfo; 64234 } 64235 } 64236 } 64237 64238 function makeMapperParam(axisInfo) { 64239 var axisModel = axisInfo.axis.model; 64240 var item = {}; 64241 var dim = item.axisDim = axisInfo.axis.dim; 64242 item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex; 64243 item.axisName = item[dim + 'AxisName'] = axisModel.name; 64244 item.axisId = item[dim + 'AxisId'] = axisModel.id; 64245 return item; 64246 } 64247 64248 function illegalPoint(point) { 64249 return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]); 64250 } 64251 64252 /* 64253* Licensed to the Apache Software Foundation (ASF) under one 64254* or more contributor license agreements. See the NOTICE file 64255* distributed with this work for additional information 64256* regarding copyright ownership. The ASF licenses this file 64257* to you under the Apache License, Version 2.0 (the 64258* "License");
64258 you may not use this file except in compliance 64259* with the License. You may obtain a copy of the License at 64260* 64261* http://www.apache.org/licenses/LICENSE-2.0 64262* 64263* Unless required by applicable law or agreed to in writing, 64264* software distributed under the License is distributed on an 64265* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 64266* KIND, either express or implied. See the License for the 64267* specific language governing permissions and limitations 64268* under the License. 64269*/ 64270 64271 var AxisPointerModel = extendComponentModel({ 64272 64273 type: 'axisPointer', 64274 64275 coordSysAxesInfo: null, 64276 64277 defaultOption: { 64278 // 'auto' means that show when triggered by tooltip or handle. 64279 show: 'auto', 64280 // 'click' | 'mousemove' | 'none' 64281 triggerOn: null, // set default in AxisPonterView.js 64282 64283 zlevel: 0, 64284 z: 50, 64285 64286 type: 'line', 64287 // axispointer triggered by tootip determine snap automatically, 64288 // see `modelHelper`. 64289 snap: false, 64290 triggerTooltip: true, 64291 64292 value: null, 64293 status: null, // Init value depends on whether handle is used. 64294 64295 // [group0, group1, ...] 64296 // Each group can be: { 64297 // mapper: function () {}, 64298 // singleTooltip: 'multiple', // 'multiple' or 'single' 64299 // xAxisId: ..., 64300 // yAxisName: ..., 64301 // angleAxisIndex: ... 64302 // } 64303 // mapper: can be ignored. 64304 // input: {axisInfo, value} 64305 // output: {axisInfo, value} 64306 link: [], 64307 64308 // Do not set 'auto' here, otherwise global animation: false 64309 // will not effect at this axispointer. 64310 animation: null, 64311 animationDurationUpdate: 200, 64312 64313 lineStyle: { 64314 color: '#aaa', 64315 width: 1, 64316 type: 'solid' 64317 }, 64318 64319 shadowStyle: { 64320 color: 'rgba(150,150,150,0.3)' 64321 }, 64322 64323 label: { 64324 show: true, 64325 formatter: null, // string | Function 64326 precision: 'auto', // Or a number like 0, 1, 2 ... 64327 margin: 3, 64328 color: '#fff', 64329 padding: [5, 7, 5, 7], 64330 backgroundColor: 'auto', // default: axis line color 64331 borderColor: null, 64332 borderWidth: 0, 64333 shadowBlur: 3, 64334 shadowColor: '#aaa' 64335 // Considering applicability, common style should 64336 // better not have shadowOffset. 64337 // shadowOffsetX: 0, 64338 // shadowOffsetY: 2 64339 }, 64340 64341 handle: { 64342 show: false, 64343 icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line 64344 size: 45, 64345 // handle margin is from symbol center to axis, which is stable when circular move. 64346 margin: 50, 64347 // color: '#1b8bbd' 64348 // color: '#2f4554' 64349 color: '#333', 64350 shadowBlur: 3, 64351 shadowColor: '#aaa', 64352 shadowOffsetX: 0, 64353 shadowOffsetY: 2, 64354 64355 // For mobile performance 64356 throttle: 40 64357 } 64358 } 64359 64360 }); 64361 64362 /* 64363* Licensed to the Apache Software Foundation (ASF) under one 64364* or more contributor license agreements. See the NOTICE file 64365* distributed with this work for additional information 64366* regarding copyright ownership. The ASF licenses this file 64367* to you under the Apache License, Version 2.0 (the 64368* "License"); you may not use this file except in compliance 64369* with the License. You may obtain a copy of the License at 64370* 64371* http://www.apache.org/licenses/LICENSE-2.0 64372* 64373* Unless required by applicable law or agreed to in writing, 64374* software distributed under the License is distributed on an 64375* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 64376* KIND, either express or implied. See the License for the 64377* specific language governing permissions and limitations 64378* under the License. 64379*/ 64380 64381 var inner$8 = makeInner(); 64382 var each$15 = each$1; 64383 64384 /** 64385 * @param {string} key 64386 * @param {module:echarts/ExtensionAPI} api 64387 * @param {Function} handler 64388 * param: {string} currTrigger 64389 * param: {Array.<number>} point 64390 */ 64391 function register(key, api, handler) { 64392 if (env$1.node) { 64393 return; 64394 } 64395 64396 var zr = api.getZr(); 64397 inner$8(zr).records || (inner$8(zr).records = {}); 64398 64399 initGlobalListeners(zr, api); 64400 64401 var record = inner$8(zr).records[key] || (inner$8(zr).records[key] = {}); 64402 record.handler = handler; 64403 } 64404 64405 function initGlobalListeners(zr, api) { 64406 if (inner$8(zr).initialized) { 64407 return; 64408 } 64409 64410 inner$8(zr).initialized = true; 64411 64412 useHandler('click', curry(doEnter, 'click')); 64413 useHandler('mousemove', curry(doEnter, 'mousemove')); 64414 // useHandler('mouseout', onLeave); 64415 useHandler('globalout', onLeave); 64416 64417 function useHandler(eventType, cb) { 64418 zr.on(eventType, function (e) { 64419 var dis = makeDispatchAction(api); 64420 64421 each$15(inner$8(zr).records, function (record) { 64422 record && cb(record, e, dis.dispatchAction); 64423 }); 64424 64425 dispatchTooltipFinally(dis.pendings, api); 64426 }); 64427 } 64428 } 64429 64430 function dispatchTooltipFinally(pendings, api) { 64431 var showLen = pendings.showTip.length; 64432 var hideLen = pendings.hideTip.length; 64433 64434 var actuallyPayload; 64435 if (showLen) { 64436 actuallyPayload = pendings.showTip[showLen - 1]; 64437 } 64438 else if (hideLen) { 64439 actuallyPayload = pendings.hideTip[hideLen - 1]; 64440 } 64441 if (actuallyPayload) { 64442 actuallyPayload.dispatchAction = null; 64443 api.dispatchAction(actuallyPayload); 64444 } 64445 } 64446 64447 function onLeave(record, e, dispatchAction) { 64448 record.handler('leave', null, dispatchAction); 64449 } 64450 64451 function doEnter(currTrigger, record, e, dispatchAction) { 64452 record.handler(currTrigger, e, dispatchAction); 64453 } 64454 64455 function makeDispatchAction(api) { 64456 var pendings = { 64457 showTip: [], 64458 hideTip: [] 64459 }; 64460 // FIXME 64461 // better approach? 64462 // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip, 64463 // which may be conflict, (axisPointer call showTip but tooltip call hideTip); 64464 // So we have to add "final stage" to merge those dispatched actions. 64465 var dispatchAction = function (payload) { 64466 var pendingList = pendings[payload.type]; 64467 if (pendingList) { 64468 pendingList.push(payload); 64469 } 64470 else {
64471 payload.dispatchAction = dispatchAction; 64472 api.dispatchAction(payload); 64473 } 64474 }; 64475 64476 return { 64477 dispatchAction: dispatchAction, 64478 pendings: pendings 64479 }; 64480 } 64481 64482 /** 64483 * @param {string} key 64484 * @param {module:echarts/ExtensionAPI} api 64485 */ 64486 function unregister(key, api) { 64487 if (env$1.node) { 64488 return; 64489 } 64490 var zr = api.getZr(); 64491 var record = (inner$8(zr).records || {})[key]; 64492 if (record) { 64493 inner$8(zr).records[key] = null; 64494 } 64495 } 64496 64497 /* 64498* Licensed to the Apache Software Foundation (ASF) under one 64499* or more contributor license agreements. See the NOTICE file 64500* distributed with this work for additional information 64501* regarding copyright ownership. The ASF licenses this file 64502* to you under the Apache License, Version 2.0 (the 64503* "License"); you may not use this file except in compliance 64504* with the License. You may obtain a copy of the License at 64505* 64506* http://www.apache.org/licenses/LICENSE-2.0 64507* 64508* Unless required by applicable law or agreed to in writing, 64509* software distributed under the License is distributed on an 64510* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 64511* KIND, either express or implied. See the License for the 64512* specific language governing permissions and limitations 64513* under the License. 64514*/ 64515 64516 var AxisPointerView = extendComponentView({ 64517 64518 type: 'axisPointer', 64519 64520 render: function (globalAxisPointerModel, ecModel, api) { 64521 var globalTooltipModel = ecModel.getComponent('tooltip'); 64522 var triggerOn = globalAxisPointerModel.get('triggerOn') 64523 || (globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'); 64524 64525 // Register global listener in AxisPointerView to enable 64526 // AxisPointerView to be independent to Tooltip. 64527 register( 64528 'axisPointer', 64529 api, 64530 function (currTrigger, e, dispatchAction) { 64531 // If 'none', it is not controlled by mouse totally. 64532 if (triggerOn !== 'none' 64533 && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0) 64534 ) { 64535 dispatchAction({ 64536 type: 'updateAxisPointer', 64537 currTrigger: currTrigger, 64538 x: e && e.offsetX, 64539 y: e && e.offsetY 64540 }); 64541 } 64542 } 64543 ); 64544 }, 64545 64546 /** 64547 * @override 64548 */ 64549 remove: function (ecModel, api) { 64550 unregister(api.getZr(), 'axisPointer'); 64551 AxisPointerView.superApply(this._model, 'remove', arguments); 64552 }, 64553 64554 /** 64555 * @override 64556 */ 64557 dispose: function (ecModel, api) { 64558 unregister('axisPointer', api); 64559 AxisPointerView.superApply(this._model, 'dispose', arguments); 64560 } 64561 64562 }); 64563 64564 /* 64565* Licensed to the Apache Software Foundation (ASF) under one 64566* or more contributor license agreements. See the NOTICE file 64567* distributed with this work for additional information 64568* regarding copyright ownership. The ASF licenses this file 64569* to you under the Apache License, Version 2.0 (the 64570* "License"); you may not use this file except in compliance 64571* with the License. You may obtain a copy of the License at 64572* 64573* http://www.apache.org/licenses/LICENSE-2.0 64574* 64575* Unless required by applicable law or agreed to in writing, 64576* software distributed under the License is distributed on an 64577* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 64578* KIND, either express or implied. See the License for the 64579* specific language governing permissions and limitations 64580* under the License. 64581*/ 64582 64583 var inner$9 = makeInner(); 64584 var clone$4 = clone; 64585 var bind$2 = bind; 64586 64587 /** 64588 * Base axis pointer class in 2D. 64589 * Implemenents {module:echarts/component/axis/IAxisPointer}. 64590 */
vendor: 4,557 bytes, lines 64591-64740
64591 function BaseAxisPointer () { 64592 } 64593 64594 BaseAxisPointer.prototype = { 64595 64596 /** 64597 * @private 64598 */ 64599 _group: null, 64600 64601 /** 64602 * @private 64603 */ 64604 _lastGraphicKey: null, 64605 64606 /** 64607 * @private 64608 */ 64609 _handle: null, 64610 64611 /** 64612 * @private 64613 */ 64614 _dragging: false, 64615 64616 /** 64617 * @private 64618 */ 64619 _lastValue: null, 64620 64621 /** 64622 * @private 64623 */ 64624 _lastStatus: null, 64625 64626 /** 64627 * @private 64628 */ 64629 _payloadInfo: null, 64630 64631 /** 64632 * In px, arbitrary value. Do not set too small, 64633 * no animation is ok for most cases. 64634 * @protected 64635 */ 64636 animationThreshold: 15, 64637 64638 /** 64639 * @implement 64640 */ 64641 render: function (axisModel, axisPointerModel, api, forceRender) { 64642 var value = axisPointerModel.get('value'); 64643 var status = axisPointerModel.get('status'); 64644 64645 // Bind them to `this`, not in closure, otherwise they will not 64646 // be replaced when user calling setOption in not merge mode. 64647 this._axisModel = axisModel; 64648 this._axisPointerModel = axisPointerModel; 64649 this._api = api; 64650 64651 // Optimize: `render` will be called repeatly during mouse move. 64652 // So it is power consuming if performing `render` each time, 64653 // especially on mobile device. 64654 if (!forceRender 64655 && this._lastValue === value 64656 && this._lastStatus === status 64657 ) { 64658 return; 64659 } 64660 this._lastValue = value; 64661 this._lastStatus = status; 64662 64663 var group = this._group; 64664 var handle = this._handle; 64665 64666 if (!status || status === 'hide') { 64667 // Do not clear here, for animation better. 64668 group && group.hide(); 64669 handle && handle.hide(); 64670 return; 64671 } 64672 group && group.show(); 64673 handle && handle.show(); 64674 64675 // Otherwise status is 'show' 64676 var elOption = {}; 64677 this.makeElOption(elOption, value, axisModel, axisPointerModel, api); 64678 64679 // Enable change axis pointer type. 64680 var graphicKey = elOption.graphicKey; 64681 if (graphicKey !== this._lastGraphicKey) { 64682 this.clear(api); 64683 } 64684 this._lastGraphicKey = graphicKey; 64685 64686 var moveAnimation = this._moveAnimation = 64687 this.determineAnimation(axisModel, axisPointerModel); 64688 64689 if (!group) { 64690 group = this._group = new Group(); 64691 this.createPointerEl(group, elOption, axisModel, axisPointerModel); 64692 this.createLabelEl(group, elOption, axisModel, axisPointerModel); 64693 api.getZr().add(group); 64694 } 64695 else { 64696 var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation); 64697 this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel); 64698 this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel); 64699 } 64700 64701 updateMandatoryProps(group, axisPointerModel, true); 64702 64703 this._renderHandle(value); 64704 }, 64705 64706 /** 64707 * @implement 64708 */ 64709 remove: function (api) { 64710 this.clear(api); 64711 }, 64712 64713 /** 64714 * @implement 64715 */ 64716 dispose: function (api) { 64717 this.clear(api); 64718 }, 64719 64720 /** 64721 * @protected 64722 */ 64723 determineAnimation: function (axisModel, axisPointerModel) { 64724 var animation = axisPointerModel.get('animation'); 64725 var axis = axisModel.axis; 64726 var isCategoryAxis = axis.type === 'category'; 64727 var useSnap = axisPointerModel.get('snap'); 64728 64729 // Value axis without snap always do not snap. 64730 if (!useSnap && !isCategoryAxis) { 64731 return false; 64732 } 64733 64734 if (animation === 'auto' || animation == null) { 64735 var animationThreshold = this.animationThreshold; 64736 if (isCategoryAxis && axis.getBandWidth() > animationThreshold) { 64737 return true; 64738 } 64739 64740 // It is important to auto animation when snap used. Consider if there is
64741 // a dataZoom, animation will be disabled when too many points exist, while 64742 // it will be enabled for better visual effect when little points exist. 64743 if (useSnap) { 64744 var seriesDataCount = getAxisInfo(axisModel).seriesDataCount; 64745 var axisExtent = axis.getExtent(); 64746 // Approximate band width 64747 return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold; 64748 } 64749 64750 return false; 64751 } 64752 64753 return animation === true; 64754 }, 64755 64756 /** 64757 * add {pointer, label, graphicKey} to elOption 64758 * @protected 64759 */ 64760 makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { 64761 // Shoule be implemenented by sub-class. 64762 }, 64763 64764 /** 64765 * @protected 64766 */ 64767 createPointerEl: function (group, elOption, axisModel, axisPointerModel) { 64768 var pointerOption = elOption.pointer; 64769 if (pointerOption) { 64770 var pointerEl = inner$9(group).pointerEl = new graphic[pointerOption.type]( 64771 clone$4(elOption.pointer) 64772 ); 64773 group.add(pointerEl); 64774 } 64775 }, 64776 64777 /** 64778 * @protected 64779 */ 64780 createLabelEl: function (group, elOption, axisModel, axisPointerModel) { 64781 if (elOption.label) { 64782 var labelEl = inner$9(group).labelEl = new Rect( 64783 clone$4(elOption.label) 64784 ); 64785 64786 group.add(labelEl); 64787 updateLabelShowHide(labelEl, axisPointerModel); 64788 } 64789 }, 64790 64791 /** 64792 * @protected 64793 */ 64794 updatePointerEl: function (group, elOption, updateProps$$1) { 64795 var pointerEl = inner$9(group).pointerEl; 64796 if (pointerEl) { 64797 pointerEl.setStyle(elOption.pointer.style); 64798 updateProps$$1(pointerEl, {shape: elOption.pointer.shape}); 64799 } 64800 }, 64801 64802 /** 64803 * @protected 64804 */ 64805 updateLabelEl: function (group, elOption, updateProps$$1, axisPointerModel) { 64806 var labelEl = inner$9(group).labelEl; 64807 if (labelEl) { 64808 labelEl.setStyle(elOption.label.style); 64809 updateProps$$1(labelEl, { 64810 // Consider text length change in vertical axis, animation should 64811 // be used on shape, otherwise the effect will be weird. 64812 shape: elOption.label.shape, 64813 position: elOption.label.position 64814 }); 64815 64816 updateLabelShowHide(labelEl, axisPointerModel); 64817 } 64818 }, 64819 64820 /** 64821 * @private 64822 */ 64823 _renderHandle: function (value) { 64824 if (this._dragging || !this.updateHandleTransform) { 64825 return; 64826 } 64827 64828 var axisPointerModel = this._axisPointerModel; 64829 var zr = this._api.getZr(); 64830 var handle = this._handle; 64831 var handleModel = axisPointerModel.getModel('handle'); 64832 64833 var status = axisPointerModel.get('status'); 64834 if (!handleModel.get('show') || !status || status === 'hide') { 64835 handle && zr.remove(handle); 64836 this._handle = null; 64837 return; 64838 } 64839 64840 var isInit; 64841 if (!this._handle) { 64842 isInit = true; 64843 handle = this._handle = createIcon( 64844 handleModel.get('icon'), 64845 { 64846 cursor: 'move', 64847 draggable: true, 64848 onmousemove: function (e) { 64849 // Fot mobile devicem, prevent screen slider on the button. 64850 stop(e.event); 64851 }, 64852 onmousedown: bind$2(this._onHandleDragMove, this, 0, 0), 64853 drift: bind$2(this._onHandleDragMove, this), 64854 ondragend: bind$2(this._onHandleDragEnd, this) 64855 } 64856 ); 64857 zr.add(handle); 64858 } 64859 64860 updateMandatoryProps(handle, axisPointerModel, false); 64861 64862 // update style 64863 var includeStyles = [ 64864 'color', 'borderColor', 'borderWidth', 'opacity', 64865 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY' 64866 ]; 64867 handle.setStyle(handleModel.getItemStyle(null, includeStyles)); 64868 64869 // update position 64870 var handleSize = handleModel.get('size'); 64871 if (!isArray(handleSize)) { 64872 handleSize = [handleSize, handleSize]; 64873 } 64874 handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]); 64875 64876 createOrUpdate( 64877 this,
64878 '_doDispatchAxisPointer', 64879 handleModel.get('throttle') || 0, 64880 'fixRate' 64881 ); 64882 64883 this._moveHandleToValue(value, isInit); 64884 }, 64885 64886 /** 64887 * @private 64888 */ 64889 _moveHandleToValue: function (value, isInit) { 64890 updateProps$1( 64891 this._axisPointerModel, 64892 !isInit && this._moveAnimation, 64893 this._handle, 64894 getHandleTransProps(this.getHandleTransform( 64895 value, this._axisModel, this._axisPointerModel 64896 )) 64897 ); 64898 }, 64899 64900 /** 64901 * @private 64902 */ 64903 _onHandleDragMove: function (dx, dy) { 64904 var handle = this._handle; 64905 if (!handle) { 64906 return; 64907 } 64908 64909 this._dragging = true; 64910 64911 // Persistent for throttle. 64912 var trans = this.updateHandleTransform( 64913 getHandleTransProps(handle), 64914 [dx, dy], 64915 this._axisModel, 64916 this._axisPointerModel 64917 ); 64918 this._payloadInfo = trans; 64919 64920 handle.stopAnimation(); 64921 handle.attr(getHandleTransProps(trans)); 64922 inner$9(handle).lastProp = null; 64923 64924 this._doDispatchAxisPointer(); 64925 }, 64926 64927 /** 64928 * Throttled method. 64929 * @private 64930 */ 64931 _doDispatchAxisPointer: function () { 64932 var handle = this._handle; 64933 if (!handle) { 64934 return; 64935 } 64936 64937 var payloadInfo = this._payloadInfo; 64938 var axisModel = this._axisModel; 64939 this._api.dispatchAction({ 64940 type: 'updateAxisPointer', 64941 x: payloadInfo.cursorPoint[0], 64942 y: payloadInfo.cursorPoint[1], 64943 tooltipOption: payloadInfo.tooltipOption, 64944 axesInfo: [{ 64945 axisDim: axisModel.axis.dim, 64946 axisIndex: axisModel.componentIndex 64947 }] 64948 }); 64949 }, 64950 64951 /** 64952 * @private 64953 */ 64954 _onHandleDragEnd: function (moveAnimation) { 64955 this._dragging = false; 64956 var handle = this._handle; 64957 if (!handle) { 64958 return; 64959 } 64960 64961 var value = this._axisPointerModel.get('value'); 64962 // Consider snap or categroy axis, handle may be not consistent with 64963 // axisPointer. So move handle to align the exact value position when 64964 // drag ended. 64965 this._moveHandleToValue(value); 64966 64967 // For the effect: tooltip will be shown when finger holding on handle 64968 // button, and will be hidden after finger left handle button. 64969 this._api.dispatchAction({ 64970 type: 'hideTip' 64971 }); 64972 }, 64973 64974 /** 64975 * Should be implemenented by sub-class if support `handle`. 64976 * @protected 64977 * @param {number} value 64978 * @param {module:echarts/model/Model} axisModel 64979 * @param {module:echarts/model/Model} axisPointerModel 64980 * @return {Object} {position: [x, y], rotation: 0} 64981 */ 64982 getHandleTransform: null, 64983 64984 /** 64985 * * Should be implemenented by sub-class if support `handle`. 64986 * @protected 64987 * @param {Object} transform {position, rotation} 64988 * @param {Array.<number>} delta [dx, dy] 64989 * @param {module:echarts/model/Model} axisModel 64990 * @param {module:echarts/model/Model} axisPointerModel 64991 * @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]} 64992 */ 64993 updateHandleTransform: null, 64994 64995 /** 64996 * @private 64997 */ 64998 clear: function (api) { 64999 this._lastValue = null; 65000 this._lastStatus = null; 65001 65002 var zr = api.getZr(); 65003 var group = this._group; 65004 var handle = this._handle; 65005 if (zr && group) { 65006 this._lastGraphicKey = null; 65007 group && zr.remove(group); 65008 handle && zr.remove(handle); 65009 this._group = null; 65010 this._handle = null; 65011 this._payloadInfo = null; 65012 } 65013 }, 65014 65015 /** 65016 * @protected 65017 */ 65018 doClear: function () { 65019 // Implemented by sub-class if necessary. 65020 }, 65021 65022 /** 65023 * @protected 65024 * @param {Array.<number>} xy 65025 * @param {Array.<number>} wh 65026 * @param {number} [xDimIndex=0] or 1 65027 */ 65028 buildLabel: function (xy, wh, xDimIndex) { 65029 xDimIndex = xDimIndex || 0; 65030 return { 65031 x: xy[xDimIndex], 65032 y: xy[1 - xDimIndex], 65033 width: wh[xDimIndex], 65034 height: wh[1 - xDimIndex] 65035 }; 65036 } 65037 }; 65038 65039 BaseAxisPointer.prototype.constructor = BaseAxisPointer; 65040 65041 65042 function updateProps$1(animationModel, moveAnimation, el, props) { 65043 // Animation optimize. 65044 if (!propsEqual(inner$9(el).lastProp, props)) { 65045 inner$9(el).lastProp = props; 65046 moveAnimation 65047 ? updateProps(el, props, animationModel) 65048 : (el.stopAnimation(), el.attr(props)); 65049 } 65050 } 65051 65052 function propsEqual(lastProps, newProps) { 65053 if (isObject$1(lastProps) && isObject$1(newProps)) { 65054 var equals = true; 65055 each$1(newProps, function (item, key) { 65056 equals = equals && propsEqual(lastProps[key], item); 65057 }); 65058 return !!equals; 65059 } 65060 else { 65061 return lastProps === newProps; 65062 } 65063 } 65064 65065 function updateLabelShowHide(labelEl, axisPointerModel) { 65066 labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide'](); 65067 } 65068 65069 function getHandleTransProps(trans) { 65070 return { 65071 position: trans.position.slice(), 65072 rotation: trans.rotation || 0 65073 }; 65074 } 65075 65076 function updateMandatoryProps(group, axisPointerModel, silent) { 65077 var z = axisPointerModel.get('z'); 65078 var zlevel = axisPointerModel.get('zlevel'); 65079 65080 group && group.traverse(function (el) { 65081 if (el.type !== 'group') { 65082 z != null && (el.z = z); 65083 zlevel != null && (el.zlevel = zlevel); 65084 el.silent = silent; 65085 } 65086 }); 65087 } 65088 65089 enableClassExtend(BaseAxisPointer); 65090 65091 /* 65092* Licensed to the Apache Software Foundation (ASF) under one 65093* or more contributor license agreements. See the NOTICE file 65094* distributed with this work for additional information 65095* regarding copyright ownership. The ASF licenses this file 65096* to you under the Apache License, Version 2.0 (the 65097* "License");
65097 you may not use this file except in compliance 65098* with the License. You may obtain a copy of the License at 65099* 65100* http://www.apache.org/licenses/LICENSE-2.0 65101* 65102* Unless required by applicable law or agreed to in writing, 65103* software distributed under the License is distributed on an 65104* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65105* KIND, either express or implied. See the License for the 65106* specific language governing permissions and limitations 65107* under the License. 65108*/ 65109 65110 /** 65111 * @param {module:echarts/model/Model} axisPointerModel 65112 */ 65113 function buildElStyle(axisPointerModel) { 65114 var axisPointerType = axisPointerModel.get('type'); 65115 var styleModel = axisPointerModel.getModel(axisPointerType + 'Style'); 65116 var style; 65117 if (axisPointerType === 'line') { 65118 style = styleModel.getLineStyle(); 65119 style.fill = null; 65120 } 65121 else if (axisPointerType === 'shadow') { 65122 style = styleModel.getAreaStyle(); 65123 style.stroke = null; 65124 } 65125 return style; 65126 } 65127 65128 /** 65129 * @param {Function} labelPos {align, verticalAlign, position} 65130 */ 65131 function buildLabelElOption( 65132 elOption, axisModel, axisPointerModel, api, labelPos 65133 ) { 65134 var value = axisPointerModel.get('value'); 65135 var text = getValueLabel( 65136 value, axisModel.axis, axisModel.ecModel, 65137 axisPointerModel.get('seriesDataIndices'), 65138 { 65139 precision: axisPointerModel.get('label.precision'), 65140 formatter: axisPointerModel.get('label.formatter') 65141 } 65142 ); 65143 var labelModel = axisPointerModel.getModel('label'); 65144 var paddings = normalizeCssArray$1(labelModel.get('padding') || 0); 65145 65146 var font = labelModel.getFont(); 65147 var textRect = getBoundingRect(text, font); 65148 65149 var position = labelPos.position; 65150 var width = textRect.width + paddings[1] + paddings[3]; 65151 var height = textRect.height + paddings[0] + paddings[2]; 65152 65153 // Adjust by align. 65154 var align = labelPos.align; 65155 align === 'right' && (position[0] -= width); 65156 align === 'center' && (position[0] -= width / 2); 65157 var verticalAlign = labelPos.verticalAlign; 65158 verticalAlign === 'bottom' && (position[1] -= height); 65159 verticalAlign === 'middle' && (position[1] -= height / 2); 65160 65161 // Not overflow ec container 65162 confineInContainer(position, width, height, api); 65163 65164 var bgColor = labelModel.get('backgroundColor'); 65165 if (!bgColor || bgColor === 'auto') { 65166 bgColor = axisModel.get('axisLine.lineStyle.color'); 65167 } 65168 65169 elOption.label = { 65170 shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')}, 65171 position: position.slice(), 65172 // TODO: rich 65173 style: { 65174 text: text, 65175 textFont: font, 65176 textFill: labelModel.getTextColor(), 65177 textPosition: 'inside', 65178 fill: bgColor, 65179 stroke: labelModel.get('borderColor') || 'transparent', 65180 lineWidth: labelModel.get('borderWidth') || 0, 65181 shadowBlur: labelModel.get('shadowBlur'), 65182 shadowColor: labelModel.get('shadowColor'), 65183 shadowOffsetX: labelModel.get('shadowOffsetX'), 65184 shadowOffsetY: labelModel.get('shadowOffsetY') 65185 }, 65186 // Lable should be over axisPointer. 65187 z2: 10 65188 }; 65189 } 65190 65191// Do not overflow ec container 65192 function confineInContainer(position, width, height, api) { 65193 var viewWidth = api.getWidth(); 65194 var viewHeight = api.getHeight(); 65195 position[0] = Math.min(position[0] + width, viewWidth) - width; 65196 position[1] = Math.min(position[1] + height, viewHeight) - height; 65197 position[0] = Math.max(position[0], 0); 65198 position[1] = Math.max(position[1], 0); 65199 } 65200 65201 /** 65202 * @param {number} value 65203 * @param {module:echarts/coord/Axis} axis 65204 * @param {module:echarts/model/Global} ecModel 65205 * @param {Object} opt 65206 * @param {Array.<Object>} seriesDataIndices 65207 * @param {number|string} opt.precision 'auto' or a number 65208 * @param {string|Function} opt.formatter label formatter 65209 */ 65210 function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) { 65211 value = axis.scale.parse(value); 65212 var text = axis.scale.getLabel( 65213 // If `precision` is set, width can be fixed (like '12.00500'), which 65214 // helps to debounce when when moving label. 65215 value, {precision: opt.precision} 65216 ); 65217 var formatter = opt.formatter; 65218 65219 if (formatter) { 65220 var params = { 65221 value: getAxisRawValue(axis, value), 65222 seriesData: [] 65223 };
vendor: 6,734 bytes, lines 65224-65402
65224 each$1(seriesDataIndices, function (idxItem) { 65225 var series = ecModel.getSeriesByIndex(idxItem.seriesIndex); 65226 var dataIndex = idxItem.dataIndexInside; 65227 var dataParams = series && series.getDataParams(dataIndex); 65228 dataParams && params.seriesData.push(dataParams); 65229 }); 65230 65231 if (isString(formatter)) { 65232 text = formatter.replace('{value}', text); 65233 } 65234 else if (isFunction$1(formatter)) { 65235 text = formatter(params); 65236 } 65237 } 65238 65239 return text; 65240 } 65241 65242 /** 65243 * @param {module:echarts/coord/Axis} axis 65244 * @param {number} value 65245 * @param {Object} layoutInfo { 65246 * rotation, position, labelOffset, labelDirection, labelMargin 65247 * } 65248 */ 65249 function getTransformedPosition (axis, value, layoutInfo) { 65250 var transform = create$1(); 65251 rotate(transform, transform, layoutInfo.rotation); 65252 translate(transform, transform, layoutInfo.position); 65253 65254 return applyTransform$1([ 65255 axis.dataToCoord(value), 65256 (layoutInfo.labelOffset || 0) 65257 + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0) 65258 ], transform); 65259 } 65260 65261 function buildCartesianSingleLabelElOption( 65262 value, elOption, layoutInfo, axisModel, axisPointerModel, api 65263 ) { 65264 var textLayout = AxisBuilder.innerTextLayout( 65265 layoutInfo.rotation, 0, layoutInfo.labelDirection 65266 ); 65267 layoutInfo.labelMargin = axisPointerModel.get('label.margin'); 65268 buildLabelElOption(elOption, axisModel, axisPointerModel, api, { 65269 position: getTransformedPosition(axisModel.axis, value, layoutInfo), 65270 align: textLayout.textAlign, 65271 verticalAlign: textLayout.textVerticalAlign 65272 }); 65273 } 65274 65275 /** 65276 * @param {Array.<number>} p1 65277 * @param {Array.<number>} p2 65278 * @param {number} [xDimIndex=0] or 1 65279 */ 65280 function makeLineShape(p1, p2, xDimIndex) { 65281 xDimIndex = xDimIndex || 0; 65282 return { 65283 x1: p1[xDimIndex], 65284 y1: p1[1 - xDimIndex], 65285 x2: p2[xDimIndex], 65286 y2: p2[1 - xDimIndex] 65287 }; 65288 } 65289 65290 /** 65291 * @param {Array.<number>} xy 65292 * @param {Array.<number>} wh 65293 * @param {number} [xDimIndex=0] or 1 65294 */ 65295 function makeRectShape(xy, wh, xDimIndex) { 65296 xDimIndex = xDimIndex || 0; 65297 return { 65298 x: xy[xDimIndex], 65299 y: xy[1 - xDimIndex], 65300 width: wh[xDimIndex], 65301 height: wh[1 - xDimIndex] 65302 }; 65303 } 65304 65305 function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) { 65306 return { 65307 cx: cx, 65308 cy: cy, 65309 r0: r0, 65310 r: r, 65311 startAngle: startAngle, 65312 endAngle: endAngle, 65313 clockwise: true 65314 }; 65315 } 65316 65317 /* 65318* Licensed to the Apache Software Foundation (ASF) under one 65319* or more contributor license agreements. See the NOTICE file 65320* distributed with this work for additional information 65321* regarding copyright ownership. The ASF licenses this file 65322* to you under the Apache License, Version 2.0 (the 65323* "License"); you may not use this file except in compliance 65324* with the License. You may obtain a copy of the License at 65325* 65326* http://www.apache.org/licenses/LICENSE-2.0 65327* 65328* Unless required by applicable law or agreed to in writing, 65329* software distributed under the License is distributed on an 65330* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65331* KIND, either express or implied. See the License for the 65332* specific language governing permissions and limitations 65333* under the License. 65334*/ 65335 65336 var CartesianAxisPointer = BaseAxisPointer.extend({ 65337 65338 /** 65339 * @override 65340 */ 65341 makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { 65342 var axis = axisModel.axis; 65343 var grid = axis.grid; 65344 var axisPointerType = axisPointerModel.get('type'); 65345 var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent(); 65346 var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true)); 65347 65348 if (axisPointerType && axisPointerType !== 'none') { 65349 var elStyle = buildElStyle(axisPointerModel); 65350 var pointerOption = pointerShapeBuilder[axisPointerType]( 65351 axis, pixelValue, otherExtent, elStyle 65352 ); 65353 pointerOption.style = elStyle; 65354 elOption.graphicKey = pointerOption.type; 65355 elOption.pointer = pointerOption; 65356 } 65357 65358 var layoutInfo = layout$1(grid.model, axisModel); 65359 buildCartesianSingleLabelElOption( 65360 value, elOption, layoutInfo, axisModel, axisPointerModel, api 65361 ); 65362 }, 65363 65364 /** 65365 * @override 65366 */ 65367 getHandleTransform: function (value, axisModel, axisPointerModel) { 65368 var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, { 65369 labelInside: false 65370 }); 65371 layoutInfo.labelMargin = axisPointerModel.get('handle.margin'); 65372 return { 65373 position: getTransformedPosition(axisModel.axis, value, layoutInfo), 65374 rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0) 65375 }; 65376 }, 65377 65378 /** 65379 * @override 65380 */ 65381 updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) { 65382 var axis = axisModel.axis; 65383 var grid = axis.grid; 65384 var axisExtent = axis.getGlobalExtent(true); 65385 var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent(); 65386 var dimIndex = axis.dim === 'x' ? 0 : 1; 65387 65388 var currPosition = transform.position; 65389 currPosition[dimIndex] += delta[dimIndex]; 65390 currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]); 65391 currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]); 65392 65393 var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2; 65394 var cursorPoint = [cursorOtherValue, cursorOtherValue]; 65395 cursorPoint[dimIndex] = currPosition[dimIndex]; 65396 65397 // Make tooltip do not overlap axisPointer and in the middle of the grid. 65398 var tooltipOptions = [{verticalAlign: 'middle'}, {align: 'center'}]; 65399 65400 return { 65401 position: currPosition, 65402 rotation: transform.rotation,
65403 cursorPoint: cursorPoint, 65404 tooltipOption: tooltipOptions[dimIndex] 65405 }; 65406 } 65407 65408 }); 65409 65410 function getCartesian(grid, axis) { 65411 var opt = {}; 65412 opt[axis.dim + 'AxisIndex'] = axis.index; 65413 return grid.getCartesian(opt); 65414 } 65415 65416 var pointerShapeBuilder = { 65417 65418 line: function (axis, pixelValue, otherExtent, elStyle) { 65419 var targetShape = makeLineShape( 65420 [pixelValue, otherExtent[0]], 65421 [pixelValue, otherExtent[1]], 65422 getAxisDimIndex(axis) 65423 ); 65424 subPixelOptimizeLine({ 65425 shape: targetShape, 65426 style: elStyle 65427 }); 65428 return { 65429 type: 'Line', 65430 shape: targetShape 65431 }; 65432 }, 65433 65434 shadow: function (axis, pixelValue, otherExtent, elStyle) { 65435 var bandWidth = Math.max(1, axis.getBandWidth()); 65436 var span = otherExtent[1] - otherExtent[0]; 65437 return { 65438 type: 'Rect', 65439 shape: makeRectShape( 65440 [pixelValue - bandWidth / 2, otherExtent[0]], 65441 [bandWidth, span], 65442 getAxisDimIndex(axis) 65443 ) 65444 }; 65445 } 65446 }; 65447 65448 function getAxisDimIndex(axis) { 65449 return axis.dim === 'x' ? 0 : 1; 65450 } 65451 65452 AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer); 65453 65454 /* 65455* Licensed to the Apache Software Foundation (ASF) under one 65456* or more contributor license agreements. See the NOTICE file 65457* distributed with this work for additional information 65458* regarding copyright ownership. The ASF licenses this file 65459* to you under the Apache License, Version 2.0 (the 65460* "License"); you may not use this file except in compliance 65461* with the License. You may obtain a copy of the License at 65462* 65463* http://www.apache.org/licenses/LICENSE-2.0 65464* 65465* Unless required by applicable law or agreed to in writing, 65466* software distributed under the License is distributed on an 65467* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65468* KIND, either express or implied. See the License for the 65469* specific language governing permissions and limitations 65470* under the License. 65471*/ 65472 65473// CartesianAxisPointer is not supposed to be required here. But consider 65474// echarts.simple.js and online build tooltip, which only require gridSimple, 65475// CartesianAxisPointer should be able to required somewhere. 65476 registerPreprocessor(function (option) { 65477 // Always has a global axisPointerModel for default setting. 65478 if (option) { 65479 (!option.axisPointer || option.axisPointer.length === 0) 65480 && (option.axisPointer = {}); 65481 65482 var link = option.axisPointer.link; 65483 // Normalize to array to avoid object mergin. But if link 65484 // is not set, remain null/undefined, otherwise it will 65485 // override existent link setting. 65486 if (link && !isArray(link)) { 65487 option.axisPointer.link = [link]; 65488 } 65489 } 65490 }); 65491 65492// This process should proformed after coordinate systems created 65493// and series data processed. So put it on statistic processing stage. 65494 registerProcessor(PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) { 65495 // Build axisPointerModel, mergin tooltip.axisPointer model for each axis. 65496 // allAxesInfo should be updated when setOption performed. 65497 ecModel.getComponent('axisPointer').coordSysAxesInfo 65498 = collect(ecModel, api); 65499 }); 65500 65501// Broadcast to all views. 65502 registerAction({ 65503 type: 'updateAxisPointer', 65504 event: 'updateAxisPointer', 65505 update: ':updateAxisPointer' 65506 }, axisTrigger); 65507 65508 /* 65509* Licensed to the Apache Software Foundation (ASF) under one 65510* or more contributor license agreements. See the NOTICE file 65511* distributed with this work for additional information 65512* regarding copyright ownership. The ASF licenses this file 65513* to you under the Apache License, Version 2.0 (the 65514* "License");
65514 you may not use this file except in compliance 65515* with the License. You may obtain a copy of the License at 65516* 65517* http://www.apache.org/licenses/LICENSE-2.0 65518* 65519* Unless required by applicable law or agreed to in writing, 65520* software distributed under the License is distributed on an 65521* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65522* KIND, either express or implied. See the License for the 65523* specific language governing permissions and limitations 65524* under the License. 65525*/ 65526 65527 var XY = ['x', 'y']; 65528 var WH = ['width', 'height']; 65529 65530 var SingleAxisPointer = BaseAxisPointer.extend({ 65531 65532 /** 65533 * @override 65534 */ 65535 makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { 65536 var axis = axisModel.axis; 65537 var coordSys = axis.coordinateSystem; 65538 var otherExtent = getGlobalExtent(coordSys, 1 - getPointDimIndex(axis)); 65539 var pixelValue = coordSys.dataToPoint(value)[0]; 65540 65541 var axisPointerType = axisPointerModel.get('type'); 65542 if (axisPointerType && axisPointerType !== 'none') { 65543 var elStyle = buildElStyle(axisPointerModel); 65544 var pointerOption = pointerShapeBuilder$1[axisPointerType]( 65545 axis, pixelValue, otherExtent, elStyle 65546 ); 65547 pointerOption.style = elStyle; 65548 65549 elOption.graphicKey = pointerOption.type; 65550 elOption.pointer = pointerOption; 65551 } 65552 65553 var layoutInfo = layout$2(axisModel); 65554 buildCartesianSingleLabelElOption( 65555 value, elOption, layoutInfo, axisModel, axisPointerModel, api 65556 ); 65557 }, 65558 65559 /** 65560 * @override 65561 */ 65562 getHandleTransform: function (value, axisModel, axisPointerModel) { 65563 var layoutInfo = layout$2(axisModel, {labelInside: false}); 65564 layoutInfo.labelMargin = axisPointerModel.get('handle.margin'); 65565 return { 65566 position: getTransformedPosition(axisModel.axis, value, layoutInfo), 65567 rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0) 65568 }; 65569 }, 65570 65571 /** 65572 * @override 65573 */ 65574 updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) { 65575 var axis = axisModel.axis; 65576 var coordSys = axis.coordinateSystem; 65577 var dimIndex = getPointDimIndex(axis); 65578 var axisExtent = getGlobalExtent(coordSys, dimIndex); 65579 var currPosition = transform.position; 65580 currPosition[dimIndex] += delta[dimIndex]; 65581 currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]); 65582 currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]); 65583 var otherExtent = getGlobalExtent(coordSys, 1 - dimIndex); 65584 var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2; 65585 var cursorPoint = [cursorOtherValue, cursorOtherValue]; 65586 cursorPoint[dimIndex] = currPosition[dimIndex]; 65587 65588 return { 65589 position: currPosition, 65590 rotation: transform.rotation, 65591 cursorPoint: cursorPoint, 65592 tooltipOption: { 65593 verticalAlign: 'middle' 65594 } 65595 }; 65596 } 65597 }); 65598 65599 var pointerShapeBuilder$1 = { 65600 65601 line: function (axis, pixelValue, otherExtent, elStyle) { 65602 var targetShape = makeLineShape( 65603 [pixelValue, otherExtent[0]], 65604 [pixelValue, otherExtent[1]], 65605 getPointDimIndex(axis) 65606 ); 65607 subPixelOptimizeLine({ 65608 shape: targetShape, 65609 style: elStyle 65610 }); 65611 return { 65612 type: 'Line', 65613 shape: targetShape 65614 }; 65615 }, 65616 65617 shadow: function (axis, pixelValue, otherExtent, elStyle) { 65618 var bandWidth = axis.getBandWidth(); 65619 var span = otherExtent[1] - otherExtent[0]; 65620 return { 65621 type: 'Rect', 65622 shape: makeRectShape( 65623 [pixelValue - bandWidth / 2, otherExtent[0]], 65624 [bandWidth, span], 65625 getPointDimIndex(axis) 65626 ) 65627 }; 65628 } 65629 }; 65630 65631 function getPointDimIndex(axis) { 65632 return axis.isHorizontal() ? 0 : 1; 65633 } 65634 65635 function getGlobalExtent(coordSys, dimIndex) { 65636 var rect = coordSys.getRect(); 65637 return [rect[XY[dimIndex]], rect[XY[dimIndex]] + rect[WH[dimIndex]]]; 65638 } 65639 65640 AxisView.registerAxisPointerClass('SingleAxisPointer', SingleAxisPointer); 65641 65642 /* 65643* Licensed to the Apache Software Foundation (ASF) under one 65644* or more contributor license agreements. See the NOTICE file 65645* distributed with this work for additional information 65646* regarding copyright ownership. The ASF licenses this file 65647* to you under the Apache License, Version 2.0 (the 65648* "License");
65648 you may not use this file except in compliance 65649* with the License. You may obtain a copy of the License at 65650* 65651* http://www.apache.org/licenses/LICENSE-2.0 65652* 65653* Unless required by applicable law or agreed to in writing, 65654* software distributed under the License is distributed on an 65655* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65656* KIND, either express or implied. See the License for the 65657* specific language governing permissions and limitations 65658* under the License. 65659*/ 65660 65661 extendComponentView({ 65662 type: 'single' 65663 }); 65664 65665 /* 65666* Licensed to the Apache Software Foundation (ASF) under one 65667* or more contributor license agreements. See the NOTICE file 65668* distributed with this work for additional information 65669* regarding copyright ownership. The ASF licenses this file 65670* to you under the Apache License, Version 2.0 (the 65671* "License"); you may not use this file except in compliance 65672* with the License. You may obtain a copy of the License at 65673* 65674* http://www.apache.org/licenses/LICENSE-2.0 65675* 65676* Unless required by applicable law or agreed to in writing, 65677* software distributed under the License is distributed on an 65678* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65679* KIND, either express or implied. See the License for the 65680* specific language governing permissions and limitations 65681* under the License. 65682*/ 65683 65684 /** 65685 * @file Define the themeRiver view's series model 65686 * @author Deqing Li([email protected]) 65687 */ 65688 65689 var DATA_NAME_INDEX = 2; 65690 65691 var ThemeRiverSeries = SeriesModel.extend({ 65692 65693 type: 'series.themeRiver', 65694 65695 dependencies: ['singleAxis'], 65696 65697 /** 65698 * @readOnly 65699 * @type {module:zrender/core/util#HashMap} 65700 */ 65701 nameMap: null, 65702 65703 /** 65704 * @override 65705 */ 65706 init: function (option) { 65707 ThemeRiverSeries.superApply(this, 'init', arguments); 65708 65709 // Put this function here is for the sake of consistency of code style. 65710 // Enable legend selection for each data item 65711 // Use a function instead of direct access because data reference may changed 65712 this.legendDataProvider = function () { 65713 return this.getRawData(); 65714 }; 65715 }, 65716 65717 /** 65718 * If there is no value of a certain point in the time for some event,set it value to 0. 65719 * 65720 * @param {Array} data initial data in the option 65721 * @return {Array} 65722 */ 65723 fixData: function (data) { 65724 var rawDataLength = data.length; 65725 65726 // grouped data by name 65727 var dataByName = nest() 65728 .key(function (dataItem) { 65729 return dataItem[2]; 65730 }) 65731 .entries(data); 65732 65733 // data group in each layer 65734 var layData = map(dataByName, function (d) { 65735 return { 65736 name: d.key, 65737 dataList: d.values 65738 }; 65739 }); 65740 65741 var layerNum = layData.length; 65742 var largestLayer = -1; 65743 var index = -1; 65744 for (var i = 0; i < layerNum; ++i) { 65745 var len = layData[i].dataList.length; 65746 if (len > largestLayer) { 65747 largestLayer = len; 65748 index = i; 65749 } 65750 } 65751 65752 for (var k = 0; k < layerNum; ++k) { 65753 if (k === index) { 65754 continue; 65755 } 65756 var name = layData[k].name; 65757 for (var j = 0; j < largestLayer; ++j) { 65758 var timeValue = layData[index].dataList[j][0]; 65759 var length = layData[k].dataList.length; 65760 var keyIndex = -1; 65761 for (var l = 0; l < length; ++l) { 65762 var value = layData[k].dataList[l][0]; 65763 if (value === timeValue) { 65764 keyIndex = l; 65765 break; 65766 } 65767 } 65768 if (keyIndex === -1) { 65769 data[rawDataLength] = []; 65770 data[rawDataLength][0] = timeValue; 65771 data[rawDataLength][1] = 0; 65772 data[rawDataLength][2] = name; 65773 rawDataLength++; 65774 65775 } 65776 } 65777 } 65778 return data; 65779 }, 65780 65781 /** 65782 * @override 65783 * @param {Object} option the initial option that user gived 65784 * @param {module:echarts/model/Model} ecModel the model object for themeRiver option
65785 * @return {module:echarts/data/List} 65786 */ 65787 getInitialData: function (option, ecModel) { 65788 65789 var singleAxisModel = ecModel.queryComponents({ 65790 mainType: 'singleAxis', 65791 index: this.get('singleAxisIndex'), 65792 id: this.get('singleAxisId') 65793 })[0]; 65794 65795 var axisType = singleAxisModel.get('type'); 65796 65797 // filter the data item with the value of label is undefined 65798 var filterData = filter(option.data, function (dataItem) { 65799 return dataItem[2] !== undefined; 65800 }); 65801 65802 // ??? TODO design a stage to transfer data for themeRiver and lines? 65803 var data = this.fixData(filterData || []); 65804 var nameList = []; 65805 var nameMap = this.nameMap = createHashMap(); 65806 var count = 0; 65807 65808 for (var i = 0; i < data.length; ++i) { 65809 nameList.push(data[i][DATA_NAME_INDEX]); 65810 if (!nameMap.get(data[i][DATA_NAME_INDEX])) { 65811 nameMap.set(data[i][DATA_NAME_INDEX], count); 65812 count++; 65813 } 65814 } 65815 65816 var dimensionsInfo = createDimensions(data, { 65817 coordDimensions: ['single'], 65818 dimensionsDefine: [ 65819 { 65820 name: 'time', 65821 type: getDimensionTypeByAxis(axisType) 65822 }, 65823 { 65824 name: 'value', 65825 type: 'float' 65826 }, 65827 { 65828 name: 'name', 65829 type: 'ordinal' 65830 } 65831 ], 65832 encodeDefine: { 65833 single: 0, 65834 value: 1, 65835 itemName: 2 65836 } 65837 }); 65838 65839 var list = new List(dimensionsInfo, this); 65840 list.initData(data); 65841 65842 return list; 65843 }, 65844 65845 /** 65846 * The raw data is divided into multiple layers and each layer 65847 * has same name. 65848 * 65849 * @return {Array.<Array.<number>>} 65850 */ 65851 getLayerSeries: function () { 65852 var data = this.getData(); 65853 var lenCount = data.count(); 65854 var indexArr = []; 65855 65856 for (var i = 0; i < lenCount; ++i) { 65857 indexArr[i] = i; 65858 } 65859 // data group by name 65860 var dataByName = nest() 65861 .key(function (index) { 65862 return data.get('name', index); 65863 }) 65864 .entries(indexArr); 65865 65866 var layerSeries = map(dataByName, function (d) { 65867 return { 65868 name: d.key, 65869 indices: d.values 65870 }; 65871 }); 65872 65873 var timeDim = data.mapDimension('single'); 65874 65875 for (var j = 0; j < layerSeries.length; ++j) { 65876 layerSeries[j].indices.sort(comparer); 65877 } 65878 65879 function comparer(index1, index2) { 65880 return data.get(timeDim, index1) - data.get(timeDim, index2); 65881 } 65882 65883 return layerSeries; 65884 }, 65885 65886 /** 65887 * Get data indices for show tooltip content 65888 * 65889 * @param {Array.<string>|string} dim single coordinate dimension 65890 * @param {number} value axis value 65891 * @param {module:echarts/coord/single/SingleAxis} baseAxis single Axis used 65892 * the themeRiver. 65893 * @return {Object} {dataIndices, nestestValue} 65894 */ 65895 getAxisTooltipData: function (dim, value, baseAxis) { 65896 if (!isArray(dim)) { 65897 dim = dim ? [dim] : []; 65898 } 65899 65900 var data = this.getData(); 65901 var layerSeries = this.getLayerSeries(); 65902 var indices = []; 65903 var layerNum = layerSeries.length; 65904 var nestestValue; 65905 65906 for (var i = 0; i < layerNum; ++i) { 65907 var minDist = Number.MAX_VALUE; 65908 var nearestIdx = -1; 65909 var pointNum = layerSeries[i].indices.length; 65910 for (var j = 0; j < pointNum; ++j) { 65911 var theValue = data.get(dim[0], layerSeries[i].indices[j]); 65912 var dist = Math.abs(theValue - value); 65913 if (dist <= minDist) { 65914 nestestValue = theValue; 65915 minDist = dist; 65916 nearestIdx = layerSeries[i].indices[j]; 65917 } 65918 } 65919 indices.push(nearestIdx); 65920 } 65921 65922 return {dataIndices: indices, nestestValue: nestestValue}; 65923 }, 65924 65925 /** 65926 * @override 65927 * @param {number} dataIndex index of data 65928 */ 65929 formatTooltip: function (dataIndex) { 65930 var data = this.getData(); 65931 var htmlName = data.getName(dataIndex); 65932 var htmlValue = data.get(data.mapDimension('value'), dataIndex); 65933 if (isNaN(htmlValue) || htmlValue == null) { 65934 htmlValue = '-'; 65935 } 65936 return encodeHTML(htmlName + ' : ' + htmlValue); 65937 }, 65938 65939 defaultOption: { 65940 zlevel: 0, 65941 z: 2, 65942 65943 coordinateSystem: 'singleAxis', 65944 65945 // gap in axis's orthogonal orientation 65946 boundaryGap: ['10%', '10%'], 65947 65948 // legendHoverLink: true, 65949 65950 singleAxisIndex: 0, 65951 65952 animationEasing: 'linear', 65953 65954 label: { 65955 margin: 4, 65956 show: true, 65957 position: 'left', 65958 color: '#000', 65959 fontSize: 11 65960 }, 65961 65962 emphasis: { 65963 label: { 65964 show: true 65965 } 65966 } 65967 } 65968 });
65969 65970 /* 65971* Licensed to the Apache Software Foundation (ASF) under one 65972* or more contributor license agreements. See the NOTICE file 65973* distributed with this work for additional information 65974* regarding copyright ownership. The ASF licenses this file 65975* to you under the Apache License, Version 2.0 (the 65976* "License"); you may not use this file except in compliance 65977* with the License. You may obtain a copy of the License at 65978* 65979* http://www.apache.org/licenses/LICENSE-2.0 65980* 65981* Unless required by applicable law or agreed to in writing, 65982* software distributed under the License is distributed on an 65983* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 65984* KIND, either express or implied. See the License for the 65985* specific language governing permissions and limitations 65986* under the License. 65987*/ 65988 65989 /** 65990 * @file The file used to draw themeRiver view 65991 * @author Deqing Li([email protected]) 65992 */ 65993 65994 extendChartView({ 65995 65996 type: 'themeRiver', 65997 65998 init: function () { 65999 this._layers = []; 66000 }, 66001 66002 render: function (seriesModel, ecModel, api) { 66003 var data = seriesModel.getData(); 66004 66005 var group = this.group; 66006 66007 var layerSeries = seriesModel.getLayerSeries(); 66008 66009 var layoutInfo = data.getLayout('layoutInfo'); 66010 var rect = layoutInfo.rect; 66011 var boundaryGap = layoutInfo.boundaryGap; 66012 66013 group.attr('position', [0, rect.y + boundaryGap[0]]); 66014 66015 function keyGetter(item) { 66016 return item.name; 66017 } 66018 var dataDiffer = new DataDiffer( 66019 this._layersSeries || [], layerSeries, 66020 keyGetter, keyGetter 66021 ); 66022 66023 var newLayersGroups = {}; 66024 66025 dataDiffer 66026 .add(bind(process, this, 'add')) 66027 .update(bind(process, this, 'update')) 66028 .remove(bind(process, this, 'remove')) 66029 .execute(); 66030 66031 function process(status, idx, oldIdx) { 66032 var oldLayersGroups = this._layers; 66033 if (status === 'remove') { 66034 group.remove(oldLayersGroups[idx]); 66035 return; 66036 } 66037 var points0 = []; 66038 var points1 = []; 66039 var color; 66040 var indices = layerSeries[idx].indices; 66041 for (var j = 0; j < indices.length; j++) { 66042 var layout = data.getItemLayout(indices[j]); 66043 var x = layout.x; 66044 var y0 = layout.y0; 66045 var y = layout.y; 66046 66047 points0.push([x, y0]); 66048 points1.push([x, y0 + y]); 66049 66050 color = data.getItemVisual(indices[j], 'color'); 66051 } 66052 66053 var polygon; 66054 var text; 66055 var textLayout = data.getItemLayout(indices[0]); 66056 var itemModel = data.getItemModel(indices[j - 1]); 66057 var labelModel = itemModel.getModel('label'); 66058 var margin = labelModel.get('margin'); 66059 if (status === 'add') { 66060 var layerGroup = newLayersGroups[idx] = new Group(); 66061 polygon = new Polygon$1({ 66062 shape: { 66063 points: points0, 66064 stackedOnPoints: points1, 66065 smooth: 0.4, 66066 stackedOnSmooth: 0.4, 66067 smoothConstraint: false 66068 }, 66069 z2: 0 66070 }); 66071 text = new Text({ 66072 style: { 66073 x: textLayout.x - margin, 66074 y: textLayout.y0 + textLayout.y / 2 66075 } 66076 }); 66077 layerGroup.add(polygon); 66078 layerGroup.add(text); 66079 group.add(layerGroup); 66080 66081 polygon.setClipPath(createGridClipShape$3(polygon.getBoundingRect
66081(), seriesModel, function () { 66082 polygon.removeClipPath(); 66083 })); 66084 } 66085 else { 66086 var layerGroup = oldLayersGroups[oldIdx]; 66087 polygon = layerGroup.childAt(0); 66088 text = layerGroup.childAt(1); 66089 group.add(layerGroup); 66090 66091 newLayersGroups[idx] = layerGroup; 66092 66093 updateProps(polygon, { 66094 shape: { 66095 points: points0, 66096 stackedOnPoints: points1 66097 } 66098 }, seriesModel); 66099 66100 updateProps(text, { 66101 style: { 66102 x: textLayout.x - margin, 66103 y: textLayout.y0 + textLayout.y / 2 66104 } 66105 }, seriesModel); 66106 } 66107 66108 var hoverItemStyleModel = itemModel.getModel('emphasis.itemStyle'); 66109 var itemStyleModel = itemModel.getModel('itemStyle'); 66110 66111 setTextStyle(text.style, labelModel, { 66112 text: labelModel.get('show') 66113 ? seriesModel.getFormattedLabel(indices[j - 1], 'normal') 66114 || data.getName(indices[j - 1]) 66115 : null, 66116 textVerticalAlign: 'middle' 66117 }); 66118 66119 polygon.setStyle(extend({ 66120 fill: color 66121 }, itemStyleModel.getItemStyle(['color']))); 66122 66123 setHoverStyle(polygon, hoverItemStyleModel.getItemStyle()); 66124 } 66125 66126 this._layersSeries = layerSeries; 66127 this._layers = newLayersGroups; 66128 }, 66129 66130 dispose: function () {} 66131 }); 66132 66133// add animation to the view 66134 function createGridClipShape$3(rect, seriesModel, cb) { 66135 var rectEl = new Rect({ 66136 shape: { 66137 x: rect.x - 10, 66138 y: rect.y - 10, 66139 width: 0, 66140 height: rect.height + 20 66141 } 66142 }); 66143 initProps(rectEl, { 66144 shape: { 66145 width: rect.width + 20, 66146 height: rect.height + 20 66147 } 66148 }, seriesModel, cb); 66149 66150 return rectEl; 66151 } 66152 66153 /* 66154* Licensed to the Apache Software Foundation (ASF) under one 66155* or more contributor license agreements. See the NOTICE file 66156* distributed with this work for additional information 66157* regarding copyright ownership. The ASF licenses this file 66158* to you under the Apache License, Version 2.0 (the 66159* "License"); you may not use this file except in compliance 66160* with the License. You may obtain a copy of the License at 66161* 66162* http://www.apache.org/licenses/LICENSE-2.0 66163* 66164* Unless required by applicable law or agreed to in writing, 66165* software distributed under the License is distributed on an 66166* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 66167* KIND, either express or implied. See the License for the 66168* specific language governing permissions and limitations 66169* under the License. 66170*/ 66171 66172 /** 66173 * @file Using layout algorithm transform the raw data to layout information. 66174 * @author Deqing Li([email protected]) 66175 */ 66176 66177 var themeRiverLayout = function (ecModel, api) { 66178 66179 ecModel.eachSeriesByType('themeRiver', function (seriesModel) { 66180 66181 var data = seriesModel.getData(); 66182 66183 var single = seriesModel.coordinateSystem; 66184 66185 var layoutInfo = {}; 66186 66187 // use the axis boundingRect for view 66188 var rect = single.getRect(); 66189 66190 layoutInfo.rect = rect; 66191 66192 var boundaryGap = seriesModel.get('boundaryGap'); 66193 66194 var axis = single.getAxis(); 66195 66196 layoutInfo.boundaryGap = boundaryGap; 66197 66198 if (axis.orient === 'horizontal') { 66199 boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.height); 66200 boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.height); 66201 var height = rect.height - boundaryGap[0] - boundaryGap[1]; 66202 themeRiverLayout$1(data, seriesModel, height); 66203 } 66204 else { 66205 boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.width); 66206 boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.width); 66207 var width = rect.width - boundaryGap[0] - boundaryGap[1]; 66208 themeRiverLayout$1(data, seriesModel, width); 66209 } 66210 66211 data.setLayout('layoutInfo', layoutInfo); 66212 }); 66213 }; 66214 66215 /** 66216 * The layout information about themeriver 66217 *
66218 * @param {module:echarts/data/List} data data in the series 66219 * @param {module:echarts/model/Series} seriesModel the model object of themeRiver series 66220 * @param {number} height value used to compute every series height 66221 */ 66222 function themeRiverLayout$1(data, seriesModel, height) { 66223 if (!data.count()) { 66224 return; 66225 } 66226 var coordSys = seriesModel.coordinateSystem; 66227 // the data in each layer are organized into a series. 66228 var layerSeries = seriesModel.getLayerSeries(); 66229 66230 // the points in each layer. 66231 var timeDim = data.mapDimension('single'); 66232 var valueDim = data.mapDimension('value'); 66233 var layerPoints = map(layerSeries, function (singleLayer) { 66234 return map(singleLayer.indices, function (idx) { 66235 var pt = coordSys.dataToPoint(data.get(timeDim, idx)); 66236 pt[1] = data.get(valueDim, idx); 66237 return pt; 66238 }); 66239 }); 66240 66241 var base = computeBaseline(layerPoints); 66242 var baseLine = base.y0; 66243 var ky = height / base.max; 66244 66245 // set layout information for each item. 66246 var n = layerSeries.length; 66247 var m = layerSeries[0].indices.length; 66248 var baseY0; 66249 for (var j = 0; j < m; ++j) { 66250 baseY0 = baseLine[j] * ky; 66251 data.setItemLayout(layerSeries[0].indices[j], { 66252 layerIndex: 0, 66253 x: layerPoints[0][j][0], 66254 y0: baseY0, 66255 y: layerPoints[0][j][1] * ky 66256 }); 66257 for (var i = 1; i < n; ++i) { 66258 baseY0 += layerPoints[i - 1][j][1] * ky; 66259 data.setItemLayout(layerSeries[i].indices[j], { 66260 layerIndex: i, 66261 x: layerPoints[i][j][0], 66262 y0: baseY0, 66263 y: layerPoints[i][j][1] * ky 66264 }); 66265 } 66266 } 66267 } 66268 66269 /** 66270 * Compute the baseLine of the rawdata 66271 * Inspired by Lee Byron's paper Stacked Graphs - Geometry & Aesthetics 66272 * 66273 * @param {Array.<Array>} data the points in each layer 66274 * @return {Object} 66275 */ 66276 function computeBaseline(data) { 66277 var layerNum = data.length; 66278 var pointNum = data[0].length; 66279 var sums = []; 66280 var y0 = []; 66281 var max = 0; 66282 var temp; 66283 var base = {}; 66284 66285 for (var i = 0; i < pointNum; ++i) { 66286 for (var j = 0, temp = 0; j < layerNum; ++j) { 66287 temp += data[j][i][1]; 66288 } 66289 if (temp > max) { 66290 max = temp; 66291 } 66292 sums.push(temp); 66293 } 66294 66295 for (var k = 0; k < pointNum; ++k) { 66296 y0[k] = (max - sums[k]) / 2; 66297 } 66298 max = 0; 66299 66300 for (var l = 0; l < pointNum; ++l) { 66301 var sum = sums[l] + y0[l]; 66302 if (sum > max) { 66303 max = sum; 66304 } 66305 } 66306 base.y0 = y0; 66307 base.max = max; 66308 66309 return base; 66310 } 66311 66312 /* 66313* Licensed to the Apache Software Foundation (ASF) under one 66314* or more contributor license agreements. See the NOTICE file 66315* distributed with this work for additional information 66316* regarding copyright ownership. The ASF licenses this file 66317* to you under the Apache License, Version 2.0 (the 66318* "License"); you may not use this file except in compliance 66319* with the License. You may obtain a copy of the License at 66320* 66321* http://www.apache.org/licenses/LICENSE-2.0 66322* 66323* Unless required by applicable law or agreed to in writing, 66324* software distributed under the License is distributed on an 66325* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 66326* KIND, either express or implied. See the License for the 66327* specific language governing permissions and limitations 66328* under the License. 66329*/ 66330 66331 /** 66332 * @file Visual encoding for themeRiver view 66333 * @author Deqing Li([email protected]) 66334 */ 66335 66336 var themeRiverVisual = function (ecModel) { 66337 ecModel.eachSeriesByType('themeRiver', function (seriesModel) { 66338 var data = seriesModel.getData(); 66339 var rawData = seriesModel.getRawData(); 66340 var colorList = seriesModel.get('color'); 66341 var idxMap = createHashMap(); 66342 66343 data.each(function (idx) { 66344 idxMap.set(data.getRawIndex(idx), idx); 66345 }); 66346 66347 rawData.each(function (rawIndex) { 66348 var name = rawData.getName(rawIndex); 66349 var color = colorList[(seriesModel.nameMap.get(name) - 1) % colorList.length]; 66350 66351 rawData.setItemVisual(rawIndex, 'color', color); 66352 66353 var idx = idxMap.get(rawIndex); 66354 66355 if (idx != null) { 66356 data.setItemVisual(idx, 'color', color); 66357 } 66358 }); 66359 }); 66360 }; 66361 66362 /* 66363* Licensed to the Apache Software Foundation (ASF) under one 66364* or more contributor license agreements. See the NOTICE file 66365* distributed with this work for additional information 66366* regarding copyright ownership. The ASF licenses this file 66367* to you under the Apache License, Version 2.0 (the 66368* "License");
66368 you may not use this file except in compliance 66369* with the License. You may obtain a copy of the License at 66370* 66371* http://www.apache.org/licenses/LICENSE-2.0 66372* 66373* Unless required by applicable law or agreed to in writing, 66374* software distributed under the License is distributed on an 66375* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 66376* KIND, either express or implied. See the License for the 66377* specific language governing permissions and limitations 66378* under the License. 66379*/ 66380 66381 registerLayout(themeRiverLayout); 66382 registerVisual(themeRiverVisual); 66383 registerProcessor(dataFilter('themeRiver')); 66384 66385 /* 66386* Licensed to the Apache Software Foundation (ASF) under one 66387* or more contributor license agreements. See the NOTICE file 66388* distributed with this work for additional information 66389* regarding copyright ownership. The ASF licenses this file 66390* to you under the Apache License, Version 2.0 (the 66391* "License"); you may not use this file except in compliance 66392* with the License. You may obtain a copy of the License at 66393* 66394* http://www.apache.org/licenses/LICENSE-2.0 66395* 66396* Unless required by applicable law or agreed to in writing, 66397* software distributed under the License is distributed on an 66398* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 66399* KIND, either express or implied. See the License for the 66400* specific language governing permissions and limitations 66401* under the License. 66402*/ 66403 66404 SeriesModel.extend({ 66405 66406 type: 'series.sunburst', 66407 66408 /** 66409 * @type {module:echarts/data/Tree~Node} 66410 */ 66411 _viewRoot: null, 66412 66413 getInitialData: function (option, ecModel) { 66414 // Create a virtual root. 66415 var root = { name: option.name, children: option.data }; 66416 66417 completeTreeValue$1(root); 66418 66419 var levels = option.levels || []; 66420 66421 // levels = option.levels = setDefault(levels, ecModel); 66422 66423 var treeOption = {}; 66424 66425 treeOption.levels = levels; 66426 66427 // Make sure always a new tree is created when setOption, 66428 // in TreemapView, we check whether oldTree === newTree 66429 // to choose mappings approach among old shapes and new shapes. 66430 return Tree.createTree(root, this, treeOption).data; 66431 }, 66432 66433 optionUpdated: function () { 66434 this.resetViewRoot(); 66435 }, 66436 66437 /* 66438 * @override 66439 */ 66440 getDataParams: function (dataIndex) { 66441 var params = SeriesModel.prototype.getDataParams.apply(this, arguments); 66442 66443 var node = this.getData().tree.getNodeByDataIndex(dataIndex); 66444 params.treePathInfo = wrapTreePathInfo(node, this); 66445 66446 return params; 66447 }, 66448 66449 defaultOption: { 66450 zlevel: 0, 66451 z: 2, 66452 66453 // é»Ë认åâ¦Â¨Ã¥Â±â¬Ã¥Â±â¦Ã¤Â¸Â 66454 center: ['50%', '50%'], 66455 radius: [0, '75%'], 66456 // é»Ë认顺æâ¶éâË 66457 clockwise: true, 66458 startAngle: 90, 66459 // æÅâ¬Ã¥Â°Âè§â度æâ¹ä¸º0 66460 minAngle: 0, 66461 66462 percentPrecision: 2, 66463 66464 // If still show when all data zero. 66465 stillShowZeroSum: true, 66466 66467 // Policy of highlighting pieces when hover on one 66468 // Valid values: 'none' (for not downplay others), 'descendant', 66469 // 'ancestor', 'self' 66470 highlightPolicy: 'descendant', 66471 66472 // 'rootToNode', 'link', or false 66473 nodeClick: 'rootToNode', 66474 66475 renderLabelForZeroData: false, 66476 66477 label: { 66478 // could be: 'radial', 'tangential', or 'none' 66479 rotate: 'radial', 66480 show: true, 66481 opacity: 1, 66482 // 'left' is for inner side of inside, and 'right' is for outter 66483 // side for inside 66484 align: 'center', 66485 position: 'inside', 66486 distance: 5, 66487 silent: true, 66488 emphasis: {} 66489 }, 66490 itemStyle: { 66491 borderWidth: 1, 66492 borderColor: 'white', 66493 opacity: 1, 66494 emphasis: {}, 66495 highlight: { 66496 opacity: 1 66497 }, 66498 downplay: { 66499 opacity: 0.9 66500 } 66501 }, 66502 66503 // Animation type canbe expansion, scale 66504 animationType: 'expansion', 66505 animationDuration: 1000, 66506 animationDurationUpdate: 500, 66507 animationEasing: 'cubicOut', 66508 66509 data: [], 66510 66511 levels: [], 66512 66513 /** 66514 * Sort order. 66515 * 66516 * Valid values: 'desc', 'asc', null, or callback function. 66517 * 'desc' and 'asc' for descend and ascendant order; 66518 * null for not sorting; 66519 * example of callback function: 66520 * function(nodeA, nodeB) { 66521 * return nodeA.getValue() - nodeB.getValue(); 66522 * } 66523 */ 66524 sort: 'desc' 66525 }, 66526 66527 getViewRoot: function () { 66528 return this._viewRoot; 66529 }, 66530 66531 /**
66532 * @param {module:echarts/data/Tree~Node} [viewRoot] 66533 */ 66534 resetViewRoot: function (viewRoot) { 66535 viewRoot 66536 ? (this._viewRoot = viewRoot) 66537 : (viewRoot = this._viewRoot); 66538 66539 var root = this.getRawData().tree.root; 66540 66541 if (!viewRoot 66542 || (viewRoot !== root && !root.contains(viewRoot)) 66543 ) { 66544 this._viewRoot = root; 66545 } 66546 } 66547 }); 66548 66549 66550 66551 /** 66552 * @param {Object} dataNode 66553 */ 66554 function completeTreeValue$1(dataNode) { 66555 // Postorder travel tree. 66556 // If value of none-leaf node is not set, 66557 // calculate it by suming up the value of all children. 66558 var sum = 0; 66559 66560 each$1(dataNode.children, function (child) { 66561 66562 completeTreeValue$1(child); 66563 66564 var childValue = child.value; 66565 isArray(childValue) && (childValue = childValue[0]); 66566 66567 sum += childValue; 66568 }); 66569 66570 var thisValue = dataNode.value; 66571 if (isArray(thisValue)) { 66572 thisValue = thisValue[0]; 66573 } 66574 66575 if (thisValue == null || isNaN(thisValue)) { 66576 thisValue = sum; 66577 } 66578 // Value should not less than 0. 66579 if (thisValue < 0) { 66580 thisValue = 0; 66581 } 66582 66583 isArray(dataNode.value) 66584 ? (dataNode.value[0] = thisValue) 66585 : (dataNode.value = thisValue); 66586 } 66587 66588 /* 66589* Licensed to the Apache Software Foundation (ASF) under one 66590* or more contributor license agreements. See the NOTICE file 66591* distributed with this work for additional information 66592* regarding copyright ownership. The ASF licenses this file 66593* to you under the Apache License, Version 2.0 (the 66594* "License"); you may not use this file except in compliance 66595* with the License. You may obtain a copy of the License at 66596* 66597* http://www.apache.org/licenses/LICENSE-2.0 66598* 66599* Unless required by applicable law or agreed to in writing, 66600* software distributed under the License is distributed on an 66601* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 66602* KIND, either express or implied. See the License for the 66603* specific language governing permissions and limitations 66604* under the License. 66605*/ 66606 66607 var NodeHighlightPolicy = { 66608 NONE: 'none', // not downplay others 66609 DESCENDANT: 'descendant', 66610 ANCESTOR: 'ancestor', 66611 SELF: 'self' 66612 }; 66613 66614 var DEFAULT_SECTOR_Z = 2; 66615 var DEFAULT_TEXT_Z = 4; 66616 66617 /** 66618 * Sunburstce of Sunburst including Sector, Label, LabelLine 66619 * @constructor 66620 * @extends {module:zrender/graphic/Group} 66621 */ 66622 function SunburstPiece(node, seriesModel, ecModel) { 66623 66624 Group.call(this); 66625 66626 var sector = new Sector({ 66627 z2: DEFAULT_SECTOR_Z 66628 }); 66629 sector.seriesIndex = seriesModel.seriesIndex; 66630 66631 var text = new Text({ 66632 z2: DEFAULT_TEXT_Z, 66633 silent: node.getModel('label').get('silent') 66634 }); 66635 this.add(sector); 66636 this.add(text); 66637 66638 this.updateData(true, node, 'normal', seriesModel, ecModel); 66639 66640 // Hover to change label and labelLine 66641 function onEmphasis() { 66642 text.ignore = text.hoverIgnore; 66643 } 66644 function onNormal() { 66645 text.ignore = text.normalIgnore; 66646 } 66647 this.on('emphasis', onEmphasis) 66648 .on('normal', onNormal) 66649 .on('mouseover', onEmphasis) 66650 .on('mouseout', onNormal); 66651 } 66652 66653 var SunburstPieceProto = SunburstPiece.prototype; 66654 66655 SunburstPieceProto.updateData = function ( 66656 firstCreate, 66657 node, 66658 state, 66659 seriesModel, 66660 ecModel 66661 ) { 66662 this.node = node; 66663 node.piece = this; 66664 66665 seriesModel = seriesModel || this._seriesModel; 66666 ecModel = ecModel || this._ecModel; 66667 66668 var sector = this.childAt(0); 66669 sector.dataIndex = node.dataIndex; 66670 66671 var itemModel = node.getModel(); 66672 var layout = node.getLayout(); 66673 if (!layout) { 66674 console.log(node.getLayout()); 66675 } 66676 var sectorShape = extend({}, layout); 66677 sectorShape.label = null; 66678 66679 var visualColor = getNodeColor(node, seriesModel, ecModel); 66680 66681 var normalStyle = itemModel.getModel('itemStyle').getItemStyle(); 66682 var style; 66683 if (state === 'normal') { 66684 style = normalStyle; 66685 } 66686 else { 66687 var stateStyle = itemModel.getModel(state + '.itemStyle') 66688 .getItemStyle(); 66689 style = merge(stateStyle, normalStyle); 66690 } 66691 style = defaults( 66692 { 66693 lineJoin: 'bevel', 66694 fill: style.fill || visualColor 66695 }, 66696 style 66697 ); 66698 66699 if (firstCreate) { 66700 sector.setShape(sectorShape); 66701 sector.shape.r = layout.r0; 66702 updateProps( 66703 sector, 66704 { 66705 shape: { 66706 r: layout.r 66707 } 66708 }, 66709 seriesModel, 66710 node.dataIndex 66711 ); 66712 sector.useStyle(style); 66713 } 66714 else if (typeof style.fill === 'object' && style.fill.type 66715 || typeof sector.style.fill === 'object' && sector.style.fill.type 66716 ) { 66717 // Disable animation for gradient since no interpolation method 66718 // is supported for gradient 66719 updateProps(sector, { 66720 shape: sectorShape 66721 }, seriesModel); 66722 sector.useStyle(style); 66723 } 66724 else { 66725 updateProps(sector, { 66726 shape: sectorShape, 66727 style: style 66728 }, seriesModel); 66729 } 66730 66731 this._updateLabel(seriesModel, visualColor, state); 66732 66733 var cursorStyle = itemModel.getShallow('cursor'); 66734 cursorStyle && sector.attr('cursor', cursorStyle); 66735 66736 if (firstCreate) { 66737 var highlightPolicy = seriesModel.getShallow('highlightPolicy'); 66738 this._initEvents(sector, node, seriesModel, highlightPolicy); 66739 } 66740 66741 this._seriesModel = seriesModel || this._seriesModel; 66742 this._ecModel = ecModel || this._ecModel; 66743 }; 66744
66745 SunburstPieceProto.onEmphasis = function (highlightPolicy) { 66746 var that = this; 66747 this.node.hostTree.root.eachNode(function (n) { 66748 if (n.piece) { 66749 if (that.node === n) { 66750 n.piece.updateData(false, n, 'emphasis'); 66751 } 66752 else if (isNodeHighlighted(n, that.node, highlightPolicy)) { 66753 n.piece.childAt(0).trigger('highlight'); 66754 } 66755 else if (highlightPolicy !== NodeHighlightPolicy.NONE) { 66756 n.piece.childAt(0).trigger('downplay'); 66757 } 66758 } 66759 }); 66760 }; 66761 66762 SunburstPieceProto.onNormal = function () { 66763 this.node.hostTree.root.eachNode(function (n) { 66764 if (n.piece) { 66765 n.piece.updateData(false, n, 'normal'); 66766 } 66767 }); 66768 }; 66769 66770 SunburstPieceProto.onHighlight = function () { 66771 this.updateData(false, this.node, 'highlight'); 66772 }; 66773 66774 SunburstPieceProto.onDownplay = function () { 66775 this.updateData(false, this.node, 'downplay'); 66776 }; 66777 66778 SunburstPieceProto._updateLabel = function (seriesModel, visualColor, state) { 66779 var itemModel = this.node.getModel(); 66780 var normalModel = itemModel.getModel('label'); 66781 var labelModel = state === 'normal' || state === 'emphasis' 66782 ? normalModel 66783 : itemModel.getModel(state + '.label'); 66784 var labelHoverModel = itemModel.getModel('emphasis.label'); 66785 66786 var text = retrieve( 66787 seriesModel.getFormattedLabel( 66788 this.node.dataIndex, 'normal', null, null, 'label' 66789 ), 66790 this.node.name 66791 ); 66792 if (getLabelAttr('show') === false) { 66793 text = ''; 66794 } 66795 66796 var layout = this.node.getLayout(); 66797 var labelMinAngle = labelModel.get('minAngle'); 66798 if (labelMinAngle == null) { 66799 labelMinAngle = normalModel.get('minAngle'); 66800 } 66801 labelMinAngle = labelMinAngle / 180 * Math.PI; 66802 var angle = layout.endAngle - layout.startAngle; 66803 if (labelMinAngle != null && Math.abs(angle) < labelMinAngle) { 66804 // Not displaying text when angle is too small 66805 text = ''; 66806 } 66807 66808 var label = this.childAt(1); 66809 66810 setLabelStyle( 66811 label.style, label.hoverStyle || {}, normalModel, labelHoverModel, 66812 { 66813 defaultText: labelModel.getShallow('show') ? text : null, 66814 autoColor: visualColor, 66815 useInsideStyle: true 66816 } 66817 ); 66818 66819 var midAngle = (layout.startAngle + layout.endAngle) / 2; 66820 var dx = Math.cos(midAngle); 66821 var dy = Math.sin(midAngle); 66822 66823 var r; 66824 var labelPosition = getLabelAttr('position'); 66825 var labelPadding = getLabelAttr('distance') || 0; 66826 var textAlign = getLabelAttr('align'); 66827 if (labelPosition === 'outside') { 66828 r = layout.r + labelPadding; 66829 textAlign = midAngle > Math.PI / 2 ? 'right' : 'left'; 66830 } 66831 else { 66832 if (!textAlign || textAlign === 'center') { 66833 r = (layout.r + layout.r0) / 2; 66834 textAlign = 'center'; 66835 } 66836 else if (textAlign === 'left') { 66837 r = layout.r0 + labelPadding; 66838 if (midAngle > Math.PI / 2) { 66839 textAlign = 'right'; 66840 } 66841 } 66842 else if (textAlign === 'right') { 66843 r = layout.r - labelPadding; 66844 if (midAngle > Math.PI / 2) { 66845 textAlign = 'left'; 66846 } 66847 } 66848 } 66849 66850 label.attr('style', { 66851 text: text, 66852 textAlign: textAlign, 66853 textVerticalAlign: getLabelAttr('verticalAlign') || 'middle', 66854 opacity: getLabelAttr('opacity') 66855 }); 66856 66857 var textX = r * dx + layout.cx; 66858 var textY = r * dy + layout.cy; 66859 label.attr('position', [textX, textY]); 66860 66861 var rotateType = getLabelAttr('rotate'); 66862 var rotate = 0; 66863 if (rotateType === 'radial') { 66864 rotate = -midAngle; 66865 if (rotate < -Math.PI / 2) { 66866 rotate += Math.PI; 66867 } 66868 } 66869 else if (rotateType === 'tangential') { 66870 rotate = Math.PI / 2 - midAngle; 66871 if (rotate > Math.PI / 2) { 66872 rotate -= Math.PI; 66873 } 66874 else if (rotate < -Math.PI / 2) { 66875 rotate += Math.PI; 66876 } 66877 } else if (typeof rotateType === 'number') { 66878 rotate = rotateType * Math.PI / 180; 66879 } 66880 label.attr('rotation', rotate); 66881 66882 function getLabelAttr(name) { 66883 var stateAttr = labelModel.get(name); 66884 if (stateAttr == null) { 66885 return normalModel.get(name); 66886 } 66887 else { 66888 return stateAttr; 66889 } 66890 } 66891 }; 66892 66893 SunburstPieceProto._initEvents = function ( 66894 sector, 66895 node, 66896 seriesModel, 66897 highlightPolicy 66898 ) { 66899 sector.off('mouseover').off('mouseout').off('emphasis').off('normal'); 66900 66901 var that = this;
66902 var onEmphasis = function () { 66903 that.onEmphasis(highlightPolicy); 66904 }; 66905 var onNormal = function () { 66906 that.onNormal(); 66907 }; 66908 var onDownplay = function () { 66909 that.onDownplay(); 66910 }; 66911 var onHighlight = function () { 66912 that.onHighlight(); 66913 }; 66914 66915 if (seriesModel.isAnimationEnabled()) { 66916 sector 66917 .on('mouseover', onEmphasis) 66918 .on('mouseout', onNormal) 66919 .on('emphasis', onEmphasis) 66920 .on('normal', onNormal) 66921 .on('downplay', onDownplay) 66922 .on('highlight', onHighlight); 66923 } 66924 }; 66925 66926 inherits(SunburstPiece, Group); 66927 66928 /** 66929 * Get node color 66930 * 66931 * @param {TreeNode} node the node to get color 66932 * @param {module:echarts/model/Series} seriesModel series 66933 * @param {module:echarts/model/Global} ecModel echarts defaults 66934 */ 66935 function getNodeColor(node, seriesModel, ecModel) { 66936 // Color from visualMap 66937 var visualColor = node.getVisual('color'); 66938 var visualMetaList = node.getVisual('visualMeta'); 66939 if (!visualMetaList || visualMetaList.length === 0) { 66940 // Use first-generation color if has no visualMap 66941 visualColor = null; 66942 } 66943 66944 // Self color or level color 66945 var color = node.getModel('itemStyle').get('color'); 66946 if (color) { 66947 return color; 66948 } 66949 else if (visualColor) { 66950 // Color mapping 66951 return visualColor; 66952 } 66953 else if (node.depth === 0) { 66954 // Virtual root node 66955 return ecModel.option.color[0]; 66956 } 66957 else { 66958 // First-generation color 66959 var length = ecModel.option.color.length; 66960 color = ecModel.option.color[getRootId(node) % length]; 66961 } 66962 return color; 66963 } 66964 66965 /** 66966 * Get index of root in sorted order 66967 * 66968 * @param {TreeNode} node current node 66969 * @return {number} index in root 66970 */ 66971 function getRootId(node) { 66972 var ancestor = node; 66973 while (ancestor.depth > 1) { 66974 ancestor = ancestor.parentNode; 66975 } 66976 66977 var virtualRoot = node.getAncestors()[0]; 66978 return indexOf(virtualRoot.children, ancestor); 66979 } 66980 66981 function isNodeHighlighted(node, activeNode, policy) { 66982 if (policy === NodeHighlightPolicy.NONE) { 66983 return false; 66984 } 66985 else if (policy === NodeHighlightPolicy.SELF) { 66986 return node === activeNode; 66987 } 66988 else if (policy === NodeHighlightPolicy.ANCESTOR) { 66989 return node === activeNode || node.isAncestorOf(activeNode); 66990 } 66991 else { 66992 return node === activeNode || node.isDescendantOf(activeNode); 66993 } 66994 } 66995 66996 /* 66997* Licensed to the Apache Software Foundation (ASF) under one 66998* or more contributor license agreements. See the NOTICE file 66999* distributed with this work for additional information 67000* regarding copyright ownership. The ASF licenses this file 67001* to you under the Apache License, Version 2.0 (the 67002* "License"); you may not use this file except in compliance 67003* with the License. You may obtain a copy of the License at 67004* 67005* http://www.apache.org/licenses/LICENSE-2.0 67006* 67007* Unless required by applicable law or agreed to in writing, 67008* software distributed under the License is distributed on an 67009* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67010* KIND, either express or implied. See the License for the 67011* specific language governing permissions and limitations 67012* under the License. 67013*/ 67014 67015 var ROOT_TO_NODE_ACTION = 'sunburstRootToNode'; 67016 67017 var SunburstView = Chart.extend({ 67018 67019 type: 'sunburst', 67020 67021 init: function () { 67022 }, 67023 67024 render: function (seriesModel, ecModel, api, payload) { 67025 var that = this; 67026 67027 this.seriesModel = seriesModel; 67028 this.api = api; 67029 this.ecModel = ecModel; 67030 67031 var data = seriesModel.getData(); 67032 var virtualRoot = data.tree.root; 67033 67034 var newRoot = seriesModel.getViewRoot(); 67035 67036 var group = this.group; 67037 67038 var renderLabelForZeroData = seriesModel.get('renderLabelForZeroData'); 67039 67040 var newChildren = []; 67041 newRoot.eachNode(function (node) { 67042 newChildren.push(node); 67043 }); 67044 var oldChildren = this._oldChildren || []; 67045 67046 dualTravel(newChildren, oldChildren); 67047 67048 renderRollUp(virtualRoot, newRoot); 67049 67050 if (payload && payload.highlight && payload.highlight.piece) { 67051 var highlightPolicy = seriesModel.getShallow('highlightPolicy');
67052 payload.highlight.piece.onEmphasis(highlightPolicy); 67053 } 67054 else if (payload && payload.unhighlight) { 67055 var piece = this.virtualPiece; 67056 if (!piece && virtualRoot.children.length) { 67057 piece = virtualRoot.children[0].piece; 67058 } 67059 if (piece) { 67060 piece.onNormal(); 67061 } 67062 } 67063 67064 this._initEvents(); 67065 67066 this._oldChildren = newChildren; 67067 67068 function dualTravel(newChildren, oldChildren) { 67069 if (newChildren.length === 0 && oldChildren.length === 0) { 67070 return; 67071 } 67072 67073 new DataDiffer(oldChildren, newChildren, getKey, getKey) 67074 .add(processNode) 67075 .update(processNode) 67076 .remove(curry(processNode, null)) 67077 .execute(); 67078 67079 function getKey(node) { 67080 return node.getId(); 67081 } 67082 67083 function processNode(newId, oldId) { 67084 var newNode = newId == null ? null : newChildren[newId]; 67085 var oldNode = oldId == null ? null : oldChildren[oldId]; 67086 67087 doRenderNode(newNode, oldNode); 67088 } 67089 } 67090 67091 function doRenderNode(newNode, oldNode) { 67092 if (!renderLabelForZeroData && newNode && !newNode.getValue()) { 67093 // Not render data with value 0 67094 newNode = null; 67095 } 67096 67097 if (newNode !== virtualRoot && oldNode !== virtualRoot) { 67098 if (oldNode && oldNode.piece) { 67099 if (newNode) { 67100 // Update 67101 oldNode.piece.updateData( 67102 false, newNode, 'normal', seriesModel, ecModel); 67103 67104 // For tooltip 67105 data.setItemGraphicEl(newNode.dataIndex, oldNode.piece); 67106 } 67107 else { 67108 // Remove 67109 removeNode(oldNode); 67110 } 67111 } 67112 else if (newNode) { 67113 // Add 67114 var piece = new SunburstPiece( 67115 newNode, 67116 seriesModel, 67117 ecModel 67118 ); 67119 group.add(piece); 67120 67121 // For tooltip 67122 data.setItemGraphicEl(newNode.dataIndex, piece); 67123 } 67124 } 67125 } 67126 67127 function removeNode(node) { 67128 if (!node) { 67129 return; 67130 } 67131 67132 if (node.piece) { 67133 group.remove(node.piece); 67134 node.piece = null; 67135 } 67136 } 67137 67138 function renderRollUp(virtualRoot, viewRoot) { 67139 if (viewRoot.depth > 0) { 67140 // Render 67141 if (that.virtualPiece) { 67142 // Update 67143 that.virtualPiece.updateData( 67144 false, virtualRoot, 'normal', seriesModel, ecModel); 67145 } 67146 else { 67147 // Add 67148 that.virtualPiece = new SunburstPiece( 67149 virtualRoot, 67150 seriesModel, 67151 ecModel 67152 ); 67153 group.add(that.virtualPiece); 67154 } 67155 67156 if (viewRoot.piece._onclickEvent) { 67157 viewRoot.piece.off('click', viewRoot.piece._onclickEvent); 67158 } 67159 var event = function (e) { 67160 that._rootToNode(viewRoot.parentNode); 67161 }; 67162 viewRoot.piece._onclickEvent = event; 67163 that.virtualPiece.on('click', event); 67164 } 67165 else if (that.virtualPiece) { 67166 // Remove 67167 group.remove(that.virtualPiece); 67168 that.virtualPiece = null; 67169 } 67170 } 67171 }, 67172 67173 dispose: function () { 67174 }, 67175 67176 /** 67177 * @private 67178 */ 67179 _initEvents: function () { 67180 var that = this; 67181 67182 var event = function (e) { 67183 var targetFound = false; 67184 var viewRoot = that.seriesModel.getViewRoot(); 67185 viewRoot.eachNode(function (node) { 67186 if (!targetFound 67187 && node.piece && node.piece.childAt(0) === e.target 67188 ) { 67189 var nodeClick = node.getModel().get('nodeClick'); 67190 if (nodeClick === 'rootToNode') { 67191 that._rootToNode(node); 67192 } 67193 else if (nodeClick === 'link') {
67194 var itemModel = node.getModel(); 67195 var link = itemModel.get('link'); 67196 if (link) { 67197 var linkTarget = itemModel.get('target', true) 67198 || '_blank'; 67199 window.open(link, linkTarget); 67200 } 67201 } 67202 targetFound = true; 67203 } 67204 }); 67205 }; 67206 67207 if (this.group._onclickEvent) { 67208 this.group.off('click', this.group._onclickEvent); 67209 } 67210 this.group.on('click', event); 67211 this.group._onclickEvent = event; 67212 }, 67213 67214 /** 67215 * @private 67216 */ 67217 _rootToNode: function (node) { 67218 if (node !== this.seriesModel.getViewRoot()) { 67219 this.api.dispatchAction({ 67220 type: ROOT_TO_NODE_ACTION, 67221 from: this.uid, 67222 seriesId: this.seriesModel.id, 67223 targetNode: node 67224 }); 67225 } 67226 }, 67227 67228 /** 67229 * @implement 67230 */ 67231 containPoint: function (point, seriesModel) { 67232 var treeRoot = seriesModel.getData(); 67233 var itemLayout = treeRoot.getItemLayout(0); 67234 if (itemLayout) { 67235 var dx = point[0] - itemLayout.cx; 67236 var dy = point[1] - itemLayout.cy; 67237 var radius = Math.sqrt(dx * dx + dy * dy); 67238 return radius <= itemLayout.r && radius >= itemLayout.r0; 67239 } 67240 } 67241 67242 }); 67243 67244 /* 67245* Licensed to the Apache Software Foundation (ASF) under one 67246* or more contributor license agreements. See the NOTICE file 67247* distributed with this work for additional information 67248* regarding copyright ownership. The ASF licenses this file 67249* to you under the Apache License, Version 2.0 (the 67250* "License"); you may not use this file except in compliance 67251* with the License. You may obtain a copy of the License at 67252* 67253* http://www.apache.org/licenses/LICENSE-2.0 67254* 67255* Unless required by applicable law or agreed to in writing, 67256* software distributed under the License is distributed on an 67257* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67258* KIND, either express or implied. See the License for the 67259* specific language governing permissions and limitations 67260* under the License. 67261*/ 67262 67263 /** 67264 * @file Sunburst action 67265 */ 67266 67267 var ROOT_TO_NODE_ACTION$1 = 'sunburstRootToNode'; 67268 67269 registerAction( 67270 {type: ROOT_TO_NODE_ACTION$1, update: 'updateView'}, 67271 function (payload, ecModel) { 67272 67273 ecModel.eachComponent( 67274 {mainType: 'series', subType: 'sunburst', query: payload}, 67275 handleRootToNode 67276 ); 67277 67278 function handleRootToNode(model, index) { 67279 var targetInfo = retrieveTargetInfo(payload, [ROOT_TO_NODE_ACTION$1], model); 67280 67281 if (targetInfo) { 67282 var originViewRoot = model.getViewRoot(); 67283 if (originViewRoot) { 67284 payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) 67285 ? 'rollUp' : 'drillDown'; 67286 } 67287 model.resetViewRoot(targetInfo.node); 67288 } 67289 } 67290 } 67291 ); 67292 67293 67294 var HIGHLIGHT_ACTION = 'sunburstHighlight'; 67295 67296 registerAction( 67297 {type: HIGHLIGHT_ACTION, update: 'updateView'}, 67298 function (payload, ecModel) { 67299 67300 ecModel.eachComponent( 67301 {mainType: 'series', subType: 'sunburst', query: payload}, 67302 handleHighlight 67303 ); 67304 67305 function handleHighlight(model, index) { 67306 var targetInfo = retrieveTargetInfo(payload, [HIGHLIGHT_ACTION], model); 67307 67308 if (targetInfo) { 67309 payload.highlight = targetInfo.node; 67310 } 67311 } 67312 } 67313 ); 67314 67315 67316 var UNHIGHLIGHT_ACTION = 'sunburstUnhighlight'; 67317 67318 registerAction( 67319 {type: UNHIGHLIGHT_ACTION, update: 'updateView'}, 67320 function (payload, ecModel) { 67321 67322 ecModel.eachComponent(
67323 {mainType: 'series', subType: 'sunburst', query: payload}, 67324 handleUnhighlight 67325 ); 67326 67327 function handleUnhighlight(model, index) { 67328 payload.unhighlight = true; 67329 } 67330 } 67331 ); 67332 67333 /* 67334* Licensed to the Apache Software Foundation (ASF) under one 67335* or more contributor license agreements. See the NOTICE file 67336* distributed with this work for additional information 67337* regarding copyright ownership. The ASF licenses this file 67338* to you under the Apache License, Version 2.0 (the 67339* "License"); you may not use this file except in compliance 67340* with the License. You may obtain a copy of the License at 67341* 67342* http://www.apache.org/licenses/LICENSE-2.0 67343* 67344* Unless required by applicable law or agreed to in writing, 67345* software distributed under the License is distributed on an 67346* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67347* KIND, either express or implied. See the License for the 67348* specific language governing permissions and limitations 67349* under the License. 67350*/ 67351 67352 67353 var RADIAN$1 = Math.PI / 180; 67354 67355 var sunburstLayout = function (seriesType, ecModel, api, payload) { 67356 ecModel.eachSeriesByType(seriesType, function (seriesModel) { 67357 var center = seriesModel.get('center'); 67358 var radius = seriesModel.get('radius'); 67359 67360 if (!isArray(radius)) { 67361 radius = [0, radius]; 67362 } 67363 if (!isArray(center)) { 67364 center = [center, center]; 67365 } 67366 67367 var width = api.getWidth(); 67368 var height = api.getHeight(); 67369 var size = Math.min(width, height); 67370 var cx = parsePercent$1(center[0], width); 67371 var cy = parsePercent$1(center[1], height); 67372 var r0 = parsePercent$1(radius[0], size / 2); 67373 var r = parsePercent$1(radius[1], size / 2); 67374 67375 var startAngle = -seriesModel.get('startAngle') * RADIAN$1; 67376 var minAngle = seriesModel.get('minAngle') * RADIAN$1; 67377 67378 var virtualRoot = seriesModel.getData().tree.root; 67379 var treeRoot = seriesModel.getViewRoot(); 67380 var rootDepth = treeRoot.depth; 67381 67382 var sort = seriesModel.get('sort'); 67383 if (sort != null) { 67384 initChildren$1(treeRoot, sort); 67385 } 67386 67387 var validDataCount = 0; 67388 each$1(treeRoot.children, function (child) { 67389 !isNaN(child.getValue()) && validDataCount++; 67390 }); 67391 67392 var sum = treeRoot.getValue(); 67393 // Sum may be 0 67394 var unitRadian = Math.PI / (sum || validDataCount) * 2; 67395 67396 var renderRollupNode = treeRoot.depth > 0; 67397 var levels = treeRoot.height - (renderRollupNode ? -1 : 1); 67398 var rPerLevel = (r - r0) / (levels || 1); 67399 67400 var clockwise = seriesModel.get('clockwise'); 67401 67402 var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); 67403 67404 // In the case some sector angle is smaller than minAngle 67405 var dir = clockwise ? 1 : -1; 67406 67407 /** 67408 * Render a tree 67409 * @return increased angle 67410 */ 67411 var renderNode = function (node, startAngle) { 67412 if (!node) { 67413 return; 67414 } 67415 67416 var endAngle = startAngle; 67417 67418 // Render self 67419 if (node !== virtualRoot) { 67420 // Tree node is virtual, so it doesn't need to be drawn 67421 var value = node.getValue(); 67422 67423 var angle = (sum === 0 && stillShowZeroSum) 67424 ? unitRadian : (value * unitRadian); 67425 if (angle < minAngle) { 67426 angle = minAngle; 67427 67428 } 67429 else { 67430 67431 } 67432 67433 endAngle = startAngle + dir * angle; 67434 67435 var depth = node.depth - rootDepth 67436 - (renderRollupNode ? -1 : 1); 67437 var rStart = r0 + rPerLevel * depth; 67438 var rEnd = r0 + rPerLevel * (depth + 1); 67439
67440 var itemModel = node.getModel(); 67441 if (itemModel.get('r0') != null) { 67442 rStart = parsePercent$1(itemModel.get('r0'), size / 2); 67443 } 67444 if (itemModel.get('r') != null) { 67445 rEnd = parsePercent$1(itemModel.get('r'), size / 2); 67446 } 67447 67448 node.setLayout({ 67449 angle: angle, 67450 startAngle: startAngle, 67451 endAngle: endAngle, 67452 clockwise: clockwise, 67453 cx: cx, 67454 cy: cy, 67455 r0: rStart, 67456 r: rEnd 67457 }); 67458 } 67459 67460 // Render children 67461 if (node.children && node.children.length) { 67462 // currentAngle = startAngle; 67463 var siblingAngle = 0; 67464 each$1(node.children, function (node) { 67465 siblingAngle += renderNode(node, startAngle + siblingAngle); 67466 }); 67467 } 67468 67469 return endAngle - startAngle; 67470 }; 67471 67472 // Virtual root node for roll up 67473 if (renderRollupNode) { 67474 var rStart = r0; 67475 var rEnd = r0 + rPerLevel; 67476 67477 var angle = Math.PI * 2; 67478 virtualRoot.setLayout({ 67479 angle: angle, 67480 startAngle: startAngle, 67481 endAngle: startAngle + angle, 67482 clockwise: clockwise, 67483 cx: cx, 67484 cy: cy, 67485 r0: rStart, 67486 r: rEnd 67487 }); 67488 } 67489 67490 renderNode(treeRoot, startAngle); 67491 }); 67492 }; 67493 67494 /** 67495 * Init node children by order and update visual 67496 * 67497 * @param {TreeNode} node root node 67498 * @param {boolean} isAsc if is in ascendant order 67499 */ 67500 function initChildren$1(node, isAsc) { 67501 var children = node.children || []; 67502 67503 node.children = sort$2(children, isAsc); 67504 67505 // Init children recursively 67506 if (children.length) { 67507 each$1(node.children, function (child) { 67508 initChildren$1(child, isAsc); 67509 }); 67510 } 67511 } 67512 67513 /** 67514 * Sort children nodes 67515 * 67516 * @param {TreeNode[]} children children of node to be sorted 67517 * @param {string | function | null} sort sort method 67518 * See SunburstSeries.js for details. 67519 */ 67520 function sort$2(children, sortOrder) { 67521 if (typeof sortOrder === 'function') { 67522 return children.sort(sortOrder); 67523 } 67524 else { 67525 var isAsc = sortOrder === 'asc'; 67526 return children.sort(function (a, b) { 67527 var diff = (a.getValue() - b.getValue()) * (isAsc ? 1 : -1); 67528 return diff === 0 67529 ? (a.dataIndex - b.dataIndex) * (isAsc ? -1 : 1) 67530 : diff; 67531 }); 67532 } 67533 } 67534 67535 /* 67536* Licensed to the Apache Software Foundation (ASF) under one 67537* or more contributor license agreements. See the NOTICE file 67538* distributed with this work for additional information 67539* regarding copyright ownership. The ASF licenses this file 67540* to you under the Apache License, Version 2.0 (the 67541* "License"); you may not use this file except in compliance 67542* with the License. You may obtain a copy of the License at 67543* 67544* http://www.apache.org/licenses/LICENSE-2.0 67545* 67546* Unless required by applicable law or agreed to in writing, 67547* software distributed under the License is distributed on an 67548* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67549* KIND, either express or implied. See the License for the 67550* specific language governing permissions and limitations 67551* under the License. 67552*/ 67553 67554 registerVisual(curry(dataColor, 'sunburst')); 67555 registerLayout(curry(sunburstLayout, 'sunburst')); 67556 registerProcessor(curry(dataFilter, 'sunburst')); 67557 67558 /* 67559* Licensed to the Apache Software Foundation (ASF) under one 67560* or more contributor license agreements. See the NOTICE file 67561* distributed with this work for additional information 67562* regarding copyright ownership. The ASF licenses this file 67563* to you under the Apache License, Version 2.0 (the 67564* "License");
67564 you may not use this file except in compliance 67565* with the License. You may obtain a copy of the License at 67566* 67567* http://www.apache.org/licenses/LICENSE-2.0 67568* 67569* Unless required by applicable law or agreed to in writing, 67570* software distributed under the License is distributed on an 67571* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67572* KIND, either express or implied. See the License for the 67573* specific language governing permissions and limitations 67574* under the License. 67575*/ 67576 67577 function dataToCoordSize(dataSize, dataItem) { 67578 // dataItem is necessary in log axis. 67579 dataItem = dataItem || [0, 0]; 67580 return map(['x', 'y'], function (dim, dimIdx) { 67581 var axis = this.getAxis(dim); 67582 var val = dataItem[dimIdx]; 67583 var halfSize = dataSize[dimIdx] / 2; 67584 return axis.type === 'category' 67585 ? axis.getBandWidth() 67586 : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); 67587 }, this); 67588 } 67589 67590 var prepareCartesian2d = function (coordSys) { 67591 var rect = coordSys.grid.getRect(); 67592 return { 67593 coordSys: { 67594 // The name exposed to user is always 'cartesian2d' but not 'grid'. 67595 type: 'cartesian2d', 67596 x: rect.x, 67597 y: rect.y, 67598 width: rect.width, 67599 height: rect.height 67600 }, 67601 api: { 67602 coord: function (data) { 67603 // do not provide "out" param 67604 return coordSys.dataToPoint(data); 67605 }, 67606 size: bind(dataToCoordSize, coordSys) 67607 } 67608 }; 67609 }; 67610 67611 /* 67612* Licensed to the Apache Software Foundation (ASF) under one 67613* or more contributor license agreements. See the NOTICE file 67614* distributed with this work for additional information 67615* regarding copyright ownership. The ASF licenses this file 67616* to you under the Apache License, Version 2.0 (the 67617* "License"); you may not use this file except in compliance 67618* with the License. You may obtain a copy of the License at 67619* 67620* http://www.apache.org/licenses/LICENSE-2.0 67621* 67622* Unless required by applicable law or agreed to in writing, 67623* software distributed under the License is distributed on an 67624* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67625* KIND, either express or implied. See the License for the 67626* specific language governing permissions and limitations 67627* under the License. 67628*/ 67629 67630 function dataToCoordSize$1(dataSize, dataItem) { 67631 dataItem = dataItem || [0, 0]; 67632 return map([0, 1], function (dimIdx) { 67633 var val = dataItem[dimIdx]; 67634 var halfSize = dataSize[dimIdx] / 2; 67635 var p1 = []; 67636 var p2 = []; 67637 p1[dimIdx] = val - halfSize; 67638 p2[dimIdx] = val + halfSize; 67639 p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx]; 67640 return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]); 67641 }, this); 67642 } 67643 67644 var prepareGeo = function (coordSys) { 67645 var rect = coordSys.getBoundingRect(); 67646 return { 67647 coordSys: { 67648 type: 'geo', 67649 x: rect.x, 67650 y: rect.y, 67651 width: rect.width, 67652 height: rect.height 67653 }, 67654 api: { 67655 coord: function (data) { 67656 // do not provide "out" and noRoam param, 67657 // Compatible with this usage: 67658 // echarts.util.map(item.points, api.coord) 67659 return coordSys.dataToPoint(data); 67660 }, 67661 size: bind(dataToCoordSize$1, coordSys) 67662 } 67663 }; 67664 }; 67665 67666 /* 67667* Licensed to the Apache Software Foundation (ASF) under one 67668* or more contributor license agreements. See the NOTICE file 67669* distributed with this work for additional information 67670* regarding copyright ownership. The ASF licenses this file 67671* to you under the Apache License, Version 2.0 (the 67672* "License");
67672 you may not use this file except in compliance 67673* with the License. You may obtain a copy of the License at 67674* 67675* http://www.apache.org/licenses/LICENSE-2.0 67676* 67677* Unless required by applicable law or agreed to in writing, 67678* software distributed under the License is distributed on an 67679* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67680* KIND, either express or implied. See the License for the 67681* specific language governing permissions and limitations 67682* under the License. 67683*/ 67684 67685 function dataToCoordSize$2(dataSize, dataItem) { 67686 // dataItem is necessary in log axis. 67687 var axis = this.getAxis(); 67688 var val = dataItem instanceof Array ? dataItem[0] : dataItem; 67689 var halfSize = (dataSize instanceof Array ? dataSize[0] : dataSize) / 2; 67690 return axis.type === 'category' 67691 ? axis.getBandWidth() 67692 : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); 67693 } 67694 67695 var prepareSingleAxis = function (coordSys) { 67696 var rect = coordSys.getRect(); 67697 67698 return { 67699 coordSys: { 67700 type: 'singleAxis', 67701 x: rect.x, 67702 y: rect.y, 67703 width: rect.width, 67704 height: rect.height 67705 }, 67706 api: { 67707 coord: function (val) { 67708 // do not provide "out" param 67709 return coordSys.dataToPoint(val); 67710 }, 67711 size: bind(dataToCoordSize$2, coordSys) 67712 } 67713 }; 67714 }; 67715 67716 /* 67717* Licensed to the Apache Software Foundation (ASF) under one 67718* or more contributor license agreements. See the NOTICE file 67719* distributed with this work for additional information 67720* regarding copyright ownership. The ASF licenses this file 67721* to you under the Apache License, Version 2.0 (the 67722* "License"); you may not use this file except in compliance 67723* with the License. You may obtain a copy of the License at 67724* 67725* http://www.apache.org/licenses/LICENSE-2.0 67726* 67727* Unless required by applicable law or agreed to in writing, 67728* software distributed under the License is distributed on an 67729* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67730* KIND, either express or implied. See the License for the 67731* specific language governing permissions and limitations 67732* under the License. 67733*/ 67734 67735 function dataToCoordSize$3(dataSize, dataItem) { 67736 // dataItem is necessary in log axis. 67737 return map(['Radius', 'Angle'], function (dim, dimIdx) { 67738 var axis = this['get' + dim + 'Axis'](); 67739 var val = dataItem[dimIdx]; 67740 var halfSize = dataSize[dimIdx] / 2; 67741 var method = 'dataTo' + dim; 67742 67743 var result = axis.type === 'category' 67744 ? axis.getBandWidth() 67745 : Math.abs(axis[method](val - halfSize) - axis[method](val + halfSize)); 67746 67747 if (dim === 'Angle') { 67748 result = result * Math.PI / 180; 67749 } 67750 67751 return result; 67752 67753 }, this); 67754 } 67755 67756 var preparePolar = function (coordSys) { 67757 var radiusAxis = coordSys.getRadiusAxis(); 67758 var angleAxis = coordSys.getAngleAxis(); 67759 var radius = radiusAxis.getExtent(); 67760 radius[0] > radius[1] && radius.reverse(); 67761 67762 return { 67763 coordSys: { 67764 type: 'polar', 67765 cx: coordSys.cx, 67766 cy: coordSys.cy, 67767 r: radius[1], 67768 r0: radius[0] 67769 }, 67770 api: { 67771 coord: bind(function (data) { 67772 var radius = radiusAxis.dataToRadius(data[0]); 67773 var angle = angleAxis.dataToAngle(data[1]); 67774 var coord = coordSys.coordToPoint([radius, angle]); 67775 coord.push(radius, angle * Math.PI / 180); 67776 return coord; 67777 }), 67778 size: bind(dataToCoordSize$3, coordSys) 67779 } 67780 }; 67781 }; 67782 67783 /* 67784* Licensed to the Apache Software Foundation (ASF) under one 67785* or more contributor license agreements. See the NOTICE file 67786* distributed with this work for additional information 67787* regarding copyright ownership. The ASF licenses this file 67788* to you under the Apache License, Version 2.0 (the 67789* "License");
67789 you may not use this file except in compliance 67790* with the License. You may obtain a copy of the License at 67791* 67792* http://www.apache.org/licenses/LICENSE-2.0 67793* 67794* Unless required by applicable law or agreed to in writing, 67795* software distributed under the License is distributed on an 67796* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67797* KIND, either express or implied. See the License for the 67798* specific language governing permissions and limitations 67799* under the License. 67800*/ 67801 67802 var prepareCalendar = function (coordSys) { 67803 var rect = coordSys.getRect(); 67804 var rangeInfo = coordSys.getRangeInfo(); 67805 67806 return { 67807 coordSys: { 67808 type: 'calendar', 67809 x: rect.x, 67810 y: rect.y, 67811 width: rect.width, 67812 height: rect.height, 67813 cellWidth: coordSys.getCellWidth(), 67814 cellHeight: coordSys.getCellHeight(), 67815 rangeInfo: { 67816 start: rangeInfo.start, 67817 end: rangeInfo.end, 67818 weeks: rangeInfo.weeks, 67819 dayCount: rangeInfo.allDay 67820 } 67821 }, 67822 api: { 67823 coord: function (data, clamp) { 67824 return coordSys.dataToPoint(data, clamp); 67825 } 67826 } 67827 }; 67828 }; 67829 67830 /* 67831* Licensed to the Apache Software Foundation (ASF) under one 67832* or more contributor license agreements. See the NOTICE file 67833* distributed with this work for additional information 67834* regarding copyright ownership. The ASF licenses this file 67835* to you under the Apache License, Version 2.0 (the 67836* "License"); you may not use this file except in compliance 67837* with the License. You may obtain a copy of the License at 67838* 67839* http://www.apache.org/licenses/LICENSE-2.0 67840* 67841* Unless required by applicable law or agreed to in writing, 67842* software distributed under the License is distributed on an 67843* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 67844* KIND, either express or implied. See the License for the 67845* specific language governing permissions and limitations 67846* under the License. 67847*/ 67848 67849 var ITEM_STYLE_NORMAL_PATH = ['itemStyle']; 67850 var ITEM_STYLE_EMPHASIS_PATH = ['emphasis', 'itemStyle']; 67851 var LABEL_NORMAL = ['label']; 67852 var LABEL_EMPHASIS = ['emphasis', 'label']; 67853// Use prefix to avoid index to be the same as el.name, 67854// which will cause weird udpate animation. 67855 var GROUP_DIFF_PREFIX = 'e\0\0'; 67856 67857 /** 67858 * To reduce total package size of each coordinate systems, the modules `prepareCustom` 67859 * of each coordinate systems are not required by each coordinate systems directly, but 67860 * required by the module `custom`. 67861 * 67862 * prepareInfoForCustomSeries {Function}: optional 67863 * @return {Object} {coordSys: {...}, api: { 67864 * coord: function (data, clamp) {}, // return point in global. 67865 * size: function (dataSize, dataItem) {} // return size of each axis in coordSys. 67866 * }} 67867 */ 67868 var prepareCustoms = { 67869 cartesian2d: prepareCartesian2d, 67870 geo: prepareGeo, 67871 singleAxis: prepareSingleAxis, 67872 polar: preparePolar, 67873 calendar: prepareCalendar 67874 }; 67875 67876// ------ 67877// Model 67878// ------ 67879 67880 extendSeriesModel({ 67881 67882 type: 'series.custom', 67883 67884 dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'], 67885 67886 defaultOption: { 67887 coordinateSystem: 'cartesian2d', // Can be set as 'none' 67888 zlevel: 0, 67889 z: 2, 67890 legendHoverLink: true 67891 67892 // Cartesian coordinate system 67893 // xAxisIndex: 0, 67894 // yAxisIndex: 0, 67895 67896 // Polar coordinate system 67897 // polarIndex: 0, 67898 67899 // Geo coordinate system 67900 // geoIndex: 0, 67901 67902 // label: {} 67903 // itemStyle: {} 67904 }, 67905 67906 getInitialData: function (option, ecModel) { 67907 return createListFromArray(this.getSource(), this); 67908 } 67909 }); 67910 67911// ----- 67912// View 67913// ----- 67914 67915 extendChartView({ 67916 67917 type: 'custom', 67918 67919 /** 67920 * @private
67921 * @type {module:echarts/data/List} 67922 */ 67923 _data: null, 67924 67925 /** 67926 * @override 67927 */ 67928 render: function (customSeries, ecModel, api) { 67929 var oldData = this._data; 67930 var data = customSeries.getData(); 67931 var group = this.group; 67932 var renderItem = makeRenderItem(customSeries, data, ecModel, api); 67933 67934 this.group.removeAll(); 67935 67936 data.diff(oldData) 67937 .add(function (newIdx) { 67938 createOrUpdate$1( 67939 null, newIdx, renderItem(newIdx), customSeries, group, data 67940 ); 67941 }) 67942 .update(function (newIdx, oldIdx) { 67943 var el = oldData.getItemGraphicEl(oldIdx); 67944 createOrUpdate$1( 67945 el, newIdx, renderItem(newIdx), customSeries, group, data 67946 ); 67947 }) 67948 .remove(function (oldIdx) { 67949 var el = oldData.getItemGraphicEl(oldIdx); 67950 el && group.remove(el); 67951 }) 67952 .execute(); 67953 67954 this._data = data; 67955 }, 67956 67957 incrementalPrepareRender: function (customSeries, ecModel, api) { 67958 this.group.removeAll(); 67959 this._data = null; 67960 }, 67961 67962 incrementalRender: function (params, customSeries, ecModel, api) { 67963 var data = customSeries.getData(); 67964 var renderItem = makeRenderItem(customSeries, data, ecModel, api); 67965 function setIncrementalAndHoverLayer(el) { 67966 if (!el.isGroup) { 67967 el.incremental = true; 67968 el.useHoverLayer = true; 67969 } 67970 } 67971 for (var idx = params.start; idx < params.end; idx++) { 67972 var el = createOrUpdate$1(null, idx, renderItem(idx), customSeries, this.group, data); 67973 el.traverse(setIncrementalAndHoverLayer); 67974 } 67975 }, 67976 67977 /** 67978 * @override 67979 */ 67980 dispose: noop 67981 }); 67982 67983 67984 function createEl(elOption) { 67985 var graphicType = elOption.type; 67986 var el; 67987 67988 if (graphicType === 'path') { 67989 var shape = elOption.shape; 67990 el = makePath( 67991 shape.pathData, 67992 null, 67993 { 67994 x: shape.x || 0, 67995 y: shape.y || 0, 67996 width: shape.width || 0, 67997 height: shape.height || 0 67998 }, 67999 'center' 68000 ); 68001 el.__customPathData = elOption.pathData; 68002 } 68003 else if (graphicType === 'image') { 68004 el = new ZImage({ 68005 }); 68006 el.__customImagePath = elOption.style.image; 68007 } 68008 else if (graphicType === 'text') { 68009 el = new Text({ 68010 }); 68011 el.__customText = elOption.style.text; 68012 } 68013 else { 68014 var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)]; 68015 68016 if (__DEV__) { 68017 assert$1(Clz, 'graphic type "' + graphicType + '" can not be found.'); 68018 } 68019 68020 el = new Clz(); 68021 } 68022 68023 el.__customGraphicType = graphicType; 68024 el.name = elOption.name; 68025 68026 return el; 68027 } 68028 68029 function updateEl(el, dataIndex, elOption, animatableModel, data, isInit) { 68030 var targetProps = {}; 68031 var elOptionStyle = elOption.style || {}; 68032 68033 elOption.shape && (targetProps.shape = clone(elOption.shape)); 68034 elOption.position && (targetProps.position = elOption.position.slice()); 68035 elOption.scale && (targetProps.scale = elOption.scale.slice()); 68036 elOption.origin && (targetProps.origin = elOption.origin.slice()); 68037 elOption.rotation && (targetProps.rotation = elOption.rotation); 68038 68039 if (el.type === 'image' && elOption.style) { 68040 var targetStyle = targetProps.style = {}; 68041 each$1(['x', 'y', 'width', 'height'], function (prop) { 68042 prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit); 68043 }); 68044 } 68045 68046 if (el.type === 'text' && elOption.style) { 68047 var targetStyle = targetProps.style = {}; 68048 each$1(['x', 'y'], function (prop) { 68049 prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit); 68050 }); 68051 // Compatible with previous: both support 68052 // textFill and fill, textStroke and stroke in 'text' element. 68053 !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && ( 68054 elOptionStyle.textFill = elOptionStyle.fill 68055 ); 68056 !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && ( 68057 elOptionStyle.textStroke = elOptionStyle.stroke 68058 ); 68059 } 68060 68061 if (el.type !== 'group') { 68062 el.useStyle(elOptionStyle); 68063 68064 // Init animation. 68065 if (isInit) { 68066 el.style.opacity = 0; 68067 var targetOpacity = elOptionStyle.opacity; 68068 targetOpacity == null && (targetOpacity = 1); 68069 initProps(el, {style: {opacity: targetOpacity}}, animatableModel, dataIndex); 68070 } 68071 } 68072 68073 if (isInit) { 68074 el.attr(targetProps); 68075 } 68076 else { 68077 updateProps(el, targetProps, animatableModel, dataIndex); 68078 } 68079 68080 // z2 must not be null/undefined, otherwise sort error may occur. 68081 el.attr({z2: elOption.z2 || 0, silent: elOption.silent}); 68082 68083 elOption.styleEmphasis !== false && setHoverStyle(el, elOption.styleEmphasis); 68084 } 68085 68086 function prepareStyleTransition(prop, targetStyle, elOptionStyle, oldElStyle, isInit) { 68087 if (elOptionStyle[prop] != null && !isInit) { 68088 targetStyle[prop] = elOptionStyle[prop]; 68089 elOptionStyle[prop] = oldElStyle[prop]; 68090 } 68091 } 68092 68093 function makeRenderItem(customSeries, data, ecModel, api) { 68094 var renderItem = customSeries.get('renderItem'); 68095 var coordSys = customSeries.coordinateSystem; 68096 var prepareResult = {}; 68097 68098 if (coordSys) { 68099 if (__DEV__) { 68100 assert$1(renderItem, 'series.render is required.'); 68101 assert$1( 68102 coordSys.prepareCustoms || prepareCustoms[coordSys.type], 68103 'This coordSys does not support custom series.' 68104 ); 68105 } 68106 68107 prepareResult = coordSys.prepareCustoms 68108 ? coordSys.prepareCustoms() 68109 : prepareCustoms[coordSys.type](coordSys); 68110 } 68111 68112 var userAPI = defaults({ 68113 getWidth: api.getWidth, 68114 getHeight: api.getHeight, 68115 getZr: api.getZr,
68116 getDevicePixelRatio: api.getDevicePixelRatio, 68117 value: value, 68118 style: style, 68119 styleEmphasis: styleEmphasis, 68120 visual: visual, 68121 barLayout: barLayout, 68122 currentSeriesIndices: currentSeriesIndices, 68123 font: font 68124 }, prepareResult.api || {}); 68125 68126 var userParams = { 68127 context: {}, 68128 seriesId: customSeries.id, 68129 seriesName: customSeries.name, 68130 seriesIndex: customSeries.seriesIndex, 68131 coordSys: prepareResult.coordSys, 68132 dataInsideLength: data.count(), 68133 encode: wrapEncodeDef(customSeries.getData()) 68134 }; 68135 68136 // Do not support call `api` asynchronously without dataIndexInside input. 68137 var currDataIndexInside; 68138 var currDirty = true; 68139 var currItemModel; 68140 var currLabelNormalModel; 68141 var currLabelEmphasisModel; 68142 var currVisualColor; 68143 68144 return function (dataIndexInside) { 68145 currDataIndexInside = dataIndexInside; 68146 currDirty = true; 68147 return renderItem && renderItem( 68148 defaults({ 68149 dataIndexInside: dataIndexInside, 68150 dataIndex: data.getRawIndex(dataIndexInside) 68151 }, userParams), 68152 userAPI 68153 ) || {}; 68154 }; 68155 68156 // Do not update cache until api called. 68157 function updateCache(dataIndexInside) { 68158 dataIndexInside == null && (dataIndexInside = currDataIndexInside); 68159 if (currDirty) { 68160 currItemModel = data.getItemModel(dataIndexInside); 68161 currLabelNormalModel = currItemModel.getModel(LABEL_NORMAL); 68162 currLabelEmphasisModel = currItemModel.getModel(LABEL_EMPHASIS); 68163 currVisualColor = data.getItemVisual(dataIndexInside, 'color'); 68164 68165 currDirty = false; 68166 } 68167 } 68168 68169 /** 68170 * @public 68171 * @param {number|string} dim 68172 * @param {number} [dataIndexInside=currDataIndexInside] 68173 * @return {number|string} value 68174 */ 68175 function value(dim, dataIndexInside) { 68176 dataIndexInside == null && (dataIndexInside = currDataIndexInside); 68177 return data.get(data.getDimension(dim || 0), dataIndexInside); 68178 } 68179 68180 /** 68181 * By default, `visual` is applied to style (to support visualMap). 68182 * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`, 68183 * it can be implemented as: 68184 * `api.style({stroke: api.visual('color'), fill: null})`; 68185 * @public 68186 * @param {Object} [extra] 68187 * @param {number} [dataIndexInside=currDataIndexInside] 68188 */ 68189 function style(extra, dataIndexInside) { 68190 dataIndexInside == null && (dataIndexInside = currDataIndexInside); 68191 updateCache(dataIndexInside); 68192 68193 var itemStyle = currItemModel.getModel(ITEM_STYLE_NORMAL_PATH).getItemStyle(); 68194 68195 currVisualColor != null && (itemStyle.fill = currVisualColor); 68196 var opacity = data.getItemVisual(dataIndexInside, 'opacity'); 68197 opacity != null && (itemStyle.opacity = opacity); 68198 68199 setTextStyle(itemStyle, currLabelNormalModel, null, { 68200 autoColor: currVisualColor, 68201 isRectText: true 68202 }); 68203 68204 itemStyle.text = currLabelNormalModel.getShallow('show') 68205 ? retrieve2( 68206 customSeries.getFormattedLabel(dataIndexInside, 'normal'), 68207 getDefaultLabel(data, dataIndexInside) 68208 ) 68209 : null; 68210 68211 extra && extend(itemStyle, extra); 68212 return itemStyle; 68213 } 68214 68215 /** 68216 * @public 68217 * @param {Object} [extra] 68218 * @param {number} [dataIndexInside=currDataIndexInside] 68219 */ 68220 function styleEmphasis(extra, dataIndexInside) { 68221 dataIndexInside == null && (dataIndexInside = currDataIndexInside); 68222 updateCache(dataIndexInside); 68223 68224 var itemStyle = currItemModel.getModel(ITEM_STYLE_EMPHASIS_PATH).getItemStyle(); 68225 68226 setTextStyle(itemStyle, currLabelEmphasisModel, null, { 68227 isRectText: true 68228 }, true); 68229 68230 itemStyle.text = currLabelEmphasisModel.getShallow('show') 68231 ? retrieve3( 68232 customSeries.getFormattedLabel(dataIndexInside, 'emphasis'), 68233 customSeries.getFormattedLabel(dataIndexInside, 'normal'), 68234 getDefaultLabel(data, dataIndexInside) 68235 ) 68236 : null; 68237 68238 extra && extend(itemStyle, extra); 68239 return itemStyle; 68240 } 68241 68242 /** 68243 * @public 68244 * @param {string} visualType 68245 * @param {number} [dataIndexInside=currDataIndexInside] 68246 */ 68247 function visual(visualType, dataIndexInside) { 68248 dataIndexInside == null && (dataIndexInside = currDataIndexInside); 68249 return data.getItemVisual(dataIndexInside, visualType); 68250 } 68251 68252 /** 68253 * @public 68254 * @param {number} opt.count Positive interger. 68255 * @param {number} [opt.barWidth] 68256 * @param {number} [opt.barMaxWidth] 68257 * @param {number} [opt.barGap] 68258 * @param {number} [opt.barCategoryGap] 68259 * @return {Object} {width, offset, offsetCenter} is not support, return undefined. 68260 */ 68261 function barLayout(opt) { 68262 if (coordSys.getBaseAxis) { 68263 var baseAxis = coordSys.getBaseAxis(); 68264 return getLayoutOnAxis(defaults({axis: baseAxis}, opt), api); 68265 } 68266 } 68267 68268 /** 68269 * @public 68270 * @return {Array.<number>} 68271 */ 68272 function currentSeriesIndices() { 68273 return ecModel.getCurrentSeriesIndices(); 68274 } 68275 68276 /** 68277 * @public 68278 * @param {Object} opt 68279 * @param {string} [opt.fontStyle] 68280 * @param {number} [opt.fontWeight] 68281 * @param {number} [opt.fontSize] 68282 * @param {string} [opt.fontFamily] 68283 * @return {string} font string 68284 */ 68285 function font(opt) { 68286 return getFont(opt, ecModel); 68287 } 68288 } 68289 68290 function wrapEncodeDef(data) { 68291 var encodeDef = {}; 68292 each$1(data.dimensions, function (dimName, dataDimIndex) { 68293 var dimInfo = data.getDimensionInfo(dimName); 68294 if (!dimInfo.isExtraCoord) { 68295 var coordDim = dimInfo.coordDim; 68296 var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || []; 68297 dataDims[dimInfo.coordDimIndex] = dataDimIndex; 68298 } 68299 }); 68300 return encodeDef; 68301 } 68302 68303 function createOrUpdate$1(el, dataIndex, elOption, animatableModel, group, data) { 68304 el = doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data); 68305 el && data.setItemGraphicEl(dataIndex, el); 68306 68307 return el; 68308 } 68309 68310 function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data) { 68311 var elOptionType = elOption.type; 68312 if (el 68313 && elOptionType !== el.__customGraphicType 68314 && (elOptionType !== 'path' || elOption.pathData !== el.__customPathData)
68315 && (elOptionType !== 'image' || elOption.style.image !== el.__customImagePath) 68316 && (elOptionType !== 'text' || elOption.style.text !== el.__customText) 68317 ) { 68318 group.remove(el); 68319 el = null; 68320 } 68321 68322 // `elOption.type` is undefined when `renderItem` returns nothing. 68323 if (elOptionType == null) { 68324 return; 68325 } 68326 68327 var isInit = !el; 68328 !el && (el = createEl(elOption)); 68329 updateEl(el, dataIndex, elOption, animatableModel, data, isInit); 68330 68331 if (elOptionType === 'group') { 68332 var oldChildren = el.children() || []; 68333 var newChildren = elOption.children || []; 68334 68335 if (elOption.diffChildrenByName) { 68336 // lower performance. 68337 diffGroupChildren({ 68338 oldChildren: oldChildren, 68339 newChildren: newChildren, 68340 dataIndex: dataIndex, 68341 animatableModel: animatableModel, 68342 group: el, 68343 data: data 68344 }); 68345 } 68346 else { 68347 // better performance. 68348 var index = 0; 68349 for (; index < newChildren.length; index++) { 68350 doCreateOrUpdate( 68351 el.childAt(index), 68352 dataIndex, 68353 newChildren[index], 68354 animatableModel, 68355 el, 68356 data 68357 ); 68358 } 68359 for (; index < oldChildren.length; index++) { 68360 oldChildren[index] && el.remove(oldChildren[index]); 68361 } 68362 } 68363 } 68364 68365 group.add(el); 68366 68367 return el; 68368 } 68369 68370 function diffGroupChildren(context) { 68371 (new DataDiffer( 68372 context.oldChildren, 68373 context.newChildren, 68374 getKey, 68375 getKey, 68376 context 68377 )) 68378 .add(processAddUpdate) 68379 .update(processAddUpdate) 68380 .remove(processRemove) 68381 .execute(); 68382 } 68383 68384 function getKey(item, idx) { 68385 var name = item && item.name; 68386 return name != null ? name : GROUP_DIFF_PREFIX + idx; 68387 } 68388 68389 function processAddUpdate(newIndex, oldIndex) { 68390 var context = this.context; 68391 var childOption = newIndex != null ? context.newChildren[newIndex] : null; 68392 var child = oldIndex != null ? context.oldChildren[oldIndex] : null; 68393 68394 doCreateOrUpdate( 68395 child, 68396 context.dataIndex, 68397 childOption, 68398 context.animatableModel, 68399 context.group, 68400 context.data 68401 ); 68402 } 68403 68404 function processRemove(oldIndex) { 68405 var context = this.context; 68406 var child = context.oldChildren[oldIndex]; 68407 child && context.group.remove(child); 68408 } 68409 68410 /* 68411* Licensed to the Apache Software Foundation (ASF) under one 68412* or more contributor license agreements. See the NOTICE file 68413* distributed with this work for additional information 68414* regarding copyright ownership. The ASF licenses this file 68415* to you under the Apache License, Version 2.0 (the 68416* "License"); you may not use this file except in compliance 68417* with the License. You may obtain a copy of the License at 68418* 68419* http://www.apache.org/licenses/LICENSE-2.0 68420* 68421* Unless required by applicable law or agreed to in writing, 68422* software distributed under the License is distributed on an 68423* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 68424* KIND, either express or implied. See the License for the 68425* specific language governing permissions and limitations 68426* under the License. 68427*/ 68428 68429// ------------- 68430// Preprocessor 68431// ------------- 68432 68433 registerPreprocessor(function (option) { 68434 var graphicOption = option.graphic; 68435 68436 // Convert 68437 // {graphic: [{left: 10, type: 'circle'}, ...]} 68438 // or 68439 // {graphic: {left: 10, type: 'circle'}} 68440 // to 68441 // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]} 68442 if (isArray(graphicOption)) { 68443 if (!graphicOption[0] || !graphicOption[0].elements) { 68444 option.graphic = [{elements: graphicOption}]; 68445 } 68446 else { 68447 // Only one graphic instance can be instantiated. (We dont 68448 // want that too many views are created in echarts._viewMap) 68449 option.graphic = [option.graphic[0]];
68450 } 68451 } 68452 else if (graphicOption && !graphicOption.elements) { 68453 option.graphic = [{elements: [graphicOption]}]; 68454 } 68455 }); 68456 68457// ------ 68458// Model 68459// ------ 68460 68461 var GraphicModel = extendComponentModel({ 68462 68463 type: 'graphic', 68464 68465 defaultOption: { 68466 68467 // Extra properties for each elements: 68468 // 68469 // left/right/top/bottom: (like 12, '22%', 'center', default undefined) 68470 // If left/rigth is set, shape.x/shape.cx/position will not be used. 68471 // If top/bottom is set, shape.y/shape.cy/position will not be used. 68472 // This mechanism is useful when you want to position a group/element 68473 // against the right side or the center of this container. 68474 // 68475 // width/height: (can only be pixel value, default 0) 68476 // Only be used to specify contianer(group) size, if needed. And 68477 // can not be percentage value (like '33%'). See the reason in the 68478 // layout algorithm below. 68479 // 68480 // bounding: (enum: 'all' (default) | 'raw') 68481 // Specify how to calculate boundingRect when locating. 68482 // 'all': Get uioned and transformed boundingRect 68483 // from both itself and its descendants. 68484 // This mode simplies confining a group of elements in the bounding 68485 // of their ancester container (e.g., using 'right: 0'). 68486 // 'raw': Only use the boundingRect of itself and before transformed. 68487 // This mode is similar to css behavior, which is useful when you 68488 // want an element to be able to overflow its container. (Consider 68489 // a rotated circle needs to be located in a corner.) 68490 68491 // Note: elements is always behind its ancestors in this elements array. 68492 elements: [], 68493 parentId: null 68494 }, 68495 68496 /** 68497 * Save el options for the sake of the performance (only update modified graphics). 68498 * The order is the same as those in option. (ancesters ->
68498 descendants) 68499 * 68500 * @private 68501 * @type {Array.<Object>} 68502 */ 68503 _elOptionsToUpdate: null, 68504 68505 /** 68506 * @override 68507 */ 68508 mergeOption: function (option) { 68509 // Prevent default merge to elements 68510 var elements = this.option.elements; 68511 this.option.elements = null; 68512 68513 GraphicModel.superApply(this, 'mergeOption', arguments); 68514 68515 this.option.elements = elements; 68516 }, 68517 68518 /** 68519 * @override 68520 */ 68521 optionUpdated: function (newOption, isInit) { 68522 var thisOption = this.option; 68523 var newList = (isInit ? thisOption : newOption).elements; 68524 var existList = thisOption.elements = isInit ? [] : thisOption.elements; 68525 68526 var flattenedList = []; 68527 this._flatten(newList, flattenedList); 68528 68529 var mappingResult = mappingToExists(existList, flattenedList); 68530 makeIdAndName(mappingResult); 68531 68532 // Clear elOptionsToUpdate 68533 var elOptionsToUpdate = this._elOptionsToUpdate = []; 68534 68535 each$1(mappingResult, function (resultItem, index) { 68536 var newElOption = resultItem.option; 68537 68538 if (__DEV__) { 68539 assert$1( 68540 isObject$1(newElOption) || resultItem.exist, 68541 'Empty graphic option definition' 68542 ); 68543 } 68544 68545 if (!newElOption) { 68546 return; 68547 } 68548 68549 elOptionsToUpdate.push(newElOption); 68550 68551 setKeyInfoToNewElOption(resultItem, newElOption); 68552 68553 mergeNewElOptionToExist(existList, index, newElOption); 68554 68555 setLayoutInfoToExist(existList[index], newElOption); 68556 68557 }, this); 68558 68559 // Clean 68560 for (var i = existList.length - 1; i >= 0; i--) { 68561 if (existList[i] == null) { 68562 existList.splice(i, 1); 68563 } 68564 else { 68565 // $action should be volatile, otherwise option gotten from 68566 // `getOption` will contain unexpected $action. 68567 delete existList[i].$action; 68568 } 68569 } 68570 }, 68571 68572 /** 68573 * Convert 68574 * [{ 68575 * type: 'group', 68576 * id: 'xx', 68577 * children: [{type: 'circle'}, {type: 'polygon'}] 68578 * }] 68579 * to 68580 * [ 68581 * {type: 'group', id: 'xx'}, 68582 * {type: 'circle', parentId: 'xx'}, 68583 * {type: 'polygon', parentId: 'xx'} 68584 * ] 68585 * 68586 * @private 68587 * @param {Array.<Object>} optionList option list 68588 * @param {Array.<Object>} result result of flatten 68589 * @param {Object} parentOption parent option 68590 */ 68591 _flatten: function (optionList, result, parentOption) { 68592 each$1(optionList, function (option) { 68593 if (!option) { 68594 return; 68595 } 68596 68597 if (parentOption) { 68598 option.parentOption = parentOption; 68599 } 68600 68601 result.push(option); 68602 68603 var children = option.children; 68604 if (option.type === 'group' && children) { 68605 this._flatten(children, result, option); 68606 } 68607 // Deleting for JSON output, and for not affecting group creation. 68608 delete option.children; 68609 }, this); 68610 }, 68611 68612 // FIXME 68613 // Pass to view using payload? setOption has a payload? 68614 useElOptionsToUpdate: function () { 68615 var els = this._elOptionsToUpdate; 68616 // Clear to avoid render duplicately when zooming. 68617 this._elOptionsToUpdate = null; 68618 return els; 68619 } 68620 }); 68621 68622// ----- 68623// View 68624// ----- 68625 68626 extendComponentView({ 68627 68628 type: 'graphic', 68629 68630 /** 68631 * @override 68632 */ 68633 init: function (ecModel, api) { 68634 68635 /** 68636 * @private 68637 * @type {module:zrender/core/util.HashMap} 68638 */ 68639 this._elMap = createHashMap(); 68640 68641 /** 68642 * @private 68643 * @type {module:echarts/graphic/GraphicModel} 68644 */ 68645 this._lastGraphicModel; 68646 }, 68647 68648 /** 68649 * @override 68650 */ 68651 render: function (graphicModel, ecModel, api) { 68652 68653 // Having leveraged between use cases and algorithm complexity, a very 68654 // simple layout mechanism is used: 68655 // The size(width/height) can be determined by itself or its parent (not 68656 // implemented yet), but can not by its children. (Top-down travel) 68657 // The location(x/y) can be determined by the bounding rect of itself
68658 // (can including its descendants or not) and the size of its parent. 68659 // (Bottom-up travel) 68660 68661 // When `chart.clear()` or `chart.setOption({...}, true)` with the same id, 68662 // view will be reused. 68663 if (graphicModel !== this._lastGraphicModel) { 68664 this._clear(); 68665 } 68666 this._lastGraphicModel = graphicModel; 68667 68668 this._updateElements(graphicModel, api); 68669 this._relocate(graphicModel, api); 68670 }, 68671 68672 /** 68673 * Update graphic elements. 68674 * 68675 * @private 68676 * @param {Object} graphicModel graphic model 68677 * @param {module:echarts/ExtensionAPI} api extension API 68678 */ 68679 _updateElements: function (graphicModel, api) { 68680 var elOptionsToUpdate = graphicModel.useElOptionsToUpdate(); 68681 68682 if (!elOptionsToUpdate) { 68683 return; 68684 } 68685 68686 var elMap = this._elMap; 68687 var rootGroup = this.group; 68688 68689 // Top-down tranverse to assign graphic settings to each elements. 68690 each$1(elOptionsToUpdate, function (elOption) { 68691 var $action = elOption.$action; 68692 var id = elOption.id; 68693 var existEl = elMap.get(id); 68694 var parentId = elOption.parentId; 68695 var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup; 68696 68697 if (elOption.type === 'text') { 68698 var elOptionStyle = elOption.style; 68699 68700 // In top/bottom mode, textVerticalAlign should not be used, which cause 68701 // inaccurately locating. 68702 if (elOption.hv && elOption.hv[1]) { 68703 elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = null; 68704 } 68705 68706 // Compatible with previous setting: both support fill and textFill, 68707 // stroke and textStroke. 68708 !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && ( 68709 elOptionStyle.textFill = elOptionStyle.fill 68710 ); 68711 !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && ( 68712 elOptionStyle.textStroke = elOptionStyle.stroke 68713 ); 68714 } 68715 68716 // Remove unnecessary props to avoid potential problems. 68717 var elOptionCleaned = getCleanedElOption(elOption); 68718 68719 // For simple, do not support parent change, otherwise reorder is needed. 68720 if (__DEV__) { 68721 existEl && assert$1( 68722 targetElParent === existEl.parent, 68723 'Changing parent is not supported.' 68724 ); 68725 } 68726 68727 if (!$action || $action === 'merge') { 68728 existEl 68729 ? existEl.attr(elOptionCleaned) 68730 : createEl$1(id, targetElParent, elOptionCleaned, elMap); 68731 } 68732 else if ($action === 'replace') { 68733 removeEl(existEl, elMap); 68734 createEl$1(id, targetElParent, elOptionCleaned, elMap); 68735 } 68736 else if ($action === 'remove') { 68737 removeEl(existEl, elMap); 68738 } 68739 68740 var el = elMap.get(id); 68741 if (el) { 68742 el.__ecGraphicWidth = elOption.width; 68743 el.__ecGraphicHeight = elOption.height; 68744 } 68745 }); 68746 }, 68747 68748 /** 68749 * Locate graphic elements. 68750 * 68751 * @private 68752 * @param {Object} graphicModel graphic model 68753 * @param {module:echarts/ExtensionAPI} api extension API 68754 */ 68755 _relocate: function (graphicModel, api) { 68756 var elOptions = graphicModel.option.elements; 68757 var rootGroup = this.group; 68758 var elMap = this._elMap; 68759 68760 // Bottom-up tranvese all elements (consider ec resize) to locate elements. 68761 for (var i = elOptions.length - 1; i >= 0; i--) { 68762 var elOption = elOptions[i]; 68763 var el = elMap.get(elOption.id); 68764 68765 if (!el) { 68766 continue; 68767 } 68768 68769 var parentEl = el.parent; 68770 var containerInfo = parentEl === rootGroup 68771 ? { 68772 width: api.getWidth(), 68773 height: api.getHeight() 68774 } 68775 : { // Like 'position:absolut' in css, default 0. 68776 width: parentEl.__ecGraphicWidth || 0, 68777 height: parentEl.__ecGraphicHeight || 0 68778 }; 68779 68780 positionElement( 68781 el, elOption, containerInfo, null, 68782 {hv: elOption.hv, boundingMode: elOption.bounding} 68783 ); 68784 } 68785 }, 68786 68787 /** 68788 * Clear all elements. 68789 * 68790 * @private 68791 */ 68792 _clear: function () { 68793 var elMap = this._elMap; 68794 elMap.each(function (el) { 68795 removeEl(el, elMap); 68796 }); 68797 this._elMap = createHashMap(); 68798 }, 68799 68800 /** 68801 * @override 68802 */ 68803 dispose: function () { 68804 this._clear(); 68805 } 68806 });
68807 68808 function createEl$1(id, targetElParent, elOption, elMap) { 68809 var graphicType = elOption.type; 68810 68811 if (__DEV__) { 68812 assert$1(graphicType, 'graphic type MUST be set'); 68813 } 68814 68815 var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)]; 68816 68817 if (__DEV__) { 68818 assert$1(Clz, 'graphic type can not be found'); 68819 } 68820 68821 var el = new Clz(elOption); 68822 targetElParent.add(el); 68823 elMap.set(id, el); 68824 el.__ecGraphicId = id; 68825 } 68826 68827 function removeEl(existEl, elMap) { 68828 var existElParent = existEl && existEl.parent; 68829 if (existElParent) { 68830 existEl.type === 'group' && existEl.traverse(function (el) { 68831 removeEl(el, elMap); 68832 }); 68833 elMap.removeKey(existEl.__ecGraphicId); 68834 existElParent.remove(existEl); 68835 } 68836 } 68837 68838// Remove unnecessary props to avoid potential problems. 68839 function getCleanedElOption(elOption) { 68840 elOption = extend({}, elOption); 68841 each$1( 68842 ['id', 'parentId', '$action', 'hv', 'bounding'].concat(LOCATION_PARAMS), 68843 function (name) { 68844 delete elOption[name]; 68845 } 68846 ); 68847 return elOption; 68848 } 68849 68850 function isSetLoc(obj, props) { 68851 var isSet; 68852 each$1(props, function (prop) { 68853 obj[prop] != null && obj[prop] !== 'auto' && (isSet = true); 68854 }); 68855 return isSet; 68856 } 68857 68858 function setKeyInfoToNewElOption(resultItem, newElOption) { 68859 var existElOption = resultItem.exist; 68860 68861 // Set id and type after id assigned. 68862 newElOption.id = resultItem.keyInfo.id; 68863 !newElOption.type && existElOption && (newElOption.type = existElOption.type); 68864 68865 // Set parent id if not specified 68866 if (newElOption.parentId == null) { 68867 var newElParentOption = newElOption.parentOption; 68868 if (newElParentOption) { 68869 newElOption.parentId = newElParentOption.id; 68870 } 68871 else if (existElOption) { 68872 newElOption.parentId = existElOption.parentId; 68873 } 68874 } 68875 68876 // Clear 68877 newElOption.parentOption = null; 68878 } 68879 68880 function mergeNewElOptionToExist(existList, index, newElOption) { 68881 // Update existing options, for `getOption` feature. 68882 var newElOptCopy = extend({}, newElOption); 68883 var existElOption = existList[index]; 68884 68885 var $action = newElOption.$action || 'merge'; 68886 if ($action === 'merge') { 68887 if (existElOption) { 68888 68889 if (__DEV__) { 68890 var newType = newElOption.type; 68891 assert$1( 68892 !newType || existElOption.type === newType, 68893 'Please set $action: "replace" to change `type`' 68894 ); 68895 } 68896 68897 // We can ensure that newElOptCopy and existElOption are not 68898 // the same object, so `merge` will not change newElOptCopy. 68899 merge(existElOption, newElOptCopy, true); 68900 // Rigid body, use ignoreSize. 68901 mergeLayoutParam(existElOption, newElOptCopy, {ignoreSize: true});
68902 // Will be used in render. 68903 copyLayoutParams(newElOption, existElOption); 68904 } 68905 else { 68906 existList[index] = newElOptCopy; 68907 } 68908 } 68909 else if ($action === 'replace') { 68910 existList[index] = newElOptCopy; 68911 } 68912 else if ($action === 'remove') { 68913 // null will be cleaned later. 68914 existElOption && (existList[index] = null); 68915 } 68916 } 68917 68918 function setLayoutInfoToExist(existItem, newElOption) { 68919 if (!existItem) { 68920 return; 68921 } 68922 existItem.hv = newElOption.hv = [ 68923 // Rigid body, dont care `width`. 68924 isSetLoc(newElOption, ['left', 'right']), 68925 // Rigid body, dont care `height`. 68926 isSetLoc(newElOption, ['top', 'bottom']) 68927 ]; 68928 // Give default group size. Otherwise layout error may occur. 68929 if (existItem.type === 'group') { 68930 existItem.width == null && (existItem.width = newElOption.width = 0); 68931 existItem.height == null && (existItem.height = newElOption.height = 0); 68932 } 68933 } 68934 68935 /* 68936* Licensed to the Apache Software Foundation (ASF) under one 68937* or more contributor license agreements. See the NOTICE file 68938* distributed with this work for additional information 68939* regarding copyright ownership. The ASF licenses this file 68940* to you under the Apache License, Version 2.0 (the 68941* "License"); you may not use this file except in compliance 68942* with the License. You may obtain a copy of the License at 68943* 68944* http://www.apache.org/licenses/LICENSE-2.0 68945* 68946* Unless required by applicable law or agreed to in writing, 68947* software distributed under the License is distributed on an 68948* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 68949* KIND, either express or implied. See the License for the 68950* specific language governing permissions and limitations 68951* under the License. 68952*/ 68953 68954 /* 68955* Licensed to the Apache Software Foundation (ASF) under one 68956* or more contributor license agreements. See the NOTICE file 68957* distributed with this work for additional information 68958* regarding copyright ownership. The ASF licenses this file 68959* to you under the Apache License, Version 2.0 (the 68960* "License"); you may not use this file except in compliance 68961* with the License. You may obtain a copy of the License at 68962* 68963* http://www.apache.org/licenses/LICENSE-2.0 68964* 68965* Unless required by applicable law or agreed to in writing, 68966* software distributed under the License is distributed on an 68967* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 68968* KIND, either express or implied. See the License for the 68969* specific language governing permissions and limitations 68970* under the License. 68971*/ 68972 68973 var LegendModel = extendComponentModel({ 68974 68975 type: 'legend.plain', 68976 68977 dependencies: ['series'], 68978 68979 layoutMode: { 68980 type: 'box', 68981 // legend.width/height are maxWidth/maxHeight actually, 68982 // whereas realy width/height is calculated by its content. 68983 // (Setting {left: 10, right: 10} does not make sense). 68984 // So consider the case: 68985 // `setOption({legend: {left: 10});` 68986 // then `setOption({legend: {right: 10});` 68987 // The previous `left` should be cleared by setting `ignoreSize`. 68988 ignoreSize: true 68989 }, 68990 68991 init: function (option, parentModel, ecModel) { 68992 this.mergeDefaultAndTheme(option, ecModel); 68993 68994 option.selected = option.selected || {}; 68995 }, 68996 68997 mergeOption: function (option) { 68998 LegendModel.superCall(this, 'mergeOption', option); 68999 }, 69000 69001 optionUpdated: function () { 69002 this._updateData(this.ecModel); 69003 69004 var legendData = this._data; 69005 69006 // If selectedMode is single, try to select one 69007 if (legendData[0] && this.get('selectedMode') === 'single') { 69008 var hasSelected = false; 69009 // If has any selected in option.selected 69010 for (var i = 0; i < legendData.length; i++) { 69011 var name = legendData[i].get('name'); 69012 if (this.isSelected(name)) { 69013 // Force to unselect others 69014 this.select(name); 69015 hasSelected = true; 69016 break; 69017 } 69018 } 69019 // Try select the first if selectedMode is single 69020 !hasSelected && this.select(legendData[0].get('name')); 69021 } 69022 }, 69023 69024 _updateData: function (ecModel) { 69025 var potentialData = []; 69026 var availableNames = []; 69027 69028 ecModel.eachRawSeries(function (seriesModel) { 69029 var seriesName = seriesModel.name; 69030 availableNames.push(seriesName); 69031 var isPotential; 69032 69033 if (seriesModel.legendDataProvider) { 69034 var data = seriesModel.legendDataProvider(); 69035 var names = data.mapArray(data.getName); 69036
69037 if (!ecModel.isSeriesFiltered(seriesModel)) { 69038 availableNames = availableNames.concat(names); 69039 } 69040 69041 if (names.length) { 69042 potentialData = potentialData.concat(names); 69043 } 69044 else { 69045 isPotential = true; 69046 } 69047 } 69048 else { 69049 isPotential = true; 69050 } 69051 69052 if (isPotential && isNameSpecified(seriesModel)) { 69053 potentialData.push(seriesModel.name); 69054 } 69055 }); 69056 69057 /** 69058 * @type {Array.<string>} 69059 * @private 69060 */ 69061 this._availableNames = availableNames; 69062 69063 // If legend.data not specified in option, use availableNames as data, 69064 // which is convinient for user preparing option. 69065 var rawData = this.get('data') || potentialData; 69066 69067 var legendData = map(rawData, function (dataItem) { 69068 // Can be string or number 69069 if (typeof dataItem === 'string' || typeof dataItem === 'number') { 69070 dataItem = { 69071 name: dataItem 69072 }; 69073 } 69074 return new Model(dataItem, this, this.ecModel); 69075 }, this); 69076 69077 /** 69078 * @type {Array.<module:echarts/model/Model>} 69079 * @private 69080 */ 69081 this._data = legendData; 69082 }, 69083 69084 /** 69085 * @return {Array.<module:echarts/model/Model>} 69086 */ 69087 getData: function () { 69088 return this._data; 69089 }, 69090 69091 /** 69092 * @param {string} name 69093 */ 69094 select: function (name) { 69095 var selected = this.option.selected; 69096 var selectedMode = this.get('selectedMode'); 69097 if (selectedMode === 'single') { 69098 var data = this._data; 69099 each$1(data, function (dataItem) { 69100 selected[dataItem.get('name')] = false; 69101 }); 69102 } 69103 selected[name] = true; 69104 }, 69105 69106 /** 69107 * @param {string} name 69108 */ 69109 unSelect: function (name) { 69110 if (this.get('selectedMode') !== 'single') { 69111 this.option.selected[name] = false; 69112 } 69113 }, 69114 69115 /** 69116 * @param {string} name 69117 */ 69118 toggleSelected: function (name) { 69119 var selected = this.option.selected; 69120 // Default is true 69121 if (!selected.hasOwnProperty(name)) { 69122 selected[name] = true; 69123 } 69124 this[selected[name] ? 'unSelect' : 'select'](name); 69125 }, 69126 69127 /** 69128 * @param {string} name 69129 */ 69130 isSelected: function (name) { 69131 var selected = this.option.selected; 69132 return !(selected.hasOwnProperty(name) && !selected[name]) 69133 && indexOf(this._availableNames, name) >= 0; 69134 }, 69135 69136 defaultOption: { 69137 // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 69138 zlevel: 0, 69139 // äºÅ级å±âå 69140 z: 4, 69141 show: true, 69142 69143 // å¸ÆÃ¥Â±â¬Ã¦â¹å¼Âï¼Åé»Ë认为水平å¸ÆÃ¥Â±â¬Ã¯Â¼Åå¯éâ¬â°Ã¤Â¸ÂºÃ¯Â¼Å¡ 69144 // 'horizontal' | 'vertical' 69145 orient: 'horizontal', 69146 69147 left: 'center', 69148 // right: 'center', 69149 69150 top: 0, 69151 // bottom: null, 69152 69153 // 水平对齠69154 // 'auto' | 'left' | 'right' 69155 // é»Ë认为 'auto', æ ¹æÂ® x çšâä½Âç½®åˤæâÂæË¯å·¦å¯¹é½Âè¿ËæË¯å³对齠69156 align: 'auto', 69157 69158 backgroundColor: 'rgba(0,0,0,0)', 69159 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¨Â¾Â¹Ã¦Â¡â é¢Åèâ°Â² 69160 borderColor: '#ccc', 69161 borderRadius: 0, 69162 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¨Â¾Â¹Ã¦Â¡â 线宽ï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»ËèÂ
69162®Â¤Ã¤Â¸Âº0ï¼Ëæâ è¾¹æ¡â ï¼Ⱐ69163 borderWidth: 0, 69164 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¥â â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认åÂâæâ¹åÂâÃ¥â â¦Ã¨Â¾Â¹Ã¨Â·Â为5*69165 // 接åÂâæâ¢Â°Ã§Â»âÃ¥Ëâ å˫设定上å³ä¸â¹Ã¥Â·Â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥ÂÅcss 69166 padding: 5, 69167 // Ã¥Ââ个itemä¹â¹Ã©â´çšâéâ´éšâï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认为10*69168 // 横åÂâå¸ÆÃ¥Â±â¬Ã¦â¶ä¸ºæ°´å¹³éâ´éšâï¼Å纵åÂâå¸ÆÃ¥Â±â¬Ã¦â¶ä¸ºçºµåÂâéâ´éšâ 69169 itemGap: 10, 69170 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¥âºÂ¾Ã¥Â½Â¢Ã¥Â®Â½Ã¥ÂºÂ¦ 69171 itemWidth: 25, 69172 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¥âºÂ¾Ã¥Â½Â¢Ã©Â«Ë度 69173 itemHeight: 14, 69174 69175 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¥â¦Â³Ã©âÂæâ¶åâ¬â¢Ã§Å¡âé¢Åèâ°Â² 69176 inactiveColor: '#ccc', 69177 69178 textStyle: { 69179 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¦ââ¡Ã¥Ââé¢Åèâ°Â² 69180 color: '#333' 69181 }, 69182 // formatter: '', 69183 // éâ¬â°Ã¦â¹Â©Ã¦Â¨Â¡Ã¥Â¼Âï¼Åé»Ë认å¼â¬Ã¥Â¯åâºÂ¾Ã¤Â¾â¹Ã¥Â¼â¬Ã¥â¦Â³ 69184 selectedMode: true, 69185 // éâ¦Âç½®é»Ë认éâ¬â°Ã¤Â¸Â状æâ¬Âï¼Åå¯éâ¦ÂÃ¥ÂËLEGEND.SELECTEDäºâ¹Ã¤Â»Â¶Ã¥ÂšåЍæâ¬Âæâ¢Â°Ã¦Â®è½½åâ¦Â¥ 69186 // selected: null, 69187 // Ã¥âºÂ¾Ã¤Â¾â¹Ã¥â â¦Ã¥Â®Â¹Ã¯Â¼Ë详è§Âlegend.dataï¼Åæâ¢Â°Ã§Â»âä¸Âæ¯Âä¸â¬Ã©Â¡Â¹Ã¤Â»Â£Ã¨Â¡Â¨Ã¤Â¸â¬Ã¤Â¸Âªitem 69188 // data: [], 69189 69190 // Tooltip çâºÂ¸Ã¥â¦Â³Ã©â¦Âç½® 69191 tooltip: { 69192 show: false 69193 } 69194 } 69195 }); 69196 69197 /* 69198* Licensed to the Apache Software Foundation (ASF) under one 69199* or more contributor license agreements. See the NOTICE file 69200* distributed with this work for additional information 69201* regarding copyright ownership. The ASF licenses this file 69202* to you under the Apache License, Version 2.0 (the 69203* "License"); you may not use this file except in compliance 69204* with the License. You may obtain a copy of the License at 69205* 69206* http://www.apache.org/licenses/LICENSE-2.0 69207* 69208* Unless required by applicable law or agreed to in writing, 69209* software distributed under the License is distributed on an 69210* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69211* KIND, either express or implied. See the License for the 69212* specific language governing permissions and limitations 69213* under the License. 69214*/ 69215 69216 function legendSelectActionHandler(methodName, payload, ecModel) { 69217 var selectedMap = {}; 69218 var isToggleSelect = methodName === 'toggleSelected'; 69219 var isSelected; 69220 // Update all legend components 69221 ecModel.eachComponent('legend', function (legendModel) { 69222 if (isToggleSelect && isSelected != null) { 69223 // Force other legend has same selected status 69224 // Or the first is toggled to true and other are toggled to false 69225 // In the case one legend has some item unSelected in option. And if other legend 69226 // doesn't has the item, they will assume it is selected. 69227 legendModel[isSelected ? 'select' : 'unSelect'](payload.name); 69228 } 69229 else { 69230 legendModel[methodName](payload.name); 69231 isSelected = legendModel.isSelected(payload.name); 69232 } 69233 var legendData = legendModel.getData(); 69234 each$1(legendData, function (model) { 69235 var name = model.get('name'); 69236 // Wrap element 69237 if (name === '\n' || name === '') { 69238 return; 69239 } 69240 var isItemSelected = legendModel.isSelected(name); 69241 if (selectedMap.hasOwnProperty(name)) { 69242 // Unselected if any legend is unselected 69243 selectedMap[name] = selectedMap[name] && isItemSelected; 69244 } 69245 else { 69246 selectedMap[name] = isItemSelected; 69247 } 69248 }); 69249 }); 69250 // Return the event explicitly 69251 return { 69252 name: payload.name, 69253 selected: selectedMap 69254 }; 69255 } 69256 /** 69257 * @event legendToggleSelect 69258 * @type {Object} 69259 * @property {string} type 'legendToggleSelect' 69260 * @property {string} [from] 69261 * @property {string}
69261 name Series name or data item name 69262 */ 69263 registerAction( 69264 'legendToggleSelect', 'legendselectchanged', 69265 curry(legendSelectActionHandler, 'toggleSelected') 69266 ); 69267 69268 /** 69269 * @event legendSelect 69270 * @type {Object} 69271 * @property {string} type 'legendSelect' 69272 * @property {string} name Series name or data item name 69273 */ 69274 registerAction( 69275 'legendSelect', 'legendselected', 69276 curry(legendSelectActionHandler, 'select') 69277 ); 69278 69279 /** 69280 * @event legendUnSelect 69281 * @type {Object} 69282 * @property {string} type 'legendUnSelect' 69283 * @property {string} name Series name or data item name 69284 */ 69285 registerAction( 69286 'legendUnSelect', 'legendunselected', 69287 curry(legendSelectActionHandler, 'unSelect') 69288 ); 69289 69290 /* 69291* Licensed to the Apache Software Foundation (ASF) under one 69292* or more contributor license agreements. See the NOTICE file 69293* distributed with this work for additional information 69294* regarding copyright ownership. The ASF licenses this file 69295* to you under the Apache License, Version 2.0 (the 69296* "License"); you may not use this file except in compliance 69297* with the License. You may obtain a copy of the License at 69298* 69299* http://www.apache.org/licenses/LICENSE-2.0 69300* 69301* Unless required by applicable law or agreed to in writing, 69302* software distributed under the License is distributed on an 69303* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69304* KIND, either express or implied. See the License for the 69305* specific language governing permissions and limitations 69306* under the License. 69307*/ 69308 69309 /** 69310 * Layout list like component. 69311 * It will box layout each items in group of component and then position the whole group in the viewport 69312 * @param {module:zrender/group/Group} group 69313 * @param {module:echarts/model/Component} componentModel 69314 * @param {module:echarts/ExtensionAPI} 69315 */ 69316 function layout$3(group, componentModel, api) { 69317 var boxLayoutParams = componentModel.getBoxLayoutParams(); 69318 var padding = componentModel.get('padding'); 69319 var viewportSize = {width: api.getWidth(), height: api.getHeight()}; 69320 69321 var rect = getLayoutRect( 69322 boxLayoutParams, 69323 viewportSize, 69324 padding 69325 ); 69326 69327 box( 69328 componentModel.get('orient'), 69329 group, 69330 componentModel.get('itemGap'), 69331 rect.width, 69332 rect.height 69333 ); 69334 69335 positionElement( 69336 group, 69337 boxLayoutParams, 69338 viewportSize, 69339 padding 69340 ); 69341 } 69342 69343 function makeBackground(rect, componentModel) { 69344 var padding = normalizeCssArray$1( 69345 componentModel.get('padding') 69346 ); 69347 var style = componentModel.getItemStyle(['color', 'opacity']); 69348 style.fill = componentModel.get('backgroundColor'); 69349 var rect = new Rect({ 69350 shape: { 69351 x: rect.x - padding[3], 69352 y: rect.y - padding[0], 69353 width: rect.width + padding[1] + padding[3], 69354 height: rect.height + padding[0] + padding[2], 69355 r: componentModel.get('borderRadius') 69356 }, 69357 style: style, 69358 silent: true, 69359 z2: -1 69360 }); 69361 // FIXME 69362 // `subPixelOptimizeRect` may bring some gap between edge of viewpart 69363 // and background rect when setting like `left: 0`, `top: 0`. 69364 // graphic.subPixelOptimizeRect(rect); 69365 69366 return rect; 69367 } 69368 69369 /* 69370* Licensed to the Apache Software Foundation (ASF) under one 69371* or more contributor license agreements. See the NOTICE file 69372* distributed with this work for additional information 69373* regarding copyright ownership. The ASF licenses this file 69374* to you under the Apache License, Version 2.0 (the 69375* "License"); you may not use this file except in compliance 69376* with the License. You may obtain a copy of the License at 69377* 69378* http://www.apache.org/licenses/LICENSE-2.0 69379* 69380* Unless required by applicable law or agreed to in writing, 69381* software distributed under the License is distributed on an 69382* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69383* KIND, either express or implied. See the License for the 69384* specific language governing permissions and limitations 69385* under the License. 69386*/ 69387 69388 var curry$4 = curry; 69389 var each$16 = each$1; 69390 var Group$3 = Group; 69391 69392 var LegendView = extendComponentView({
69393 69394 type: 'legend.plain', 69395 69396 newlineDisabled: false, 69397 69398 /** 69399 * @override 69400 */ 69401 init: function () { 69402 69403 /** 69404 * @private 69405 * @type {module:zrender/container/Group} 69406 */ 69407 this.group.add(this._contentGroup = new Group$3()); 69408 69409 /** 69410 * @private 69411 * @type {module:zrender/Element} 69412 */ 69413 this._backgroundEl; 69414 }, 69415 69416 /** 69417 * @protected 69418 */ 69419 getContentGroup: function () { 69420 return this._contentGroup; 69421 }, 69422 69423 /** 69424 * @override 69425 */ 69426 render: function (legendModel, ecModel, api) { 69427 69428 this.resetInner(); 69429 69430 if (!legendModel.get('show', true)) { 69431 return; 69432 } 69433 69434 var itemAlign = legendModel.get('align'); 69435 if (!itemAlign || itemAlign === 'auto') { 69436 itemAlign = ( 69437 legendModel.get('left') === 'right' 69438 && legendModel.get('orient') === 'vertical' 69439 ) ? 'right' : 'left'; 69440 } 69441 69442 this.renderInner(itemAlign, legendModel, ecModel, api); 69443 69444 // Perform layout. 69445 var positionInfo = legendModel.getBoxLayoutParams(); 69446 var viewportSize = {width: api.getWidth(), height: api.getHeight()}; 69447 var padding = legendModel.get('padding'); 69448 69449 var maxSize = getLayoutRect(positionInfo, viewportSize, padding); 69450 var mainRect = this.layoutInner(legendModel, itemAlign, maxSize); 69451 69452 // Place mainGroup, based on the calculated `mainRect`. 69453 var layoutRect = getLayoutRect( 69454 defaults({width: mainRect.width, height: mainRect.height}, positionInfo), 69455 viewportSize, 69456 padding 69457 ); 69458 this.group.attr('position', [layoutRect.x - mainRect.x, layoutRect.y - mainRect.y]); 69459 69460 // Render background after group is layout. 69461 this.group.add( 69462 this._backgroundEl = makeBackground(mainRect, legendModel) 69463 ); 69464 }, 69465 69466 /** 69467 * @protected 69468 */ 69469 resetInner: function () { 69470 this.getContentGroup().removeAll(); 69471 this._backgroundEl && this.group.remove(this._backgroundEl); 69472 }, 69473 69474 /** 69475 * @protected 69476 */ 69477 renderInner: function (itemAlign, legendModel, ecModel, api) { 69478 var contentGroup = this.getContentGroup(); 69479 var legendDrawnMap = createHashMap(); 69480 var selectMode = legendModel.get('selectedMode'); 69481 69482 var excludeSeriesId = []; 69483 ecModel.eachRawSeries(function (seriesModel) { 69484 !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesM
69484odel.id); 69485 }); 69486 69487 each$16(legendModel.getData(), function (itemModel, dataIndex) { 69488 var name = itemModel.get('name'); 69489 69490 // Use empty string or \n as a newline string 69491 if (!this.newlineDisabled && (name === '' || name === '\n')) { 69492 contentGroup.add(new Group$3({ 69493 newline: true 69494 })); 69495 return; 69496 } 69497 69498 // Representitive series. 69499 var seriesModel = ecModel.getSeriesByName(name)[0]; 69500 69501 if (legendDrawnMap.get(name)) { 69502 // Have been drawed 69503 return; 69504 } 69505 69506 // Series legend 69507 if (seriesModel) { 69508 var data = seriesModel.getData(); 69509 var color = data.getVisual('color'); 69510 69511 // If color is a callback function 69512 if (typeof color === 'function') { 69513 // Use the first data 69514 color = color(seriesModel.getDataParams(0)); 69515 } 69516 69517 // Using rect symbol defaultly 69518 var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect'; 69519 var symbolType = data.getVisual('symbol'); 69520 69521 var itemGroup = this._createItem( 69522 name, dataIndex, itemModel, legendModel, 69523 legendSymbolType, symbolType, 69524 itemAlign, color, 69525 selectMode 69526 ); 69527 69528 itemGroup.on('click', curry$4(dispatchSelectAction, name, api)) 69529 .on('mouseover', curry$4(dispatchHighlightAction, seriesModel, null, api, excludeSeriesId)) 69530 .on('mouseout', curry$4(dispatchDownplayAction, seriesModel, null, api, excludeSeriesId)); 69531 69532 legendDrawnMap.set(name, true); 69533 } 69534 else { 69535 // Data legend of pie, funnel 69536 ecModel.eachRawSeries(function (seriesModel) { 69537 // In case multiple series has same data name 69538 if (legendDrawnMap.get(name)) { 69539 return; 69540 } 69541 69542 if (seriesModel.legendDataProvider) { 69543 var data = seriesModel.legendDataProvider(); 69544 var idx = data.indexOfName(name); 69545 if (idx < 0) { 69546 return; 69547 } 69548 69549 var color = data.getItemVisual(idx, 'color'); 69550 69551 var legendSymbolType = 'roundRect'; 69552 69553 var itemGroup = this._createItem( 69554 name, dataIndex, itemModel, legendModel, 69555 legendSymbolType, null, 69556 itemAlign, color, 69557 selectMode 69558 ); 69559 69560 // FIXME: consider different series has items with the same name. 69561 itemGroup.on('click', curry$4(dispatchSelectAction, name, api)) 69562 // FIXME Should not specify the series name 69563 .on('mouseover', curry$4(dispatchHighlightAction, seriesModel, name, api, excludeSeriesId)) 69564 .on('mouseout', curry$4(dispatchDownplayAction, seriesModel, name, api, excludeSeriesId)); 69565 69566 legendDrawnMap.set(name, true); 69567 } 69568 69569 }, this); 69570 } 69571 69572 if (__DEV__) { 69573 if (!legendDrawnMap.get(name)) { 69574 console.warn(name + ' series not exists. Legend data should be same with series name or data name.'); 69575 } 69576 } 69577 }, this); 69578 }, 69579 69580 _createItem: function ( 69581 name, dataIndex, itemModel, legendModel, 69582 legendSymbolType, symbolType, 69583 itemAlign, color, selectMode 69584 ) { 69585 var itemWidth = legendModel.get('itemWidth'); 69586 var itemHeight = legendModel.get('itemHeight'); 69587 var inactiveColor = legendModel.get('inactiveColor'); 69588 var symbolKeepAspect = legendModel.get('symbolKeepAspect'); 69589 69590 var isSelected = legendModel.isSelected(name); 69591 var itemGroup = new Group$3(); 69592 69593 var textStyleModel = itemModel.getModel('textStyle'); 69594 69595 var itemIcon = itemModel.get('icon'); 69596 69597 var tooltipModel = itemModel.getModel('tooltip'); 69598 var legendGlobalTooltipModel = tooltipModel.parentModel; 69599 69600 // Use user given icon first 69601 legendSymbolType = itemIcon || legendSymbolType; 69602 itemGroup.add(createSymbol( 69603 legendSymbolType, 69604 0, 69605 0, 69606 itemWidth, 69607 itemHeight, 69608 isSelected ? color : inactiveColor, 69609 // symbolKeepAspect default true for legend 69610 symbolKeepAspect == null ? true : symbolKeepAspect 69611 )); 69612 69613 // Compose symbols 69614 // PENDING 69615 if (!itemIcon && symbolType 69616 // At least show one symbol, can't be all none 69617 && ((symbolType !== legendSymbolType) || symbolType == 'none') 69618 ) { 69619 var size = itemHeight * 0.8; 69620 if (symbolType === 'none') { 69621 symbolType = 'circle'; 69622 } 69623 // Put symbol in the center 69624 itemGroup.add(createSymbol( 69625 symbolType, 69626 (itemWidth - size) / 2, 69627 (itemHeight - size) / 2, 69628 size, 69629 size, 69630 isSelected ? color : inactiveColor, 69631 // symbolKeepAspect default true for legend 69632 symbolKeepAspect == null ? true : symbolKeepAspect 69633 )); 69634 } 69635 69636 var textX = itemAlign === 'left' ? itemWidth + 5 : -5; 69637 var textAlign = itemAlign; 69638 69639 var formatter = legendModel.get('formatter'); 69640 var content = name; 69641 if (typeof formatter === 'string' && formatter) { 69642 content = formatter.replace('{name}', name != null ? name : ''); 69643 } 69644 else if (typeof formatter === 'function') { 69645 content = formatter(name); 69646 } 69647 69648 itemGroup.add(new Text({ 69649 style: setTextStyle({}, textStyleModel, { 69650 text: content, 69651 x: textX, 69652 y: itemHeight / 2, 69653 textFill: isSelected ? textStyleModel.getTextColor() : inactiveColor, 69654 textAlign: textAlign, 69655 textVerticalAlign: 'middle' 69656 }) 69657 })); 69658 69659 // Add a invisible rect to increase the area of mouse hover 69660 var hitRect = new Rect({
69661 shape: itemGroup.getBoundingRect(), 69662 invisible: true, 69663 tooltip: tooltipModel.get('show') ? extend({ 69664 content: name, 69665 // Defaul formatter 69666 formatter: legendGlobalTooltipModel.get('formatter', true) || function () { 69667 return name; 69668 }, 69669 formatterParams: { 69670 componentType: 'legend', 69671 legendIndex: legendModel.componentIndex, 69672 name: name, 69673 $vars: ['name'] 69674 } 69675 }, tooltipModel.option) : null 69676 }); 69677 itemGroup.add(hitRect); 69678 69679 itemGroup.eachChild(function (child) { 69680 child.silent = true; 69681 }); 69682 69683 hitRect.silent = !selectMode; 69684 69685 this.getContentGroup().add(itemGroup); 69686 69687 setHoverStyle(itemGroup); 69688 69689 itemGroup.__legendDataIndex = dataIndex; 69690 69691 return itemGroup; 69692 }, 69693 69694 /** 69695 * @protected 69696 */ 69697 layoutInner: function (legendModel, itemAlign, maxSize) { 69698 var contentGroup = this.getContentGroup(); 69699 69700 // Place items in contentGroup. 69701 box( 69702 legendModel.get('orient'), 69703 contentGroup, 69704 legendModel.get('itemGap'), 69705 maxSize.width, 69706 maxSize.height 69707 ); 69708 69709 var contentRect = contentGroup.getBoundingRect(); 69710 contentGroup.attr('position', [-contentRect.x, -contentRect.y]); 69711 69712 return this.group.getBoundingRect(); 69713 } 69714 69715 }); 69716 69717 function dispatchSelectAction(name, api) { 69718 api.dispatchAction({ 69719 type: 'legendToggleSelect', 69720 name: name 69721 }); 69722 } 69723 69724 function dispatchHighlightAction(seriesModel, dataName, api, excludeSeriesId) { 69725 // If element hover will move to a hoverLayer. 69726 var el = api.getZr().storage.getDisplayList()[0]; 69727 if (!(el && el.useHoverLayer)) { 69728 api.dispatchAction({ 69729 type: 'highlight', 69730 seriesName: seriesModel.name, 69731 name: dataName, 69732 excludeSeriesId: excludeSeriesId 69733 }); 69734 } 69735 } 69736 69737 function dispatchDownplayAction(seriesModel, dataName, api, excludeSeriesId) { 69738 // If element hover will move to a hoverLayer. 69739 var el = api.getZr().storage.getDisplayList()[0]; 69740 if (!(el && el.useHoverLayer)) { 69741 api.dispatchAction({ 69742 type: 'downplay', 69743 seriesName: seriesModel.name, 69744 name: dataName, 69745 excludeSeriesId: excludeSeriesId 69746 }); 69747 } 69748 } 69749 69750 /* 69751* Licensed to the Apache Software Foundation (ASF) under one 69752* or more contributor license agreements. See the NOTICE file 69753* distributed with this work for additional information 69754* regarding copyright ownership. The ASF licenses this file 69755* to you under the Apache License, Version 2.0 (the 69756* "License"); you may not use this file except in compliance 69757* with the License. You may obtain a copy of the License at 69758* 69759* http://www.apache.org/licenses/LICENSE-2.0 69760* 69761* Unless required by applicable law or agreed to in writing, 69762* software distributed under the License is distributed on an 69763* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69764* KIND, either express or implied. See the License for the 69765* specific language governing permissions and limitations 69766* under the License. 69767*/ 69768 69769 var legendFilter = function (ecModel) { 69770 69771 var legendModels = ecModel.findComponents({ 69772 mainType: 'legend' 69773 }); 69774 if (legendModels && legendModels.length) { 69775 ecModel.filterSeries(function (series) { 69776 // If in any legend component the status is not selected. 69777 // Because in legend series is assumed selected when it is not in the legend data. 69778 for (var i = 0; i < legendModels.length; i++) { 69779 if (!legendModels[i].isSelected(series.name)) { 69780 return false; 69781 } 69782 } 69783 return true; 69784 }); 69785 } 69786 69787 }; 69788 69789 /* 69790* Licensed to the Apache Software Foundation (ASF) under one 69791* or more contributor license agreements. See the NOTICE file 69792* distributed with this work for additional information 69793* regarding copyright ownership. The ASF licenses this file 69794* to you under the Apache License, Version 2.0 (the 69795* "License");
69795 you may not use this file except in compliance 69796* with the License. You may obtain a copy of the License at 69797* 69798* http://www.apache.org/licenses/LICENSE-2.0 69799* 69800* Unless required by applicable law or agreed to in writing, 69801* software distributed under the License is distributed on an 69802* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69803* KIND, either express or implied. See the License for the 69804* specific language governing permissions and limitations 69805* under the License. 69806*/ 69807 69808 69809// Do not contain scrollable legend, for sake of file size. 69810 69811// Series Filter 69812 registerProcessor(legendFilter); 69813 69814 ComponentModel.registerSubTypeDefaulter('legend', function () { 69815 // Default 'plain' when no type specified. 69816 return 'plain'; 69817 }); 69818 69819 /* 69820* Licensed to the Apache Software Foundation (ASF) under one 69821* or more contributor license agreements. See the NOTICE file 69822* distributed with this work for additional information 69823* regarding copyright ownership. The ASF licenses this file 69824* to you under the Apache License, Version 2.0 (the 69825* "License"); you may not use this file except in compliance 69826* with the License. You may obtain a copy of the License at 69827* 69828* http://www.apache.org/licenses/LICENSE-2.0 69829* 69830* Unless required by applicable law or agreed to in writing, 69831* software distributed under the License is distributed on an 69832* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69833* KIND, either express or implied. See the License for the 69834* specific language governing permissions and limitations 69835* under the License. 69836*/ 69837 69838 var ScrollableLegendModel = LegendModel.extend({ 69839 69840 type: 'legend.scroll', 69841 69842 /** 69843 * @param {number} scrollDataIndex 69844 */ 69845 setScrollDataIndex: function (scrollDataIndex) { 69846 this.option.scrollDataIndex = scrollDataIndex; 69847 }, 69848 69849 defaultOption: { 69850 scrollDataIndex: 0, 69851 pageButtonItemGap: 5, 69852 pageButtonGap: null, 69853 pageButtonPosition: 'end', // 'start' or 'end' 69854 pageFormatter: '{current}/{total}', // If null/undefined, do not show page. 69855 pageIcons: { 69856 horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'], 69857 vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z'] 69858 }, 69859 pageIconColor: '#2f4554', 69860 pageIconInactiveColor: '#aaa', 69861 pageIconSize: 15, // Can be [10, 3], which represents [width, height] 69862 pageTextStyle: { 69863 color: '#333' 69864 }, 69865 69866 animationDurationUpdate: 800 69867 }, 69868 69869 /** 69870 * @override 69871 */ 69872 init: function (option, parentModel, ecModel, extraOpt) { 69873 var inputPositionParams = getLayoutParams(option); 69874 69875 ScrollableLegendModel.superCall(this, 'init', option, parentModel, ecModel, extraOpt); 69876 69877 mergeAndNormalizeLayoutParams(this, option, inputPositionParams); 69878 }, 69879 69880 /** 69881 * @override 69882 */ 69883 mergeOption: function (option, extraOpt) { 69884 ScrollableLegendModel.superCall(this, 'mergeOption', option, extraOpt); 69885 69886 mergeAndNormalizeLayoutParams(this, this.option, option); 69887 }, 69888 69889 getOrient: function () { 69890 return this.get('orient') === 'vertical' 69891 ? {index: 1, name: 'vertical'} 69892 : {index: 0, name: 'horizontal'}; 69893 } 69894 69895 }); 69896 69897// Do not `ignoreSize` to enable setting {left: 10, right: 10}. 69898 function mergeAndNormalizeLayoutParams(legendModel, target, raw) { 69899 var orient = legendModel.getOrient(); 69900 var ignoreSize = [1, 1]; 69901 ignoreSize[orient.index] = 0; 69902 mergeLayoutParam(target, raw, { 69903 type: 'box', ignoreSize: ignoreSize 69904 }); 69905 } 69906 69907 /* 69908* Licensed to the Apache Software Foundation (ASF) under one 69909* or more contributor license agreements. See the NOTICE file 69910* distributed with this work for additional information 69911* regarding copyright ownership. The ASF licenses this file 69912* to you under the Apache License, Version 2.0 (the 69913* "License");
69913 you may not use this file except in compliance 69914* with the License. You may obtain a copy of the License at 69915* 69916* http://www.apache.org/licenses/LICENSE-2.0 69917* 69918* Unless required by applicable law or agreed to in writing, 69919* software distributed under the License is distributed on an 69920* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 69921* KIND, either express or implied. See the License for the 69922* specific language governing permissions and limitations 69923* under the License. 69924*/ 69925 69926 /** 69927 * Separate legend and scrollable legend to reduce package size. 69928 */ 69929 69930 var Group$4 = Group; 69931 69932 var WH$1 = ['width', 'height']; 69933 var XY$1 = ['x', 'y']; 69934 69935 var ScrollableLegendView = LegendView.extend({ 69936 69937 type: 'legend.scroll', 69938 69939 newlineDisabled: true, 69940 69941 init: function () { 69942 69943 ScrollableLegendView.superCall(this, 'init'); 69944 69945 /** 69946 * @private 69947 * @type {number} For `scroll`. 69948 */ 69949 this._currentIndex = 0; 69950 69951 /** 69952 * @private 69953 * @type {module:zrender/container/Group} 69954 */ 69955 this.group.add(this._containerGroup = new Group$4()); 69956 this._containerGroup.add(this.getContentGroup()); 69957 69958 /** 69959 * @private 69960 * @type {module:zrender/container/Group} 69961 */ 69962 this.group.add(this._controllerGroup = new Group$4()); 69963 69964 /** 69965 * 69966 * @private 69967 */ 69968 this._showController; 69969 }, 69970 69971 /** 69972 * @override 69973 */ 69974 resetInner: function () { 69975 ScrollableLegendView.superCall(this, 'resetInner'); 69976 69977 this._controllerGroup.removeAll(); 69978 this._containerGroup.removeClipPath(); 69979 this._containerGroup.__rectSize = null; 69980 }, 69981 69982 /** 69983 * @override 69984 */ 69985 renderInner: function (itemAlign, legendModel, ecModel, api) { 69986 var me = this; 69987 69988 // Render content items. 69989 ScrollableLegendView.superCall(this, 'renderInner', itemAlign, legendModel, ecModel, api); 69990 69991 var controllerGroup = this._controllerGroup; 69992 69993 var pageIconSize = legendModel.get('pageIconSize', true); 69994 if (!isArray(pageIconSize)) { 69995 pageIconSize = [pageIconSize, pageIconSize]; 69996 } 69997 69998 createPageButton('pagePrev', 0); 69999 70000 var pageTextStyleModel = legendModel.getModel('pageTextStyle'); 70001 controllerGroup.add(new Text({ 70002 name: 'pageText', 70003 style: { 70004 textFill: pageTextStyleModel.getTextColor(), 70005 font: pageTextStyleModel.getFont(), 70006 textVerticalAlign: 'middle', 70007 textAlign: 'center' 70008 }, 70009 silent: true 70010 })); 70011 70012 createPageButton('pageNext', 1); 70013 70014 function createPageButton(name, iconIdx) { 70015 var pageDataIndexName = name + 'DataIndex'; 70016 var icon = createIcon( 70017 legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], 70018 { 70019 // Buttons will be created in each render, so we do not need 70020 // to worry about avoiding using legendModel kept in scope. 70021 onclick: bind( 70022 me._pageGo, me, pageDataIndexName, legendModel, api 70023 ) 70024 }, 70025 { 70026 x: -pageIconSize[0] / 2, 70027 y: -pageIconSize[1] / 2, 70028 width: pageIconSize[0], 70029 height: pageIconSize[1] 70030 } 70031 ); 70032 icon.name = name; 70033 controllerGroup.add(icon); 70034 } 70035 }, 70036 70037 /** 70038 * @override 70039 */ 70040 layoutInner: function (legendModel, itemAlign, maxSize) { 70041 var contentGroup = this.getContentGroup(); 70042 var containerGroup = this._containerGroup; 70043 var controllerGroup = this._controllerGroup; 70044 70045 var orientIdx = legendModel.getOrient().index; 70046 var wh = WH$1[orientIdx];
70047 var hw = WH$1[1 - orientIdx]; 70048 var yx = XY$1[1 - orientIdx]; 70049 70050 // Place items in contentGroup. 70051 box( 70052 legendModel.get('orient'), 70053 contentGroup, 70054 legendModel.get('itemGap'), 70055 !orientIdx ? null : maxSize.width, 70056 orientIdx ? null : maxSize.height 70057 ); 70058 70059 box( 70060 // Buttons in controller are layout always horizontally. 70061 'horizontal', 70062 controllerGroup, 70063 legendModel.get('pageButtonItemGap', true) 70064 ); 70065 70066 var contentRect = contentGroup.getBoundingRect(); 70067 var controllerRect = controllerGroup.getBoundingRect(); 70068 var showController = this._showController = contentRect[wh] > maxSize[wh]; 70069 70070 var contentPos = [-contentRect.x, -contentRect.y]; 70071 // Remain contentPos when scroll animation perfroming. 70072 contentPos[orientIdx] = contentGroup.position[orientIdx]; 70073 70074 // Layout container group based on 0. 70075 var containerPos = [0, 0]; 70076 var controllerPos = [-controllerRect.x, -controllerRect.y]; 70077 var pageButtonGap = retrieve2( 70078 legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true) 70079 ); 70080 70081 // Place containerGroup and controllerGroup and contentGroup. 70082 if (showController) { 70083 var pageButtonPosition = legendModel.get('pageButtonPosition', true); 70084 // controller is on the right / bottom. 70085 if (pageButtonPosition === 'end') { 70086 controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh]; 70087 } 70088 // controller is on the left / top. 70089 else { 70090 containerPos[orientIdx] += controllerRect[wh] + pageButtonGap; 70091 } 70092 } 70093 70094 // Always align controller to content as 'middle'. 70095 controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2; 70096 70097 contentGroup.attr('position', contentPos); 70098 containerGroup.attr('position', containerPos); 70099 controllerGroup.attr('position', controllerPos); 70100 70101 // Calculate `mainRect` and set `clipPath`. 70102 // mainRect should not be calculated by `this.group.getBoundingRect()` 70103 // for sake of the overflow. 70104 var mainRect = this.group.getBoundingRect(); 70105 var mainRect = {x: 0, y: 0}; 70106 // Consider content may be overflow (should be clipped). 70107 mainRect[wh] = showController ? maxSize[wh] : contentRect[wh]; 70108 mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); 70109 // `containerRect[yx] + containerPos[1 - orientIdx]` is 0. 70110 mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]); 70111 70112 containerGroup.__rectSize = maxSize[wh]; 70113 if (showController) { 70114 var clipShape = {x: 0, y: 0}; 70115 clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0); 70116 clipShape[hw] = mainRect[hw]; 70117 containerGroup.setClipPath(new Rect({shape: clipShape})); 70118 // Consider content may be larger than container, container rect 70119 // can not be obtained from `containerGroup.getBoundingRect()`. 70120 containerGroup.__rectSize = clipShape[wh]; 70121 } 70122 else { 70123 // Do not remove or ignore controller. Keep them set as place holders. 70124 controllerGroup.eachChild(function (child) { 70125 child.attr({invisible: true, silent: true}); 70126 }); 70127 } 70128 70129 // Content translate animation. 70130 var pageInfo = this._getPageInfo(legendModel); 70131 pageInfo.pageIndex != null && updateProps( 70132 contentGroup, 70133 {position: pageInfo.contentPosition}, 70134 // When switch from "show controller" to "not show controller", view should be 70135 // updated immediately without animation, otherwise causes weird efffect. 70136 showController ? legendModel : false 70137 ); 70138 70139 this._updatePageInfoView(legendModel, pageInfo); 70140 70141 return mainRect; 70142 }, 70143 70144 _pageGo: function (to, legendModel, api) { 70145 var scrollDataIndex = this._getPageInfo(legendModel)[to]; 70146 70147 scrollDataIndex != null && api.dispatchAction({ 70148 type: 'legendScroll', 70149 scrollDataIndex: scrollDataIndex, 70150 legendId: legendModel.id 70151 }); 70152 }, 70153 70154 _updatePageInfoView: function (legendModel, pageInfo) { 70155 var controllerGroup = this._controllerGroup; 70156 70157 each$1(['pagePrev', 'pageNext'], function (name) { 70158 var canJump = pageInfo[name + 'DataIndex'] != null; 70159 var icon = controllerGroup.childOfName(name); 70160 if (icon) { 70161 icon.setStyle( 70162 'fill', 70163 canJump 70164 ? legendModel.get('pageIconColor', true) 70165 : legendModel.get('pageIconInactiveColor', true) 70166 ); 70167 icon.cursor = canJump ? 'pointer' : 'default'; 70168 } 70169 }); 70170 70171 var pageText = controllerGroup.childOfName('pageText'); 70172 var pageFormatter = legendModel.get('pageFormatter'); 70173 var pageIndex = pageInfo.pageIndex; 70174 var current = pageIndex != null ? pageIndex + 1 : 0; 70175 var total = pageInfo.pageCount; 70176 70177 pageText && pageFormatter && pageText.setStyle( 70178 'text', 70179 isString(pageFormatter) 70180 ? pageFormatter.replace('{current}', current).replace('{total}', total) 70181 : pageFormatter({current: current, total: total}) 70182 ); 70183 }, 70184 70185 /** 70186 * @param {module:echarts/model/Model} legendModel 70187 * @return {Object} { 70188 * contentPosition: Array.<number>, null when data item not found. 70189 * pageIndex: number, null when data item not found.
70190 * pageCount: number, always be a number, can be 0. 70191 * pagePrevDataIndex: number, null when no next page. 70192 * pageNextDataIndex: number, null when no previous page. 70193 * } 70194 */ 70195 _getPageInfo: function (legendModel) { 70196 // Align left or top by the current dataIndex. 70197 var currDataIndex = legendModel.get('scrollDataIndex', true); 70198 var contentGroup = this.getContentGroup(); 70199 var contentRect = contentGroup.getBoundingRect(); 70200 var containerRectSize = this._containerGroup.__rectSize; 70201 70202 var orientIdx = legendModel.getOrient().index; 70203 var wh = WH$1[orientIdx]; 70204 var hw = WH$1[1 - orientIdx]; 70205 var xy = XY$1[orientIdx]; 70206 var contentPos = contentGroup.position.slice(); 70207 70208 var pageIndex; 70209 var pagePrevDataIndex; 70210 var pageNextDataIndex; 70211 70212 var targetItemGroup; 70213 if (this._showController) { 70214 contentGroup.eachChild(function (child) { 70215 if (child.__legendDataIndex === currDataIndex) { 70216 targetItemGroup = child; 70217 } 70218 }); 70219 } 70220 else { 70221 targetItemGroup = contentGroup.childAt(0); 70222 } 70223 70224 var pageCount = containerRectSize ? Math.ceil(contentRect[wh] / containerRectSize) : 0; 70225 70226 if (targetItemGroup) { 70227 var itemRect = targetItemGroup.getBoundingRect(); 70228 var itemLoc = targetItemGroup.position[orientIdx] + itemRect[xy]; 70229 contentPos[orientIdx] = -itemLoc - contentRect[xy]; 70230 pageIndex = Math.floor( 70231 pageCount * (itemLoc + itemRect[xy] + containerRectSize / 2) / contentRect[wh] 70232 ); 70233 pageIndex = (contentRect[wh] && pageCount) 70234 ? Math.max(0, Math.min(pageCount - 1, pageIndex)) 70235 : -1; 70236 70237 var winRect = {x: 0, y: 0}; 70238 winRect[wh] = containerRectSize; 70239 winRect[hw] = contentRect[hw]; 70240 winRect[xy] = -contentPos[orientIdx] - contentRect[xy]; 70241 70242 var startIdx; 70243 var children = contentGroup.children(); 70244 70245 contentGroup.eachChild(function (child, index) { 70246 var itemRect = getItemRect(child); 70247 70248 if (itemRect.intersect(winRect)) { 70249 startIdx == null && (startIdx = index); 70250 // It is user-friendly that the last item shown in the 70251 // current window is shown at the begining of next window. 70252 pageNextDataIndex = child.__legendDataIndex; 70253 } 70254 70255 // If the last item is shown entirely, no next page. 70256 if (index === children.length - 1 70257 && itemRect[xy] + itemRect[wh] <= winRect[xy] + winRect[wh] 70258 ) { 70259 pageNextDataIndex = null; 70260 } 70261 }); 70262 70263 // Always align based on the left/top most item, so the left/top most 70264 // item in the previous window is needed to be found here. 70265 if (startIdx != null) { 70266 var startItem = children[startIdx]; 70267 var startRect = getItemRect(startItem); 70268 winRect[xy] = startRect[xy] + startRect[wh] - winRect[wh]; 70269 70270 // If the first item is shown entirely, no previous page. 70271 if (startIdx <= 0 && startRect[xy] >= winRect[xy]) { 70272 pagePrevDataIndex = null; 70273 } 70274 else { 70275 while (startIdx > 0 && getItemRect(children[startIdx - 1]).intersect(winRect)) { 70276 startIdx--; 70277 } 70278 pagePrevDataIndex = children[startIdx].__legendDataIndex; 70279 } 70280 } 70281 } 70282 70283 return { 70284 contentPosition: contentPos, 70285 pageIndex: pageIndex, 70286 pageCount: pageCount, 70287 pagePrevDataIndex: pagePrevDataIndex, 70288 pageNextDataIndex: pageNextDataIndex 70289 }; 70290 70291 function getItemRect(el) {
70292 var itemRect = el.getBoundingRect().clone(); 70293 itemRect[xy] += el.position[orientIdx]; 70294 return itemRect; 70295 } 70296 } 70297 70298 }); 70299 70300 /* 70301* Licensed to the Apache Software Foundation (ASF) under one 70302* or more contributor license agreements. See the NOTICE file 70303* distributed with this work for additional information 70304* regarding copyright ownership. The ASF licenses this file 70305* to you under the Apache License, Version 2.0 (the 70306* "License"); you may not use this file except in compliance 70307* with the License. You may obtain a copy of the License at 70308* 70309* http://www.apache.org/licenses/LICENSE-2.0 70310* 70311* Unless required by applicable law or agreed to in writing, 70312* software distributed under the License is distributed on an 70313* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 70314* KIND, either express or implied. See the License for the 70315* specific language governing permissions and limitations 70316* under the License. 70317*/ 70318 70319 /** 70320 * @event legendScroll 70321 * @type {Object} 70322 * @property {string} type 'legendScroll' 70323 * @property {string} scrollDataIndex 70324 */ 70325 registerAction( 70326 'legendScroll', 'legendscroll', 70327 function (payload, ecModel) { 70328 var scrollDataIndex = payload.scrollDataIndex; 70329 70330 scrollDataIndex != null && ecModel.eachComponent( 70331 {mainType: 'legend', subType: 'scroll', query: payload}, 70332 function (legendModel) { 70333 legendModel.setScrollDataIndex(scrollDataIndex); 70334 } 70335 ); 70336 } 70337 ); 70338 70339 /* 70340* Licensed to the Apache Software Foundation (ASF) under one 70341* or more contributor license agreements. See the NOTICE file 70342* distributed with this work for additional information 70343* regarding copyright ownership. The ASF licenses this file 70344* to you under the Apache License, Version 2.0 (the 70345* "License"); you may not use this file except in compliance 70346* with the License. You may obtain a copy of the License at 70347* 70348* http://www.apache.org/licenses/LICENSE-2.0 70349* 70350* Unless required by applicable law or agreed to in writing, 70351* software distributed under the License is distributed on an 70352* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 70353* KIND, either express or implied. See the License for the 70354* specific language governing permissions and limitations 70355* under the License. 70356*/ 70357 70358 /** 70359 * Legend component entry file8 70360 */ 70361 70362 /* 70363* Licensed to the Apache Software Foundation (ASF) under one 70364* or more contributor license agreements. See the NOTICE file 70365* distributed with this work for additional information 70366* regarding copyright ownership. The ASF licenses this file 70367* to you under the Apache License, Version 2.0 (the 70368* "License"); you may not use this file except in compliance 70369* with the License. You may obtain a copy of the License at 70370* 70371* http://www.apache.org/licenses/LICENSE-2.0 70372* 70373* Unless required by applicable law or agreed to in writing, 70374* software distributed under the License is distributed on an 70375* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 70376* KIND, either express or implied. See the License for the 70377* specific language governing permissions and limitations 70378* under the License. 70379*/ 70380 70381 extendComponentModel({ 70382 70383 type: 'tooltip', 70384 70385 dependencies: ['axisPointer'], 70386 70387 defaultOption: { 70388 zlevel: 0, 70389 70390 z: 8, 70391 70392 show: true, 70393 70394 // tooltip主ä½âÃ¥â â¦Ã¥Â®Â¹ 70395 showContent: true, 70396 70397 // 'trigger' only works on coordinate system. 70398 // 'item' | 'axis' | 'none' 70399 trigger: 'item', 70400 70401 // 'click' | 'mousemove' | 'none' 70402 triggerOn: 'mousemove|click', 70403 70404 alwaysShowContent: false, 70405 70406 displayMode: 'single', // 'single' | 'multipleByCoordSys' 70407 70408 // ä½Âç½® {Array} | {Function} 70409 // position: null 70410 // Consider triggered from axisPointer handle, verticalAlign should be 'middle' 70411 // align: null, 70412 // verticalAlign: null, 70413 70414 // æË¯å¦约æÂŸ content åŨ viewRect ä¸Âãâ¬âé»ËèÂ
70414®Â¤ false æË¯ä¸ºäºâ Ã¥â¦Â¼Ã¥Â®Â¹Ã¤Â»Â¥Ã¥â°Âçâ°ËæÅ‹â¬â 70415 confine: false, 70416 70417 // Ã¥â â¦Ã¥Â®Â¹Ã¦ ¼å¼ÂÃ¥â¢Â¨Ã¯Â¼Å¡{string}ï¼ËTemplateï¼Ⱐæ {Function} 70418 // formatter: null 70419 70420 showDelay: 0, 70421 70422 // éšÂèâÂ延迟ï¼ÅÃ¥Ââ¢Ã¤Â½Âms 70423 hideDelay: 100, 70424 70425 // 动çâȌÂËæÂ¢æâ¶éâ´ï¼ÅÃ¥Ââ¢Ã¤Â½Âs 70426 transitionDuration: 0.4, 70427 70428 enterable: false, 70429 70430 // æÂÂ示èÆÅæâ¢Â¯Ã©Â¢Åèâ°Â²Ã¯Â¼Åé»Ë认为éâ¬ÂæËŽåº¦ä¸º0.7çšâé»âèâ°Â² 70431 backgroundColor: 'rgba(50,50,50,0.7)', 70432 70433 // æÂÂ示边æ¡â é¢Åèâ°Â² 70434 borderColor: '#333', 70435 70436 // æÂÂ示边æ¡â Ã¥Åâ è§âï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认为4 70437 borderRadius: 4, 70438 70439 // æÂÂ示边æ¡â 线宽ï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认为0ï¼Ëæâ è¾¹æ¡â ï¼Ⱐ70440 borderWidth: 0, 70441 70442 // æÂÂ示åâ â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认åÂâæâ¹åÂâÃ¥â â¦Ã¨Â¾Â¹Ã¨Â·Â为5*70443 // 接åÂâæâ¢Â°Ã§Â»âÃ¥Ëâ å˫设定上å³ä¸â¹Ã¥Â·Â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥ÂÅcss 70444 padding: 5, 70445 70446 // Extra css text 70447 extraCssText: '', 70448 70449 // Ã¥ÂÂæ â¡Ã¨Â½Â´Ã¦Åâ¡Ã§Â¤ÂºÃ¥â¢Â¨Ã¯Â¼ÅÃ¥ÂÂæ â¡Ã¨Â½Â´Ã¨Â§Â¦Ã¥ÂâæÅâ°Ã¦â¢Ë 70450 axisPointer: { 70451 // é»Ë认为çâºÂ´Ã§ÂºÂ¿ 70452 // å¯éâ¬â°Ã¤Â¸ÂºÃ¯Â¼Å¡'line' | 'shadow' | 'cross' 70453 type: 'line', 70454 70455 // type 为 line çšâæâ¶åâ¬â¢Ã¦Åâ°Ã¦â¢Ëï¼ÅæÅâ¡Ã¥Â®Å¡ tooltip line æâ°â¬Ã¥Å¨çšâè½´ï¼Åå¯éâ¬â° 70456 // å¯éâ¬â° 'x' | 'y' | 'angle' | 'radius' | 'auto' 70457 // é»Ë认 'auto'ï¼Å会éâ¬â°Ã¦â¹Â©Ã§Â±Â»Ã¥Å¾â¹Ã¤Â¸Âº category çšâè½´ï¼Å对于åÂÅæâ¢Â°Ã¥â¬Â¼Ã¨Â½Â´Ã¯Â¼Åç¬âºÃ¥Â¡å°âÃ¥ÂÂæ â¡Ã§Â³Â»Ã¤Â¼Å¡Ã©Â»Ë认éâ¬â°Ã¦â¹Â© x è½´ 70458 // æžÂÃ¥ÂÂæ â¡Ã§Â³Â»Ã¤Â¼Å¡Ã©Â»Ë认éâ¬â°Ã¦â¹Â© angle è½´ 70459 axis: 'auto', 70460 70461 animation: 'auto', 70462 animationDurationUpdate: 200, 70463 animationEasingUpdate: 'exponentialOut', 70464 70465 crossStyle: { 70466 color: '#999', 70467 width: 1, 70468 type: 'dashed', 70469 70470 // TODO formatter 70471 textStyle: {} 70472 } 70473 70474 // lineStyle and shadowStyle should not be specified here, 70475 // otherwise it will always override those styles on option.axisPointer. 70476 }, 70477 textStyle: { 70478 color: '#fff', 70479 fontSize: 14 70480 } 70481 } 70482 }); 70483 70484 /* 70485* Licensed to the Apache Software Foundation (ASF) under one 70486* or more contributor license agreements. See the NOTICE file 70487* distributed with this work for additional information 70488* regarding copyright ownership. The ASF licenses this file 70489* to you under the Apache License, Version 2.0 (the 70490* "License"); you may not use this file except in compliance 70491* with the License. You may obtain a copy of the License at 70492* 70493* http://www.apache.org/licenses/LICENSE-2.0 70494* 70495* Unless required by applicable law or agreed to in writing, 70496* software distributed under the License is distributed on an 70497* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 70498* KIND, either express or implied. See the License for the 70499* specific language governing permissions and limitations 70500* under the License. 70501*/ 70502 70503 var each$18 = each$1; 70504 var toCamelCase$1 = toCamelCase; 70505 70506 var vendors = ['', '-webkit-', '-moz-', '-o-']; 70507 70508 var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;'; 70509 70510 /** 70511 * @param {number} duration 70512 * @return {string} 70513 * @inner 70514 */ 70515 function assembleTransition(duration) { 70516 var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)'; 70517 var transitionText = 'left ' + duration + 's ' + transitionCurve + ',' 70518 + 'top ' + duration + 's ' + transitionCurve; 70519 return map(vendors, function (vendorPrefix) { 70520 return vendorPrefix + 'transition:' + transitionText; 70521 }).join(';'); 70522 } 70523 70524 /** 70525 * @param {Object} textStyle 70526 * @return {string} 70527 * @inner 70528 */ 70529 function assembleFont(textStyleModel) { 70530 var cssText = []; 70531 70532 var fontSize = textStyleModel.get('fontSize'); 70533 var color = textStyleModel.getTextColor(); 70534 70535 color && cssText.push('color:' + color); 70536 70537 cssText.push('font:' + textStyleModel.getFont()); 70538 70539 fontSize && 70540 cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px'); 70541 70542 each$18(['decoration', 'align'], function (name) { 70543 var val = textStyleModel.get(name); 70544 val && cssText.push('text-' + name + ':' + val); 70545 }); 70546 70547 return cssText.join(';'); 70548 } 70549 70550 /** 70551 * @param {Object} tooltipModel 70552 * @return {string} 70553 * @inner 70554 */
70555 function assembleCssText(tooltipModel) { 70556 70557 var cssText = []; 70558 70559 var transitionDuration = tooltipModel.get('transitionDuration'); 70560 var backgroundColor = tooltipModel.get('backgroundColor'); 70561 var textStyleModel = tooltipModel.getModel('textStyle'); 70562 var padding = tooltipModel.get('padding'); 70563 70564 // Animation transition. Do not animate when transitionDuration is 0. 70565 transitionDuration && 70566 cssText.push(assembleTransition(transitionDuration)); 70567 70568 if (backgroundColor) { 70569 if (env$1.canvasSupported) { 70570 cssText.push('background-Color:' + backgroundColor); 70571 } 70572 else { 70573 // for ie 70574 cssText.push( 70575 'background-Color:#' + toHex(backgroundColor) 70576 ); 70577 cssText.push('filter:alpha(opacity=70)'); 70578 } 70579 } 70580 70581 // Border style 70582 each$18(['width', 'color', 'radius'], function (name) { 70583 var borderName = 'border-' + name; 70584 var camelCase = toCamelCase$1(borderName); 70585 var val = tooltipModel.get(camelCase); 70586 val != null && 70587 cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px')); 70588 }); 70589 70590 // Text style 70591 cssText.push(assembleFont(textStyleModel)); 70592 70593 // Padding 70594 if (padding != null) { 70595 cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px'); 70596 } 70597 70598 return cssText.join(';') + ';'; 70599 } 70600 70601 /** 70602 * @alias module:echarts/component/tooltip/TooltipContent 70603 * @constructor 70604 */ 70605 function TooltipContent(container, api) { 70606 if (env$1.wxa) { 70607 return null; 70608 } 70609 70610 var el = document.createElement('div'); 70611 var zr = this._zr = api.getZr(); 70612 70613 this.el = el; 70614 70615 this._x = api.getWidth() / 2; 70616 this._y = api.getHeight() / 2; 70617 70618 container.appendChild(el); 70619 70620 this._container = container; 70621 70622 this._show = false; 70623 70624 /** 70625 * @private 70626 */ 70627 this._hideTimeout; 70628 70629 var self = this; 70630 el.onmouseenter = function () { 70631 // clear the timeout in hideLater and keep showing tooltip 70632 if (self._enterable) { 70633 clearTimeout(self._hideTimeout); 70634 self._show = true; 70635 } 70636 self._inContent = true; 70637 }; 70638 el.onmousemove = function (e) { 70639 e = e || window.event; 70640 if (!self._enterable) { 70641 // Try trigger zrender event to avoid mouse 70642 // in and out shape too frequently 70643 var handler = zr.handler; 70644 normalizeEvent(container, e, true); 70645 handler.dispatch('mousemove', e); 70646 } 70647 }; 70648 el.onmouseleave = function () { 70649 if (self._enterable) { 70650 if (self._show) { 70651 self.hideLater(self._hideDelay); 70652 } 70653 } 70654 self._inContent = false; 70655 }; 70656 } 70657 70658 TooltipContent.prototype = { 70659 70660 constructor: TooltipContent, 70661 70662 /** 70663 * @private 70664 * @type {boolean} 70665 */ 70666 _enterable: true, 70667 70668 /** 70669 * Update when tooltip is rendered 70670 */ 70671 update: function () { 70672 // FIXME 70673 // Move this logic to ec main? 70674 var container = this._container; 70675 var stl = container.currentStyle 70676 || document.defaultView.getComputedStyle(container); 70677 var domStyle = container.style; 70678 if (domStyle.position !== 'absolute' && stl.position !== 'absolute') { 70679 domStyle.position = 'relative'; 70680 } 70681 // Hide the tooltip 70682 // PENDING 70683 // this.hide(); 70684 }, 70685 70686 show: function (tooltipModel) { 70687 clearTimeout(this._hideTimeout); 70688 var el = this.el; 70689
70690 el.style.cssText = gCssText + assembleCssText(tooltipModel) 70691 // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore 70692 + ';left:' + this._x + 'px;top:' + this._y + 'px;' 70693 + (tooltipModel.get('extraCssText') || ''); 70694 70695 el.style.display = el.innerHTML ? 'block' : 'none'; 70696 70697 this._show = true; 70698 }, 70699 70700 setContent: function (content) { 70701 this.el.innerHTML = content == null ? '' : content; 70702 }, 70703 70704 setEnterable: function (enterable) { 70705 this._enterable = enterable; 70706 }, 70707 70708 getSize: function () { 70709 var el = this.el; 70710 return [el.clientWidth, el.clientHeight]; 70711 }, 70712 70713 moveTo: function (x, y) { 70714 // xy should be based on canvas root. But tooltipContent is 70715 // the sibling of canvas root. So padding of ec container 70716 // should be considered here. 70717 var zr = this._zr; 70718 var viewportRootOffset; 70719 if (zr && zr.painter && (viewportRootOffset = zr.painter.getViewportRootOffset())) { 70720 x += viewportRootOffset.offsetLeft; 70721 y += viewportRootOffset.offsetTop; 70722 } 70723 70724 var style = this.el.style; 70725 style.left = x + 'px'; 70726 style.top = y + 'px'; 70727 70728 this._x = x; 70729 this._y = y; 70730 }, 70731 70732 hide: function () { 70733 this.el.style.display = 'none'; 70734 this._show = false; 70735 }, 70736 70737 hideLater: function (time) { 70738 if (this._show && !(this._inContent && this._enterable)) { 70739 if (time) { 70740 this._hideDelay = time; 70741 // Set show false to avoid invoke hideLater mutiple times 70742 this._show = false; 70743 this._hideTimeout = setTimeout(bind(this.hide, this), time); 70744 } 70745 else { 70746 this.hide(); 70747 } 70748 } 70749 }, 70750 70751 isShow: function () { 70752 return this._show; 70753 } 70754 }; 70755 70756 /* 70757* Licensed to the Apache Software Foundation (ASF) under one 70758* or more contributor license agreements. See the NOTICE file 70759* distributed with this work for additional information 70760* regarding copyright ownership. The ASF licenses this file 70761* to you under the Apache License, Version 2.0 (the 70762* "License"); you may not use this file except in compliance 70763* with the License. You may obtain a copy of the License at 70764* 70765* http://www.apache.org/licenses/LICENSE-2.0 70766* 70767* Unless required by applicable law or agreed to in writing, 70768* software distributed under the License is distributed on an 70769* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 70770* KIND, either express or implied. See the License for the 70771* specific language governing permissions and limitations 70772* under the License. 70773*/ 70774 70775 var bind$3 = bind; 70776 var each$17 = each$1; 70777 var parsePercent$2 = parsePercent$1; 70778 70779 var proxyRect = new Rect({ 70780 shape: {x: -1, y: -1, width: 2, height: 2} 70781 }); 70782 70783 extendComponentView({ 70784 70785 type: 'tooltip', 70786 70787 init: function (ecModel, api) { 70788 if (env$1.node) { 70789 return; 70790 } 70791 var tooltipContent = new TooltipContent(api.getDom(), api); 70792 this._tooltipContent = tooltipContent; 70793 }, 70794 70795 render: function (tooltipModel, ecModel, api) { 70796 if (env$1.node || env$1.wxa) { 70797 return; 70798 } 70799 70800 // Reset 70801 this.group.removeAll(); 70802 70803 /** 70804 * @private 70805 * @type {module:echarts/component/tooltip/TooltipModel} 70806 */ 70807 this._tooltipModel = tooltipModel; 70808 70809 /** 70810 * @private 70811 * @type {module:echarts/model/Global} 70812 */ 70813 this._ecModel = ecModel; 70814 70815 /** 70816 * @private 70817 * @type {module:echarts/ExtensionAPI} 70818 */ 70819 this._api = api; 70820 70821 /** 70822 * Should be cleaned when render. 70823 * @private 70824 * @type {Array.<Array.<Object>>} 70825 */ 70826 this._lastDataByCoordSys = null; 70827 70828 /** 70829 * @private 70830 * @type {boolean} 70831 */ 70832 this._alwaysShowContent = tooltipModel.get('alwaysShowContent'); 70833 70834 var tooltipContent = this._tooltipContent;
70835 tooltipContent.update(); 70836 tooltipContent.setEnterable(tooltipModel.get('enterable')); 70837 70838 this._initGlobalListener(); 70839 70840 this._keepShow(); 70841 }, 70842 70843 _initGlobalListener: function () { 70844 var tooltipModel = this._tooltipModel; 70845 var triggerOn = tooltipModel.get('triggerOn'); 70846 70847 register( 70848 'itemTooltip', 70849 this._api, 70850 bind$3(function (currTrigger, e, dispatchAction) { 70851 // If 'none', it is not controlled by mouse totally. 70852 if (triggerOn !== 'none') { 70853 if (triggerOn.indexOf(currTrigger) >= 0) { 70854 this._tryShow(e, dispatchAction); 70855 } 70856 else if (currTrigger === 'leave') { 70857 this._hide(dispatchAction); 70858 } 70859 } 70860 }, this) 70861 ); 70862 }, 70863 70864 _keepShow: function () { 70865 var tooltipModel = this._tooltipModel; 70866 var ecModel = this._ecModel; 70867 var api = this._api; 70868 70869 // Try to keep the tooltip show when refreshing 70870 if (this._lastX != null 70871 && this._lastY != null 70872 // When user is willing to control tooltip totally using API, 70873 // self.manuallyShowTip({x, y}) might cause tooltip hide, 70874 // which is not expected. 70875 && tooltipModel.get('triggerOn') !== 'none' 70876 ) { 70877 var self = this; 70878 clearTimeout(this._refreshUpdateTimeout); 70879 this._refreshUpdateTimeout = setTimeout(function () { 70880 // Show tip next tick after other charts are rendered 70881 // In case highlight action has wrong result 70882 // FIXME 70883 self.manuallyShowTip(tooltipModel, ecModel, api, { 70884 x: self._lastX, 70885 y: self._lastY 70886 }); 70887 }); 70888 } 70889 }, 70890 70891 /** 70892 * Show tip manually by 70893 * dispatchAction({ 70894 * type: 'showTip', 70895 * x: 10, 70896 * y: 10 70897 * }); 70898 * Or 70899 * dispatchAction({ 70900 * type: 'showTip', 70901 * seriesIndex: 0, 70902 * dataIndex or dataIndexInside or name 70903 * }); 70904 * 70905 * TODO Batch 70906 */ 70907 manuallyShowTip: function (tooltipModel, ecModel, api, payload) { 70908 if (payload.from === this.uid || env$1.node) { 70909 return; 70910 } 70911 70912 var dispatchAction = makeDispatchAction$1(payload, api); 70913 70914 // Reset ticket 70915 this._ticket = ''; 70916 70917 // When triggered from axisPointer. 70918 var dataByCoordSys = payload.dataByCoordSys; 70919 70920 if (payload.tooltip && payload.x != null && payload.y != null) { 70921 var el = proxyRect; 70922 el.position = [payload.x, payload.y]; 70923 el.update(); 70924 el.tooltip = payload.tooltip; 70925 // Manually show tooltip while view is not using zrender elements. 70926 this._tryShow({ 70927 offsetX: payload.x, 70928 offsetY: payload.y, 70929 target: el 70930 }, dispatchAction); 70931 } 70932 else if (dataByCoordSys) { 70933 this._tryShow({ 70934 offsetX: payload.x, 70935 offsetY: payload.y, 70936 position: payload.position, 70937 event: {}, 70938 dataByCoordSys: payload.dataByCoordSys, 70939 tooltipOption: payload.tooltipOption 70940 }, dispatchAction); 70941 } 70942 else if (payload.seriesIndex != null) { 70943 70944 if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) { 70945 return; 70946 } 70947 70948 var pointInfo = findPointFromSeries(payload, ecModel); 70949 var cx = pointInfo.point[0]; 70950 var cy = pointInfo.point[1]; 70951 if (cx != null && cy != null) { 70952 this._tryShow({ 70953 offsetX: cx, 70954 offsetY: cy, 70955 position: payload.position, 70956 target: pointInfo.el, 70957 event: {} 70958 }, dispatchAction); 70959 } 70960 } 70961 else if (payload.x != null && payload.y != null) { 70962 // FIXME 70963 // should wrap dispatchAction like `axisPointer/globalListener` ? 70964 api.dispatchAction({ 70965 type: 'updateAxisPointer', 70966 x: payload.x, 70967 y: payload.y 70968 }); 70969 70970 this._tryShow({ 70971 offsetX: payload.x, 70972 offsetY: payload.y, 70973 position: payload.position, 70974 target: api.getZr().findHover(payload.x, payload.y).target, 70975 event: {} 70976 }, dispatchAction); 70977 } 70978 }, 70979 70980 manuallyHideTip: function (tooltipModel, ecModel, api, payload) { 70981 var tooltipContent = this._tooltipContent; 70982 70983 if (!this._alwaysShowContent && this._tooltipModel) { 70984 tooltipContent.hideLater(this._tooltipModel.get('hideDelay')); 70985 } 70986 70987 this._lastX = this._lastY = null; 70988 70989 if (payload.from !== this.uid) { 70990 this._hide(makeDispatchAction$1(payload, api)); 70991 } 70992 }, 70993 70994 // Be compatible with previous design, that is, when tooltip.type is 'axis' and 70995 // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer 70996 // and tooltip. 70997 _manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) { 70998 var seriesIndex = payload.seriesIndex; 70999 var dataIndex = payload.dataIndex; 71000 var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; 71001 71002 if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) { 71003 return; 71004 } 71005 71006 var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
71007 if (!seriesModel) { 71008 return; 71009 } 71010 71011 var data = seriesModel.getData(); 71012 var tooltipModel = buildTooltipModel([ 71013 data.getItemModel(dataIndex), 71014 seriesModel, 71015 (seriesModel.coordinateSystem || {}).model, 71016 tooltipModel 71017 ]); 71018 71019 if (tooltipModel.get('trigger') !== 'axis') { 71020 return; 71021 } 71022 71023 api.dispatchAction({ 71024 type: 'updateAxisPointer', 71025 seriesIndex: seriesIndex, 71026 dataIndex: dataIndex, 71027 position: payload.position 71028 }); 71029 71030 return true; 71031 }, 71032 71033 _tryShow: function (e, dispatchAction) { 71034 var el = e.target; 71035 var tooltipModel = this._tooltipModel; 71036 71037 if (!tooltipModel) { 71038 return; 71039 } 71040 71041 // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed 71042 this._lastX = e.offsetX; 71043 this._lastY = e.offsetY; 71044 71045 var dataByCoordSys = e.dataByCoordSys; 71046 if (dataByCoordSys && dataByCoordSys.length) { 71047 this._showAxisTooltip(dataByCoordSys, e); 71048 } 71049 // Always show item tooltip if mouse is on the element with dataIndex 71050 else if (el && el.dataIndex != null) { 71051 this._lastDataByCoordSys = null; 71052 this._showSeriesItemTooltip(e, el, dispatchAction); 71053 } 71054 // Tooltip provided directly. Like legend. 71055 else if (el && el.tooltip) { 71056 this._lastDataByCoordSys = null; 71057 this._showComponentItemTooltip(e, el, dispatchAction); 71058 } 71059 else { 71060 this._lastDataByCoordSys = null; 71061 this._hide(dispatchAction); 71062 } 71063 }, 71064 71065 _showOrMove: function (tooltipModel, cb) { 71066 // showDelay is used in this case: tooltip.enterable is set 71067 // as true. User intent to move mouse into tooltip and click 71068 // something. `showDelay` makes it easyer to enter the content 71069 // but tooltip do not move immediately. 71070 var delay = tooltipModel.get('showDelay'); 71071 cb = bind(cb, this); 71072 clearTimeout(this._showTimout); 71073 delay > 0 71074 ? (this._showTimout = setTimeout(cb, delay)) 71075 : cb(); 71076 }, 71077 71078 _showAxisTooltip: function (dataByCoordSys, e) { 71079 var ecModel = this._ecModel; 71080 var globalTooltipModel = this._tooltipModel; 71081 var point = [e.offsetX, e.offsetY]; 71082 var singleDefaultHTML = []; 71083 var singleParamsList = []; 71084 var singleTooltipModel = buildTooltipModel([ 71085 e.tooltipOption, 71086 globalTooltipModel 71087 ]); 71088 71089 each$17(dataByCoordSys, function (itemCoordSys) { 71090 // var coordParamList = []; 71091 // var coordDefaultHTML = []; 71092 // var coordTooltipModel = buildTooltipModel([ 71093 // e.tooltipOption, 71094 // itemCoordSys.tooltipOption, 71095 // ecModel.getComponent(itemCoordSys.coordSysMainType, itemCoordSys.coordSysIndex), 71096 // globalTooltipModel 71097 // ]); 71098 // var displayMode = coordTooltipModel.get('displayMode'); 71099 // var paramsList = displayMode === 'single' ? singleParamsList : []; 71100 71101 each$17(itemCoordSys.dataByAxis, function (item) { 71102 var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex); 71103 var axisValue = item.value; 71104 var seriesDefaultHTML = []; 71105 71106 if (!axisModel || axisValue == null) { 71107 return; 71108 } 71109 71110 var valueLabel = getValueLabel( 71111 axisValue, axisModel.axis, ecModel, 71112 item.seriesDataIndices, 71113 item.valueLabelOpt 71114 ); 71115 71116 each$1(item.seriesDataIndices, function (idxItem) { 71117 var series = ecModel.getSeriesByIndex(idxItem.seriesIndex); 71118 var dataIndex = idxItem.dataIndexInside; 71119 var dataParams = series && series.getDataParams(dataIndex); 71120 dataParams.axisDim = item.axisDim; 71121 dataParams.axisIndex = item.axisIndex; 71122 dataParams.axisType = item.axisType; 71123 dataParams.axisId = item.axisId; 71124 dataParams.axisValue = getAxisRawValue(axisModel.axis, axisValue); 71125 dataParams.axisValueLabel = valueLabel; 71126 71127 if (dataParams) { 71128 singleParamsList.push(dataParams); 71129 seriesDefaultHTML.push(series.formatTooltip(dataIndex, true)); 71130 } 71131 }); 71132 71133 // Default tooltip content 71134 // FIXME 71135 // (1) shold be the first data which has name? 71136 // (2) themeRiver, firstDataIndex is array, and first line is unnecessary. 71137 var firstLine = valueLabel; 71138 singleDefaultHTML.push( 71139 (firstLine ? encodeHTML(firstLine) + '<br />' : '') 71140 + seriesDefaultHTML.join('<br />') 71141 ); 71142 }); 71143 }, this); 71144 71145 // In most case, the second axis is shown upper than the first one. 71146 singleDefaultHTML.reverse(); 71147 singleDefaultHTML = singleDefaultHTML.join('<br /><br />'); 71148 71149 var positionExpr = e.position; 71150 this._showOrMove(singleTooltipModel, function () { 71151 if (this._updateContentNotChangedOnAxis(dataByCoordSys)) { 71152 this._updatePosition( 71153 singleTooltipModel, 71154 positionExpr, 71155 point[0], point[1], 71156 this._tooltipContent, 71157 singleParamsList 71158 ); 71159 } 71160 else { 71161 this._showTooltipContent( 71162 singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(), 71163 point[0], point[1], positionExpr 71164 ); 71165 } 71166 }); 71167
71168 // Do not trigger events here, because this branch only be entered 71169 // from dispatchAction. 71170 }, 71171 71172 _showSeriesItemTooltip: function (e, el, dispatchAction) { 71173 var ecModel = this._ecModel; 71174 // Use dataModel in element if possible 71175 // Used when mouseover on a element like markPoint or edge 71176 // In which case, the data is not main data in series. 71177 var seriesIndex = el.seriesIndex; 71178 var seriesModel = ecModel.getSeriesByIndex(seriesIndex); 71179 71180 // For example, graph link. 71181 var dataModel = el.dataModel || seriesModel; 71182 var dataIndex = el.dataIndex; 71183 var dataType = el.dataType; 71184 var data = dataModel.getData(); 71185 71186 var tooltipModel = buildTooltipModel([ 71187 data.getItemModel(dataIndex), 71188 dataModel, 71189 seriesModel && (seriesModel.coordinateSystem || {}).model, 71190 this._tooltipModel 71191 ]); 71192 71193 var tooltipTrigger = tooltipModel.get('trigger'); 71194 if (tooltipTrigger != null && tooltipTrigger !== 'item') { 71195 return; 71196 } 71197 71198 var params = dataModel.getDataParams(dataIndex, dataType); 71199 var defaultHtml = dataModel.formatTooltip(dataIndex, false, dataType); 71200 var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex; 71201 71202 this._showOrMove(tooltipModel, function () { 71203 this._showTooltipContent( 71204 tooltipModel, defaultHtml, params, asyncTicket, 71205 e.offsetX, e.offsetY, e.position, e.target 71206 ); 71207 }); 71208 71209 // FIXME 71210 // duplicated showtip if manuallyShowTip is called from dispatchAction. 71211 dispatchAction({ 71212 type: 'showTip', 71213 dataIndexInside: dataIndex, 71214 dataIndex: data.getRawIndex(dataIndex), 71215 seriesIndex: seriesIndex, 71216 from: this.uid 71217 }); 71218 }, 71219 71220 _showComponentItemTooltip: function (e, el, dispatchAction) { 71221 var tooltipOpt = el.tooltip; 71222 if (typeof tooltipOpt === 'string') { 71223 var content = tooltipOpt; 71224 tooltipOpt = { 71225 content: content, 71226 // Fixed formatter 71227 formatter: content 71228 }; 71229 } 71230 var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel); 71231 var defaultHtml = subTooltipModel.get('content'); 71232 var asyncTicket = Math.random(); 71233 71234 // Do not check whether `trigger` is 'none' here, because `trigger` 71235 // only works on cooridinate system. In fact, we have not found case 71236 // that requires setting `trigger` nothing on component yet. 71237 71238 this._showOrMove(subTooltipModel, function () { 71239 this._showTooltipContent( 71240 subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {}, 71241 asyncTicket, e.offsetX, e.offsetY, e.position, el 71242 ); 71243 }); 71244 71245 // If not dispatch showTip, tip may be hide triggered by axis. 71246 dispatchAction({ 71247 type: 'showTip', 71248 from: this.uid 71249 }); 71250 }, 71251 71252 _showTooltipContent: function ( 71253 tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el 71254 ) { 71255 // Reset ticket 71256 this._ticket = ''; 71257 71258 if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) { 71259 return; 71260 } 71261 71262 var tooltipContent = this._tooltipContent; 71263 71264 var formatter = tooltipModel.get('formatter'); 71265 positionExpr = positionExpr || tooltipModel.get('position'); 71266 var html = defaultHtml; 71267 71268 if (formatter && typeof formatter === 'string') { 71269 html = formatTpl(formatter, params, true); 71270 } 71271 else if (typeof formatter === 'function') { 71272 var callback = bind$3(function (cbTicket, html) { 71273 if (cbTicket === this._ticket) { 71274 tooltipContent.setContent(html); 71275 this._updatePosition( 71276 tooltipModel, positionExpr, x, y, tooltipContent, params, el 71277 ); 71278 } 71279 }, this); 71280 this._ticket = asyncTicket; 71281 html = formatter(params, asyncTicket, callback); 71282 } 71283 71284 tooltipContent.setContent(html); 71285 tooltipContent.show(tooltipModel); 71286 71287 this._updatePosition( 71288 tooltipModel, positionExpr, x, y, tooltipContent, params, el 71289 ); 71290 }, 71291 71292 /** 71293 * @param {string|Function|Array.<number>|Object} positionExpr 71294 * @param {number} x Mouse x 71295 * @param {number} y Mouse y 71296 * @param {boolean} confine Whether confine tooltip content in view rect. 71297 * @param {Object|<Array.<Object>} params 71298 * @param {module:zrender/Element} el target element 71299 * @param {module:echarts/ExtensionAPI} api 71300 * @return {Array.<number>} 71301 */ 71302 _updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) { 71303 var viewWidth = this._api.getWidth(); 71304 var viewHeight = this._api.getHeight(); 71305 positionExpr = positionExpr || tooltipModel.get('position'); 71306 71307 var contentSize = content.getSize(); 71308 var align = tooltipModel.get('align'); 71309 var vAlign = tooltipModel.get('verticalAlign');
71310 var rect = el && el.getBoundingRect().clone(); 71311 el && rect.applyTransform(el.transform); 71312 71313 if (typeof positionExpr === 'function') { 71314 // Callback of position can be an array or a string specify the position 71315 positionExpr = positionExpr([x, y], params, content.el, rect, { 71316 viewSize: [viewWidth, viewHeight], 71317 contentSize: contentSize.slice() 71318 }); 71319 } 71320 71321 if (isArray(positionExpr)) { 71322 x = parsePercent$2(positionExpr[0], viewWidth); 71323 y = parsePercent$2(positionExpr[1], viewHeight); 71324 } 71325 else if (isObject$1(positionExpr)) { 71326 positionExpr.width = contentSize[0]; 71327 positionExpr.height = contentSize[1]; 71328 var layoutRect = getLayoutRect( 71329 positionExpr, {width: viewWidth, height: viewHeight} 71330 ); 71331 x = layoutRect.x; 71332 y = layoutRect.y; 71333 align = null; 71334 // When positionExpr is left/top/right/bottom, 71335 // align and verticalAlign will not work. 71336 vAlign = null; 71337 } 71338 // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element 71339 else if (typeof positionExpr === 'string' && el) { 71340 var pos = calcTooltipPosition( 71341 positionExpr, rect, contentSize 71342 ); 71343 x = pos[0]; 71344 y = pos[1]; 71345 } 71346 else { 71347 var pos = refixTooltipPosition( 71348 x, y, content.el, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20 71349 ); 71350 x = pos[0]; 71351 y = pos[1]; 71352 } 71353 71354 align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0); 71355 vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0); 71356 71357 if (tooltipModel.get('confine')) { 71358 var pos = confineTooltipPosition( 71359 x, y, content.el, viewWidth, viewHeight 71360 ); 71361 x = pos[0]; 71362 y = pos[1]; 71363 } 71364 71365 content.moveTo(x, y); 71366 }, 71367 71368 // FIXME 71369 // Should we remove this but leave this to user? 71370 _updateContentNotChangedOnAxis: function (dataByCoordSys) { 71371 var lastCoordSys = this._lastDataByCoordSys; 71372 var contentNotChanged = !!lastCoordSys 71373 && lastCoordSys.length === dataByCoordSys.length; 71374 71375 contentNotChanged && each$17(lastCoordSys, function (lastItemCoordSys, indexCoordSys) { 71376 var lastDataByAxis = lastItemCoordSys.dataByAxis || {}; 71377 var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {}; 71378 var thisDataByAxis = thisItemCoordSys.dataByAxis || []; 71379 contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length; 71380 71381 contentNotChanged && each$17(lastDataByAxis, function (lastItem, indexAxis) { 71382 var thisItem = thisDataByAxis[indexAxis] || {}; 71383 var lastIndices = lastItem.seriesDataIndices || []; 71384 var newIndices = thisItem.seriesDataIndices || []; 71385 71386 contentNotChanged &= 71387 lastItem.value === thisItem.value 71388 && lastItem.axisType === thisItem.axisType 71389 && lastItem.axisId === thisItem.axisId 71390 && lastIndices.length === newIndices.length; 71391 71392 contentNotChanged && each$17(lastIndices, function (lastIdxItem, j) { 71393 var newIdxItem = newIndices[j]; 71394 contentNotChanged &= 71395 lastIdxItem.seriesIndex === newIdxItem.seriesIndex 71396 && lastIdxItem.dataIndex === newIdxItem.dataIndex; 71397 }); 71398 }); 71399 }); 71400 71401 this._lastDataByCoordSys = dataByCoordSys; 71402 71403 return !!contentNotChanged; 71404 }, 71405 71406 _hide: function (dispatchAction) {
71407 // Do not directly hideLater here, because this behavior may be prevented 71408 // in dispatchAction when showTip is dispatched. 71409 71410 // FIXME 71411 // duplicated hideTip if manuallyHideTip is called from dispatchAction. 71412 this._lastDataByCoordSys = null; 71413 dispatchAction({ 71414 type: 'hideTip', 71415 from: this.uid 71416 }); 71417 }, 71418 71419 dispose: function (ecModel, api) { 71420 if (env$1.node || env$1.wxa) { 71421 return; 71422 } 71423 this._tooltipContent.hide(); 71424 unregister('itemTooltip', api); 71425 } 71426 }); 71427 71428 71429 /** 71430 * @param {Array.<Object|module:echarts/model/Model>} modelCascade 71431 * From top to bottom. (the last one should be globalTooltipModel); 71432 */ 71433 function buildTooltipModel(modelCascade) { 71434 var resultModel = modelCascade.pop(); 71435 while (modelCascade.length) { 71436 var tooltipOpt = modelCascade.pop(); 71437 if (tooltipOpt) { 71438 if (Model.isInstance(tooltipOpt)) { 71439 tooltipOpt = tooltipOpt.get('tooltip', true); 71440 } 71441 // In each data item tooltip can be simply write: 71442 // { 71443 // value: 10, 71444 // tooltip: 'Something you need to know' 71445 // } 71446 if (typeof tooltipOpt === 'string') { 71447 tooltipOpt = {formatter: tooltipOpt}; 71448 } 71449 resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel); 71450 } 71451 } 71452 return resultModel; 71453 } 71454 71455 function makeDispatchAction$1(payload, api) { 71456 return payload.dispatchAction || bind(api.dispatchAction, api); 71457 } 71458 71459 function refixTooltipPosition(x, y, el, viewWidth, viewHeight, gapH, gapV) { 71460 var size = getOuterSize(el); 71461 var width = size.width; 71462 var height = size.height; 71463 71464 if (gapH != null) { 71465 if (x + width + gapH > viewWidth) { 71466 x -= width + gapH; 71467 } 71468 else { 71469 x += gapH; 71470 } 71471 } 71472 if (gapV != null) { 71473 if (y + height + gapV > viewHeight) { 71474 y -= height + gapV; 71475 } 71476 else { 71477 y += gapV; 71478 } 71479 } 71480 return [x, y]; 71481 } 71482 71483 function confineTooltipPosition(x, y, el, viewWidth, viewHeight) { 71484 var size = getOuterSize(el); 71485 var width = size.width; 71486 var height = size.height; 71487 71488 x = Math.min(x + width, viewWidth) - width; 71489 y = Math.min(y + height, viewHeight) - height; 71490 x = Math.max(x, 0); 71491 y = Math.max(y, 0); 71492 71493 return [x, y]; 71494 } 71495 71496 function getOuterSize(el) { 71497 var width = el.clientWidth; 71498 var height = el.clientHeight; 71499 71500 // Consider browser compatibility. 71501 // IE8 does not support getComputedStyle. 71502 if (document.defaultView && document.defaultView.getComputedStyle) { 71503 var stl = document.defaultView.getComputedStyle(el); 71504 if (stl) { 71505 width += parseInt(stl.paddingLeft, 10) + parseInt(stl.paddingRight, 10) 71506 + parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10); 71507 height += parseInt(stl.paddingTop, 10) + parseInt(stl.paddingBottom, 10) 71508 + parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10); 71509 } 71510 } 71511 71512 return {width: width, height: height}; 71513 } 71514 71515 function calcTooltipPosition(position, rect, contentSize) { 71516 var domWidth = contentSize[0]; 71517 var domHeight = contentSize[1]; 71518 var gap = 5; 71519 var x = 0; 71520 var y = 0; 71521 var rectWidth = rect.width; 71522 var rectHeight = rect.height; 71523 switch (position) { 71524 case 'inside': 71525 x = rect.x + rectWidth / 2 - domWidth / 2; 71526 y = rect.y + rectHeight / 2 - domHeight / 2; 71527 break; 71528 case 'top': 71529 x = rect.x + rectWidth / 2 - domWidth / 2;
71530 y = rect.y - domHeight - gap; 71531 break; 71532 case 'bottom': 71533 x = rect.x + rectWidth / 2 - domWidth / 2; 71534 y = rect.y + rectHeight + gap; 71535 break; 71536 case 'left': 71537 x = rect.x - domWidth - gap; 71538 y = rect.y + rectHeight / 2 - domHeight / 2; 71539 break; 71540 case 'right': 71541 x = rect.x + rectWidth + gap; 71542 y = rect.y + rectHeight / 2 - domHeight / 2; 71543 } 71544 return [x, y]; 71545 } 71546 71547 function isCenterAlign(align) { 71548 return align === 'center' || align === 'middle'; 71549 } 71550 71551 /* 71552* Licensed to the Apache Software Foundation (ASF) under one 71553* or more contributor license agreements. See the NOTICE file 71554* distributed with this work for additional information 71555* regarding copyright ownership. The ASF licenses this file 71556* to you under the Apache License, Version 2.0 (the 71557* "License"); you may not use this file except in compliance 71558* with the License. You may obtain a copy of the License at 71559* 71560* http://www.apache.org/licenses/LICENSE-2.0 71561* 71562* Unless required by applicable law or agreed to in writing, 71563* software distributed under the License is distributed on an 71564* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 71565* KIND, either express or implied. See the License for the 71566* specific language governing permissions and limitations 71567* under the License. 71568*/ 71569 71570// FIXME Better way to pack data in graphic element 71571 71572 /** 71573 * @action 71574 * @property {string} type 71575 * @property {number} seriesIndex 71576 * @property {number} dataIndex 71577 * @property {number} [x] 71578 * @property {number} [y] 71579 */ 71580 registerAction( 71581 { 71582 type: 'showTip', 71583 event: 'showTip', 71584 update: 'tooltip:manuallyShowTip' 71585 }, 71586 // noop 71587 function () {} 71588 ); 71589 71590 registerAction( 71591 { 71592 type: 'hideTip', 71593 event: 'hideTip', 71594 update: 'tooltip:manuallyHideTip' 71595 }, 71596 // noop 71597 function () {} 71598 ); 71599 71600 /* 71601* Licensed to the Apache Software Foundation (ASF) under one 71602* or more contributor license agreements. See the NOTICE file 71603* distributed with this work for additional information 71604* regarding copyright ownership. The ASF licenses this file 71605* to you under the Apache License, Version 2.0 (the 71606* "License"); you may not use this file except in compliance 71607* with the License. You may obtain a copy of the License at 71608* 71609* http://www.apache.org/licenses/LICENSE-2.0 71610* 71611* Unless required by applicable law or agreed to in writing, 71612* software distributed under the License is distributed on an 71613* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 71614* KIND, either express or implied. See the License for the 71615* specific language governing permissions and limitations 71616* under the License. 71617*/ 71618 71619 function getSeriesStackId$1(seriesModel) { 71620 return seriesModel.get('stack') 71621 || '__ec_stack_' + seriesModel.seriesIndex; 71622 } 71623 71624 function getAxisKey$1(axis) { 71625 return axis.dim; 71626 } 71627 71628 /** 71629 * @param {string} seriesType 71630 * @param {module:echarts/model/Global} ecModel 71631 * @param {module:echarts/ExtensionAPI} api 71632 */ 71633 function barLayoutPolar(seriesType, ecModel, api) { 71634 71635 var width = api.getWidth(); 71636 var height = api.getHeight(); 71637 71638 var lastStackCoords = {}; 71639 71640 var barWidthAndOffset = calRadialBar( 71641 filter( 71642 ecModel.getSeriesByType(seriesType), 71643 function (seriesModel) { 71644 return !ecModel.isSeriesFiltered(seriesModel) 71645 && seriesModel.coordinateSystem 71646 && seriesModel.coordinateSystem.type === 'polar'; 71647 } 71648 ) 71649 ); 71650 71651 ecModel.eachSeriesByType(seriesType, function (seriesModel) { 71652 // Check series coordinate, do layout for polar only 71653 if (seriesModel.coordinateSystem.type !== 'polar') { 71654 return; 71655 } 71656 71657 var data = seriesModel.getData(); 71658 var polar = seriesModel.coordinateSystem; 71659 var baseAxis = polar.getBaseAxis(); 71660
71661 var stackId = getSeriesStackId$1(seriesModel); 71662 var columnLayoutInfo 71663 = barWidthAndOffset[getAxisKey$1(baseAxis)][stackId]; 71664 var columnOffset = columnLayoutInfo.offset; 71665 var columnWidth = columnLayoutInfo.width; 71666 var valueAxis = polar.getOtherAxis(baseAxis); 71667 71668 var center = seriesModel.get('center') || ['50%', '50%']; 71669 var cx = parsePercent$1(center[0], width); 71670 var cy = parsePercent$1(center[1], height); 71671 71672 var barMinHeight = seriesModel.get('barMinHeight') || 0; 71673 var barMinAngle = seriesModel.get('barMinAngle') || 0; 71674 71675 lastStackCoords[stackId] = lastStackCoords[stackId] || []; 71676 71677 var valueDim = data.mapDimension(valueAxis.dim); 71678 var baseDim = data.mapDimension(baseAxis.dim); 71679 var stacked = isDimensionStacked(data, valueDim /*, baseDim*/); 71680 71681 var valueAxisStart = valueAxis.getExtent()[0]; 71682 71683 for (var idx = 0, len = data.count(); idx < len; idx++) { 71684 var value = data.get(valueDim, idx); 71685 var baseValue = data.get(baseDim, idx); 71686 71687 if (isNaN(value)) { 71688 continue; 71689 } 71690 71691 var sign = value >= 0 ? 'p' : 'n'; 71692 var baseCoord = valueAxisStart; 71693 71694 // Because of the barMinHeight, we can not use the value in 71695 // stackResultDimension directly. 71696 // Only ordinal axis can be stacked. 71697 if (stacked) { 71698 if (!lastStackCoords[stackId][baseValue]) { 71699 lastStackCoords[stackId][baseValue] = { 71700 p: valueAxisStart, // Positive stack 71701 n: valueAxisStart // Negative stack 71702 }; 71703 } 71704 // Should also consider #4243 71705 baseCoord = lastStackCoords[stackId][baseValue][sign]; 71706 } 71707 71708 var r0; 71709 var r; 71710 var startAngle; 71711 var endAngle; 71712 71713 // radial sector 71714 if (valueAxis.dim === 'radius') { 71715 var radiusSpan = valueAxis.dataToRadius(value) - valueAxisStart; 71716 var angle = baseAxis.dataToAngle(baseValue); 71717 71718 if (Math.abs(radiusSpan) < barMinHeight) { 71719 radiusSpan = (radiusSpan < 0 ? -1 : 1) * barMinHeight; 71720 } 71721 71722 r0 = baseCoord;
71723 r = baseCoord + radiusSpan; 71724 startAngle = angle - columnOffset; 71725 endAngle = startAngle - columnWidth; 71726 71727 stacked && (lastStackCoords[stackId][baseValue][sign] = r); 71728 } 71729 // tangential sector 71730 else { 71731 // angleAxis must be clamped. 71732 var angleSpan = valueAxis.dataToAngle(value, true) - valueAxisStart; 71733 var radius = baseAxis.dataToRadius(baseValue); 71734 71735 if (Math.abs(angleSpan) < barMinAngle) { 71736 angleSpan = (angleSpan < 0 ? -1 : 1) * barMinAngle; 71737 } 71738 71739 r0 = radius + columnOffset; 71740 r = r0 + columnWidth; 71741 startAngle = baseCoord; 71742 endAngle = baseCoord + angleSpan; 71743 71744 // if the previous stack is at the end of the ring, 71745 // add a round to differentiate it from origin 71746 // var extent = angleAxis.getExtent(); 71747 // var stackCoord = angle; 71748 // if (stackCoord === extent[0] && value > 0) { 71749 // stackCoord = extent[1]; 71750 // } 71751 // else if (stackCoord === extent[1] && value < 0) { 71752 // stackCoord = extent[0]; 71753 // } 71754 stacked && (lastStackCoords[stackId][baseValue][sign] = endAngle); 71755 } 71756 71757 data.setItemLayout(idx, { 71758 cx: cx, 71759 cy: cy, 71760 r0: r0, 71761 r: r, 71762 // Consider that positive angle is anti-clockwise, 71763 // while positive radian of sector is clockwise 71764 startAngle: -startAngle * Math.PI / 180, 71765 endAngle: -endAngle * Math.PI / 180 71766 }); 71767 71768 } 71769 71770 }, this); 71771 71772 } 71773 71774 /** 71775 * Calculate bar width and offset for radial bar charts 71776 */ 71777 function calRadialBar(barSeries, api) { 71778 // Columns info on each category axis. Key is polar name 71779 var columnsMap = {}; 71780 71781 each$1(barSeries, function (seriesModel, idx) { 71782 var data = seriesModel.getData(); 71783 var polar = seriesModel.coordinateSystem; 71784 71785 var baseAxis = polar.getBaseAxis(); 71786 71787 var axisExtent = baseAxis.getExtent(); 71788 var bandWidth = baseAxis.type === 'category' 71789 ? baseAxis.getBandWidth() 71790 : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count()); 71791 71792 var columnsOnAxis = columnsMap[getAxisKey$1(baseAxis)] || { 71793 bandWidth: bandWidth, 71794 remainedWidth: bandWidth, 71795 autoWidthCount: 0, 71796 categoryGap: '20%', 71797 gap: '30%', 71798 stacks: {} 71799 }; 71800 var stacks = columnsOnAxis.stacks; 71801 columnsMap[getAxisKey$1(baseAxis)] = columnsOnAxis; 71802 71803 var stackId = getSeriesStackId$1(seriesModel); 71804 71805 if (!stacks[stackId]) { 71806 columnsOnAxis.autoWidthCount++; 71807 } 71808 stacks[stackId] = stacks[stackId] || { 71809 width: 0, 71810 maxWidth: 0 71811 }; 71812 71813 var barWidth = parsePercent$1( 71814 seriesModel.get('barWidth'), 71815 bandWidth 71816 ); 71817 var barMaxWidth = parsePercent$1( 71818 seriesModel.get('barMaxWidth'), 71819 bandWidth 71820 ); 71821 var barGap = seriesModel.get('barGap'); 71822 var barCategoryGap = seriesModel.get('barCategoryGap'); 71823 71824 if (barWidth && !stacks[stackId].width) { 71825 barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); 71826 stacks[stackId].width = barWidth; 71827 columnsOnAxis.remainedWidth -= barWidth; 71828 } 71829 71830 barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); 71831 (barGap != null) && (columnsOnAxis.gap = barGap); 71832 (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap); 71833 }); 71834 71835 71836 var result = {}; 71837 71838 each$1(columnsMap, function (columnsOnAxis, coordSysName) { 71839 71840 result[coordSysName] = {}; 71841 71842 var stacks = columnsOnAxis.stacks; 71843 var bandWidth = columnsOnAxis.bandWidth; 71844 var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth); 71845 var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1); 71846 71847 var remainedWidth = columnsOnAxis.remainedWidth; 71848 var autoWidthCount = columnsOnAxis.autoWidthCount; 71849 var autoWidth = (remainedWidth - categoryGap) 71850 / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); 71851 autoWidth = Math.max(autoWidth, 0); 71852 71853 // Find if any auto calculated bar exceeded maxBarWidth 71854 each$1(stacks, function (column, stack) { 71855 var maxWidth = column.maxWidth; 71856 if (maxWidth && maxWidth < autoWidth) { 71857 maxWidth = Math.min(maxWidth, remainedWidth); 71858 if (column.width) { 71859 maxWidth = Math.min(maxWidth, column.width); 71860 } 71861 remainedWidth -= maxWidth; 71862 column.width = maxWidth; 71863 autoWidthCount--; 71864 } 71865 }); 71866 71867 // Recalculate width again 71868 autoWidth = (remainedWidth - categoryGap) 71869 / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); 71870 autoWidth = Math.max(autoWidth, 0); 71871 71872 var widthSum = 0; 71873 var lastColumn; 71874 each$1(stacks, function (column, idx) { 71875 if (!column.width) { 71876 column.width = autoWidth; 71877 } 71878 lastColumn = column; 71879 widthSum += column.width * (1 + barGapPercent); 71880 }); 71881 if (lastColumn) { 71882 widthSum -= lastColumn.width * barGapPercent; 71883 } 71884 71885 var offset = -widthSum / 2; 71886 each$1(stacks, function (column, stackId) { 71887 result[coordSysName][stackId] = result[coordSysName][stackId] || { 71888 offset: offset, 71889 width: column.width 71890 }; 71891 71892 offset += column.width * (1 + barGapPercent); 71893 }); 71894 }); 71895 71896 return result; 71897 } 71898 71899 /* 71900* Licensed to the Apache Software Foundation (ASF) under one 71901* or more contributor license agreements. See the NOTICE file 71902* distributed with this work for additional information 71903* regarding copyright ownership. The ASF licenses this file 71904* to you under the Apache License, Version 2.0 (the 71905* "License");
71905 you may not use this file except in compliance 71906* with the License. You may obtain a copy of the License at 71907* 71908* http://www.apache.org/licenses/LICENSE-2.0 71909* 71910* Unless required by applicable law or agreed to in writing, 71911* software distributed under the License is distributed on an 71912* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 71913* KIND, either express or implied. See the License for the 71914* specific language governing permissions and limitations 71915* under the License. 71916*/ 71917 71918 function RadiusAxis(scale, radiusExtent) { 71919 71920 Axis.call(this, 'radius', scale, radiusExtent); 71921 71922 /** 71923 * Axis type 71924 * - 'category' 71925 * - 'value' 71926 * - 'time' 71927 * - 'log' 71928 * @type {string} 71929 */ 71930 this.type = 'category'; 71931 } 71932 71933 RadiusAxis.prototype = { 71934 71935 constructor: RadiusAxis, 71936 71937 /** 71938 * @override 71939 */ 71940 pointToData: function (point, clamp) { 71941 return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1]; 71942 }, 71943 71944 dataToRadius: Axis.prototype.dataToCoord, 71945 71946 radiusToData: Axis.prototype.coordToData 71947 }; 71948 71949 inherits(RadiusAxis, Axis); 71950 71951 /* 71952* Licensed to the Apache Software Foundation (ASF) under one 71953* or more contributor license agreements. See the NOTICE file 71954* distributed with this work for additional information 71955* regarding copyright ownership. The ASF licenses this file 71956* to you under the Apache License, Version 2.0 (the 71957* "License"); you may not use this file except in compliance 71958* with the License. You may obtain a copy of the License at 71959* 71960* http://www.apache.org/licenses/LICENSE-2.0 71961* 71962* Unless required by applicable law or agreed to in writing, 71963* software distributed under the License is distributed on an 71964* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 71965* KIND, either express or implied. See the License for the 71966* specific language governing permissions and limitations 71967* under the License. 71968*/ 71969 71970 function AngleAxis(scale, angleExtent) { 71971 71972 angleExtent = angleExtent || [0, 360]; 71973 71974 Axis.call(this, 'angle', scale, angleExtent); 71975 71976 /** 71977 * Axis type 71978 * - 'category' 71979 * - 'value' 71980 * - 'time' 71981 * - 'log' 71982 * @type {string} 71983 */ 71984 this.type = 'category'; 71985 } 71986 71987 AngleAxis.prototype = { 71988 71989 constructor: AngleAxis, 71990 71991 /** 71992 * @override 71993 */ 71994 pointToData: function (point, clamp) { 71995 return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1]; 71996 }, 71997 71998 dataToAngle: Axis.prototype.dataToCoord, 71999 72000 angleToData: Axis.prototype.coordToData 72001 }; 72002 72003 inherits(AngleAxis, Axis); 72004 72005 /* 72006* Licensed to the Apache Software Foundation (ASF) under one 72007* or more contributor license agreements. See the NOTICE file 72008* distributed with this work for additional information 72009* regarding copyright ownership. The ASF licenses this file 72010* to you under the Apache License, Version 2.0 (the 72011* "License"); you may not use this file except in compliance 72012* with the License. You may obtain a copy of the License at 72013* 72014* http://www.apache.org/licenses/LICENSE-2.0 72015* 72016* Unless required by applicable law or agreed to in writing, 72017* software distributed under the License is distributed on an 72018* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72019* KIND, either express or implied. See the License for the 72020* specific language governing permissions and limitations 72021* under the License. 72022*/ 72023 72024 /** 72025 * @module echarts/coord/polar/Polar 72026 */ 72027 72028 /** 72029 * @alias {module:echarts/coord/polar/Polar} 72030 * @constructor 72031 * @param {string} name 72032 */ 72033 var Polar = function (name) { 72034 72035 /** 72036 * @type {string} 72037 */ 72038 this.name = name || ''; 72039 72040 /** 72041 * x of polar center 72042 * @type {number} 72043 */ 72044 this.cx = 0; 72045 72046 /** 72047 * y of polar center 72048 * @type {number} 72049 */ 72050 this.cy = 0; 72051 72052 /** 72053 * @type {module:echarts/coord/polar/RadiusAxis} 72054 * @private 72055 */ 72056 this._radiusAxis = new RadiusAxis(); 72057 72058 /** 72059 * @type {module:echarts/coord/polar/AngleAxis} 72060 * @private 72061 */ 72062 this._angleAxis = new AngleAxis(); 72063 72064 this._radiusAxis.polar = this._angleAxis.polar = this; 72065 }; 72066 72067 Polar.prototype = { 72068 72069 type: 'polar', 72070 72071 axisPointerEnabled: true, 72072 72073 constructor: Polar, 72074 72075 /** 72076 * @param {Array.<string>} 72077 * @readOnly 72078 */ 72079 dimensions: ['radius', 'angle'], 72080 72081 /** 72082 * @type {module:echarts/coord/PolarModel} 72083 */ 72084 model: null, 72085 72086 /** 72087 * If contain coord 72088 * @param {Array.<number>} point 72089 * @return {boolean} 72090 */ 72091 containPoint: function (point) { 72092 var coord = this.pointToCoord(point); 72093 return this._radiusAxis.contain(coord[0]) 72094 && this._angleAxis.contain(coord[1]); 72095 }, 72096 72097 /** 72098 * If contain data 72099 * @param {Array.<number>} data 72100 * @return {boolean} 72101 */ 72102 containData: function (data) { 72103 return this._radiusAxis.containData(data[0]) 72104 && this._angleAxis.containData(data[1]); 72105 }, 72106 72107 /** 72108 * @param {string} dim 72109 * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} 72110 */ 72111 getAxis: function (dim) { 72112 return this['_' + dim + 'Axis']; 72113 }, 72114 72115 /** 72116 * @return {Array.<module:echarts/coord/Axis>} 72117 */ 72118 getAxes: function () { 72119 return [this._radiusAxis, this._angleAxis]; 72120 }, 72121 72122 /** 72123 * Get axes by type of scale 72124 * @param {string} scaleType 72125 * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} 72126 */ 72127 getAxesByScale: function (scaleType) { 72128 var axes = []; 72129 var angleAxis = this._angleAxis; 72130 var radiusAxis = this._radiusAxis; 72131 angleAxis.scale.type === scaleType && axes.push(angleAxis); 72132 radiusAxis.scale.type === scaleType && axes.push(radiusAxis); 72133 72134 return axes; 72135 }, 72136 72137 /** 72138 * @return {module:echarts/coord/polar/AngleAxis} 72139 */ 72140 getAngleAxis: function () { 72141 return this._angleAxis; 72142 }, 72143 72144 /** 72145 * @return {module:echarts/coord/polar/RadiusAxis} 72146 */ 72147 getRadiusAxis: function () { 72148 return this._radiusAxis; 72149 }, 72150 72151 /** 72152 * @param {module:echarts/coord/polar/Axis} 72153 * @return {module:echarts/coord/polar/Axis} 72154 */ 72155 getOtherAxis: function (axis) { 72156 var angleAxis = this._angleAxis; 72157 return axis === angleAxis ? this._radiusAxis : angleAxis; 72158 }, 72159 72160 /** 72161 * Base axis will be used on stacking. 72162 * 72163 * @return {module:echarts/coord/polar/Axis} 72164 */ 72165 getBaseAxis: function () { 72166 return this.getAxesByScale('ordinal')[0] 72167 || this.getAxesByScale('time')[0] 72168 || this.getAngleAxis(); 72169 }, 72170 72171 /** 72172 * @param {string} [dim] 'radius' or 'angle' or 'auto' or null/undefined 72173 * @return {Object} {baseAxes: [], otherAxes: []} 72174 */ 72175 getTooltipAxes: function (dim) { 72176 var baseAxis = (dim != null && dim !== 'auto') 72177 ? this.getAxis(dim) : this.getBaseAxis(); 72178 return { 72179 baseAxes: [baseAxis], 72180 otherAxes: [this.getOtherAxis(baseAxis)] 72181 }; 72182 }, 72183 72184 /** 72185 * Convert a single data item to (x, y) point. 72186 * Parameter data is an array which the first element is radius and the second is angle 72187 * @param {Array.<number>} data 72188 * @param {boolean} [clamp=false] 72189 * @return {Array.<number>} 72190 */ 72191 dataToPoint: function (data, clamp) { 72192 return this.coordToPoint([ 72193 this._radiusAxis.dataToRadius(data[0], clamp), 72194 this._angleAxis.dataToAngle(data[1], clamp) 72195 ]); 72196 }, 72197 72198 /** 72199 * Convert a (x, y) point to data 72200 * @param {Array.<number>} point 72201 * @param {boolean} [clamp=false] 72202 * @return {Array.<number>} 72203 */ 72204 pointToData: function (point, clamp) { 72205 var coord = this.pointToCoord(point); 72206 return [ 72207 this._radiusAxis.radiusToData(coord[0], clamp), 72208 this._angleAxis.angleToData(coord[1], clamp) 72209 ]; 72210 }, 72211 72212 /** 72213 * Convert a (x, y) point to (radius, angle) coord 72214 * @param {Array.<number>} point 72215 * @return {Array.<number>} 72216 */ 72217 pointToCoord: function (point) { 72218 var dx = point[0] - this.cx; 72219 var dy = point[1] - this.cy; 72220 var angleAxis = this.getAngleAxis(); 72221 var extent = angleAxis.getExtent(); 72222 var minAngle = Math.min(extent[0], extent[1]); 72223 var maxAngle = Math.max(extent[0], extent[1]); 72224 // Fix fixed extent in polarCreator 72225 // FIXME 72226 angleAxis.inverse 72227 ? (minAngle = maxAngle - 360) 72228 : (maxAngle = minAngle + 360); 72229 72230 var radius = Math.sqrt(dx * dx + dy * dy); 72231 dx /= radius; 72232 dy /= radius; 72233 72234 var radian = Math.atan2(-dy, dx) / Math.PI * 180; 72235 72236 // move to angleExtent 72237 var dir = radian < minAngle ? 1 : -1; 72238 while (radian < minAngle || radian > maxAngle) { 72239 radian += dir * 360; 72240 } 72241 72242 return [radius, radian]; 72243 }, 72244 72245 /** 72246 * Convert a (radius, angle) coord to (x, y) point 72247 * @param {Array.<number>} coord 72248 * @return {Array.<number>} 72249 */ 72250 coordToPoint: function (coord) { 72251 var radius = coord[0]; 72252 var radian = coord[1] / 180 * Math.PI; 72253 var x = Math.cos(radian) * radius + this.cx; 72254 // Inverse the y 72255 var y = -Math.sin(radian) * radius + this.cy; 72256 72257 return [x, y]; 72258 } 72259 72260 }; 72261 72262 /* 72263* Licensed to the Apache Software Foundation (ASF) under one 72264* or more contributor license agreements. See the NOTICE file 72265* distributed with this work for additional information 72266* regarding copyright ownership. The ASF licenses this file 72267* to you under the Apache License, Version 2.0 (the 72268* "License");
72268 you may not use this file except in compliance 72269* with the License. You may obtain a copy of the License at 72270* 72271* http://www.apache.org/licenses/LICENSE-2.0 72272* 72273* Unless required by applicable law or agreed to in writing, 72274* software distributed under the License is distributed on an 72275* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72276* KIND, either express or implied. See the License for the 72277* specific language governing permissions and limitations 72278* under the License. 72279*/ 72280 72281 var PolarAxisModel = ComponentModel.extend({ 72282 72283 type: 'polarAxis', 72284 72285 /** 72286 * @type {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} 72287 */ 72288 axis: null, 72289 72290 /** 72291 * @override 72292 */ 72293 getCoordSysModel: function () { 72294 return this.ecModel.queryComponents({ 72295 mainType: 'polar', 72296 index: this.option.polarIndex, 72297 id: this.option.polarId 72298 })[0]; 72299 } 72300 72301 }); 72302 72303 merge(PolarAxisModel.prototype, axisModelCommonMixin); 72304 72305 var polarAxisDefaultExtendedOption = { 72306 angle: { 72307 // polarIndex: 0, 72308 // polarId: '', 72309 72310 startAngle: 90, 72311 72312 clockwise: true, 72313 72314 splitNumber: 12, 72315 72316 axisLabel: { 72317 rotate: false 72318 } 72319 }, 72320 radius: { 72321 // polarIndex: 0, 72322 // polarId: '', 72323 72324 splitNumber: 5 72325 } 72326 }; 72327 72328 function getAxisType$3(axisDim, option) { 72329 // Default axis with data is category axis 72330 return option.type || (option.data ? 'category' : 'value'); 72331 } 72332 72333 axisModelCreator('angle', PolarAxisModel, getAxisType$3, polarAxisDefaultExtendedOption.angle); 72334 axisModelCreator('radius', PolarAxisModel, getAxisType$3, polarAxisDefaultExtendedOption.radius); 72335 72336 /* 72337* Licensed to the Apache Software Foundation (ASF) under one 72338* or more contributor license agreements. See the NOTICE file 72339* distributed with this work for additional information 72340* regarding copyright ownership. The ASF licenses this file 72341* to you under the Apache License, Version 2.0 (the 72342* "License"); you may not use this file except in compliance 72343* with the License. You may obtain a copy of the License at 72344* 72345* http://www.apache.org/licenses/LICENSE-2.0 72346* 72347* Unless required by applicable law or agreed to in writing, 72348* software distributed under the License is distributed on an 72349* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72350* KIND, either express or implied. See the License for the 72351* specific language governing permissions and limitations 72352* under the License. 72353*/ 72354 72355 extendComponentModel({ 72356 72357 type: 'polar', 72358 72359 dependencies: ['polarAxis', 'angleAxis'], 72360 72361 /** 72362 * @type {module:echarts/coord/polar/Polar} 72363 */ 72364 coordinateSystem: null, 72365 72366 /** 72367 * @param {string} axisType 72368 * @return {module:echarts/coord/polar/AxisModel} 72369 */ 72370 findAxisModel: function (axisType) { 72371 var foundAxisModel; 72372 var ecModel = this.ecModel; 72373 72374 ecModel.eachComponent(axisType, function (axisModel) { 72375 if (axisModel.getCoordSysModel() === this) { 72376 foundAxisModel = axisModel; 72377 } 72378 }, this); 72379 return foundAxisModel; 72380 }, 72381 72382 defaultOption: { 72383 72384 zlevel: 0, 72385 72386 z: 0, 72387 72388 center: ['50%', '50%'], 72389 72390 radius: '80%' 72391 } 72392 }); 72393 72394 /* 72395* Licensed to the Apache Software Foundation (ASF) under one 72396* or more contributor license agreements. See the NOTICE file 72397* distributed with this work for additional information 72398* regarding copyright ownership. The ASF licenses this file 72399* to you under the Apache License, Version 2.0 (the 72400* "License"); you may not use this file except in compliance 72401* with the License. You may obtain a copy of the License at 72402* 72403* http://www.apache.org/licenses/LICENSE-2.0 72404* 72405* Unless required by applicable law or agreed to in writing, 72406* software distributed under the License is distributed on an 72407* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72408* KIND, either express or implied. See the License for the 72409* specific language governing permissions and limitations 72410* under the License. 72411*/ 72412 72413// TODO Axis scale 72414 72415 /** 72416 * Resize method bound to the polar 72417 * @param {module:echarts/coord/polar/PolarModel} polarModel 72418 * @param {module:echarts/ExtensionAPI} api 72419 */ 72420 function resizePolar(polar, polarModel, api) { 72421 var center = polarModel.get('center'); 72422 var width = api.getWidth(); 72423 var height = api.getHeight(); 72424 72425 polar.cx = parsePercent$1(center[0], width); 72426 polar.cy = parsePercent$1(center[1], height); 72427 72428 var radiusAxis = polar.getRadiusAxis(); 72429 var size = Math.min(width, height) / 2; 72430 var radius = parsePercent$1(polarModel.get('radius'), size); 72431 radiusAxis.inverse 72432 ? radiusAxis.setExtent(radius, 0) 72433 : radiusAxis.setExtent(0, radius); 72434 } 72435 72436 /** 72437 * Update polar 72438 */ 72439 function updatePolarScale(ecModel, api) { 72440 var polar = this; 72441 var angleAxis = polar.getAngleAxis(); 72442 var radiusAxis = polar.getRadiusAxis(); 72443 // Reset scale 72444 angleAxis.scale.setExtent(Infinity, -Infinity); 72445 radiusAxis.scale.setExtent(Infinity, -Infinity); 72446 72447 ecModel.eachSeries(function (seriesModel) { 72448 if (seriesModel.coordinateSystem === polar) { 72449 var data = seriesModel.getData(); 72450 each$1(data.mapDimension('radius', true), function (dim) { 72451 radiusAxis.scale.unionExtentFromData( 72452 data, getStackedDimension(data, dim) 72453 ); 72454 }); 72455 each$1(data.mapDimension('angle', true), function (dim) { 72456 angleAxis.scale.unionExtentFromData( 72457 data, getStackedDimension(data, dim) 72458 ); 72459 }); 72460 } 72461 }); 72462 72463 niceScaleExtent(angleAxis.scale, angleAxis.model); 72464 niceScaleExtent(radiusAxis.scale, radiusAxis.model); 72465 72466 // Fix extent of category angle axis 72467 if (angleAxis.type === 'category' && !angleAxis.onBand) { 72468 var extent = angleAxis.getExtent(); 72469 var diff = 360 / angleAxis.scale.count(); 72470 angleAxis.inverse ? (extent[1] += diff) : (extent[1] -= diff); 72471 angleAxis.setExtent(extent[0], extent[1]); 72472 } 72473 } 72474 72475 /** 72476 * Set common axis properties 72477 * @param {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} 72478 * @param {module:echarts/coord/polar/AxisModel} 72479 * @inner 72480 */ 72481 function setAxis(axis, axisModel) { 72482 axis.type = axisModel.get('type'); 72483 axis.scale = createScaleByModel(axisModel); 72484 axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category'; 72485 axis.inverse = axisModel.get('inverse'); 72486 72487 if (axisModel.mainType === 'angleAxis') { 72488 axis.inverse ^= axisModel.get('clockwise'); 72489 var startAngle = axisModel.get('startAngle'); 72490 axis.setExtent(startAngle, startAngle + (axis.inverse ? -360 : 360)); 72491 } 72492 72493 // Inject axis instance 72494 axisModel.axis = axis; 72495 axis.model = axisModel; 72496 } 72497 72498 72499 var polarCreator = { 72500 72501 dimensions: Polar.prototype.dimensions, 72502 72503 create: function (ecModel, api) { 72504 var polarList = []; 72505 ecModel.eachComponent('polar', function (polarModel, idx) { 72506 var polar = new Polar(idx); 72507 // Inject resize and update method 72508 polar.update = updatePolarScale; 72509 72510 var radiusAxis = polar.getRadiusAxis(); 72511 var angleAxis = polar.getAngleAxis(); 72512 72513 var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); 72514 var angleAxisModel = polarModel.findAxisModel('angleAxis'); 72515 72516 setAxis(radiusAxis, radiusAxisModel); 72517 setAxis(angleAxis, angleAxisModel); 72518 72519 resizePolar(polar, polarModel, api); 72520 72521 polarList.push(polar); 72522 72523 polarModel.coordinateSystem = polar; 72524 polar.model = polarModel; 72525 }); 72526 // Inject coordinateSystem to series 72527 ecModel.eachSeries(function (seriesModel) { 72528 if (seriesModel.get('coordinateSystem') === 'polar') { 72529 var polarModel = ecModel.queryComponents({ 72530 mainType: 'polar', 72531 index: seriesModel.get('polarIndex'), 72532 id: seriesModel.get('polarId') 72533 })[0]; 72534 72535 if (__DEV__) { 72536 if (!polarModel) { 72537 throw new Error( 72538 'Polar "' + retrieve( 72539 seriesModel.get('polarIndex'), 72540 seriesModel.get('polarId'), 72541 0 72542 ) + '" not found' 72543 ); 72544 } 72545 } 72546 seriesModel.coordinateSystem = polarModel.coordinateSystem; 72547 } 72548 }); 72549 72550 return polarList; 72551 } 72552 }; 72553 72554 CoordinateSystemManager.register('polar', polarCreator); 72555 72556 /* 72557* Licensed to the Apache Software Foundation (ASF) under one 72558* or more contributor license agreements. See the NOTICE file 72559* distributed with this work for additional information 72560* regarding copyright ownership. The ASF licenses this file 72561* to you under the Apache License, Version 2.0 (the 72562* "License");
72562 you may not use this file except in compliance 72563* with the License. You may obtain a copy of the License at 72564* 72565* http://www.apache.org/licenses/LICENSE-2.0 72566* 72567* Unless required by applicable law or agreed to in writing, 72568* software distributed under the License is distributed on an 72569* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72570* KIND, either express or implied. See the License for the 72571* specific language governing permissions and limitations 72572* under the License. 72573*/ 72574 72575 var elementList$1 = ['axisLine', 'axisLabel', 'axisTick', 'splitLine', 'splitArea']; 72576 72577 function getAxisLineShape(polar, rExtent, angle) { 72578 rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse()); 72579 var start = polar.coordToPoint([rExtent[0], angle]); 72580 var end = polar.coordToPoint([rExtent[1], angle]); 72581 72582 return { 72583 x1: start[0], 72584 y1: start[1], 72585 x2: end[0], 72586 y2: end[1] 72587 }; 72588 } 72589 72590 function getRadiusIdx(polar) { 72591 var radiusAxis = polar.getRadiusAxis(); 72592 return radiusAxis.inverse ? 0 : 1; 72593 } 72594 72595// Remove the last tick which will overlap the first tick 72596 function fixAngleOverlap(list) { 72597 var firstItem = list[0]; 72598 var lastItem = list[list.length - 1]; 72599 if (firstItem 72600 && lastItem 72601 && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4 72602 ) { 72603 list.pop(); 72604 } 72605 } 72606 72607 AxisView.extend({ 72608 72609 type: 'angleAxis', 72610 72611 axisPointerClass: 'PolarAxisPointer', 72612 72613 render: function (angleAxisModel, ecModel) { 72614 this.group.removeAll(); 72615 if (!angleAxisModel.get('show')) { 72616 return; 72617 } 72618 72619 var angleAxis = angleAxisModel.axis; 72620 var polar = angleAxis.polar; 72621 var radiusExtent = polar.getRadiusAxis().getExtent(); 72622 72623 var ticksAngles = angleAxis.getTicksCoords(); 72624 var labels = map(angleAxis.getViewLabels(), function (labelItem) { 72625 var labelItem = clone(labelItem); 72626 labelItem.coord = angleAxis.dataToCoord(labelItem.tickValue); 72627 return labelItem; 72628 }); 72629 72630 fixAngleOverlap(labels); 72631 fixAngleOverlap(ticksAngles); 72632 72633 each$1(elementList$1, function (name) { 72634 if (angleAxisModel.get(name +'.show') 72635 && (!angleAxis.scale.isBlank() || name === 'axisLine') 72636 ) { 72637 this['_' + name](angleAxisModel, polar, ticksAngles, radiusExtent, labels); 72638 } 72639 }, this); 72640 }, 72641 72642 /** 72643 * @private 72644 */ 72645 _axisLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { 72646 var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle'); 72647 72648 var circle = new Circle({ 72649 shape: { 72650 cx: polar.cx, 72651 cy: polar.cy, 72652 r: radiusExtent[getRadiusIdx(polar)] 72653 }, 72654 style: lineStyleModel.getLineStyle(), 72655 z2: 1, 72656 silent: true 72657 }); 72658 circle.style.fill = null; 72659 72660 this.group.add(circle); 72661 }, 72662 72663 /** 72664 * @private 72665 */ 72666 _axisTick: function (angleAxisModel, polar, ticksAngles, radiusExtent) { 72667 var tickModel = angleAxisModel.getModel('axisTick'); 72668 72669 var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length'); 72670 var radius = radiusExtent[getRadiusIdx(polar)]; 72671 72672 var lines = map(ticksAngles, function (tickAngleItem) { 72673 return new Line({ 72674 shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord) 72675 }); 72676 }); 72677 this.group.add(mergePath( 72678 lines, { 72679 style: defaults( 72680 tickModel.getModel('lineStyle').getLineStyle(), 72681 { 72682 stroke: angleAxisModel.get('axisLine.lineStyle.color') 72683 } 72684 ) 72685 } 72686 )); 72687 }, 72688 72689 /** 72690 * @private 72691 */ 72692 _axisLabel: function (angleAxisModel, polar, ticksAngles, radiusExtent, labels) { 72693 var rawCategoryData = angleAxisModel.getCategories(true); 72694 72695 var commonLabelModel = angleAxisModel.getModel('axisLabel'); 72696 72697 var labelMargin = commonLabelModel.get('margin'); 72698 72699 // Use length of ticksAngles because it may remove the last tick to avoid overlapping 72700 each$1(labels, function (labelItem, idx) { 72701 var labelModel = commonLabelModel; 72702 var tickValue = labelItem.tickValue; 72703 72704 var r = radiusExtent[getRadiusIdx(polar)]; 72705 var p = polar.coordToPoint([r + labelMargin, labelItem.coord]); 72706 var cx = polar.cx; 72707 var cy = polar.cy; 72708 72709 var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3 72710 ? 'center' : (p[0] > cx ? 'left' : 'right'); 72711 var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3 72712 ? 'middle' : (p[1] > cy ? 'top' : 'bottom'); 72713 72714 if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) { 72715 labelModel = new Model(rawCategoryData[tickValue].textStyle, commonLabelModel, commonLabelModel.ecModel); 72716 } 72717 72718 var textEl = new Text({silent: true}); 72719 this.group.add(textEl); 72720 setTextStyle(textEl.style, labelModel, { 72721 x: p[0], 72722 y: p[1], 72723 textFill: labelModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'), 72724 text: labelItem.formattedLabel, 72725 textAlign: labelTextAlign, 72726 textVerticalAlign: labelTextVerticalAlign 72727 }); 72728 }, this); 72729 }, 72730 72731 /** 72732 * @private 72733 */ 72734 _splitLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { 72735 var splitLineModel = angleAxisModel.getModel('splitLine'); 72736 var lineStyleModel = splitLineModel.getModel('lineStyle'); 72737 var lineColors = lineStyleModel.get('color'); 72738 var lineCount = 0; 72739 72740 lineColors = lineColors instanceof Array ? lineColors : [lineColors]; 72741 72742 var splitLines = []; 72743 72744 for (var i = 0; i < ticksAngles.length; i++) { 72745 var colorIndex = (lineCount++) % lineColors.length; 72746 splitLines[colorIndex] = splitLines[colorIndex] || []; 72747 splitLines[colorIndex].push(new Line({ 72748 shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord) 72749 })); 72750 } 72751 72752 // Simple optimization 72753 // Batching the lines if color are the same 72754 for (var i = 0; i < splitLines.length; i++) { 72755 this.group.add(mergePath(splitLines[i], { 72756 style: defaults({ 72757 stroke: lineColors[i % lineColors.length] 72758 }, lineStyleModel.getLineStyle()), 72759 silent: true, 72760 z: angleAxisModel.get('z') 72761 })); 72762 } 72763 }, 72764 72765 /** 72766 * @private 72767 */ 72768 _splitArea: function (angleAxisModel, polar, ticksAngles, radiusExtent) { 72769 if (!ticksAngles.length) { 72770 return; 72771 } 72772 72773 var splitAreaModel = angleAxisModel.getModel('splitArea'); 72774 var areaStyleModel = splitAreaModel.getModel('areaStyle'); 72775 var areaColors = areaStyleModel.get('color'); 72776 var lineCount = 0; 72777 72778 areaColors = areaColors instanceof Array ? areaColors : [areaColors]; 72779 72780 var splitAreas = []; 72781 72782 var RADIAN = Math.PI / 180; 72783 var prevAngle = -ticksAngles[0].coord * RADIAN; 72784 var r0 = Math.min(radiusExtent[0], radiusExtent[1]); 72785 var r1 = Math.max(radiusExtent[0], radiusExtent[1]); 72786 72787 var clockwise = angleAxisModel.get('clockwise'); 72788 72789 for (var i = 1; i < ticksAngles.length; i++) { 72790 var colorIndex = (lineCount++) % areaColors.length; 72791 splitAreas[colorIndex] = splitAreas[colorIndex] || []; 72792 splitAreas[colorIndex].push(new Sector({ 72793 shape: { 72794 cx: polar.cx, 72795 cy: polar.cy, 72796 r0: r0, 72797 r: r1, 72798 startAngle: prevAngle, 72799 endAngle: -ticksAngles[i].coord * RADIAN, 72800 clockwise: clockwise 72801 }, 72802 silent: true 72803 })); 72804 prevAngle = -ticksAngles[i].coord * RADIAN; 72805 } 72806 72807 // Simple optimization 72808 // Batching the lines if color are the same 72809 for (var i = 0; i < splitAreas.length; i++) { 72810 this.group.add(mergePath(splitAreas[i], { 72811 style: defaults({ 72812 fill: areaColors[i % areaColors.length] 72813 }, areaStyleModel.getAreaStyle()), 72814 silent: true 72815 })); 72816 } 72817 } 72818 });
72819 72820 /* 72821* Licensed to the Apache Software Foundation (ASF) under one 72822* or more contributor license agreements. See the NOTICE file 72823* distributed with this work for additional information 72824* regarding copyright ownership. The ASF licenses this file 72825* to you under the Apache License, Version 2.0 (the 72826* "License"); you may not use this file except in compliance 72827* with the License. You may obtain a copy of the License at 72828* 72829* http://www.apache.org/licenses/LICENSE-2.0 72830* 72831* Unless required by applicable law or agreed to in writing, 72832* software distributed under the License is distributed on an 72833* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72834* KIND, either express or implied. See the License for the 72835* specific language governing permissions and limitations 72836* under the License. 72837*/ 72838 72839 /* 72840* Licensed to the Apache Software Foundation (ASF) under one 72841* or more contributor license agreements. See the NOTICE file 72842* distributed with this work for additional information 72843* regarding copyright ownership. The ASF licenses this file 72844* to you under the Apache License, Version 2.0 (the 72845* "License"); you may not use this file except in compliance 72846* with the License. You may obtain a copy of the License at 72847* 72848* http://www.apache.org/licenses/LICENSE-2.0 72849* 72850* Unless required by applicable law or agreed to in writing, 72851* software distributed under the License is distributed on an 72852* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 72853* KIND, either express or implied. See the License for the 72854* specific language governing permissions and limitations 72855* under the License. 72856*/ 72857 72858 var axisBuilderAttrs$3 = [ 72859 'axisLine', 'axisTickLabel', 'axisName' 72860 ]; 72861 var selfBuilderAttrs$1 = [ 72862 'splitLine', 'splitArea' 72863 ]; 72864 72865 AxisView.extend({ 72866 72867 type: 'radiusAxis', 72868 72869 axisPointerClass: 'PolarAxisPointer', 72870 72871 render: function (radiusAxisModel, ecModel) { 72872 this.group.removeAll(); 72873 if (!radiusAxisModel.get('show')) { 72874 return; 72875 } 72876 var radiusAxis = radiusAxisModel.axis; 72877 var polar = radiusAxis.polar; 72878 var angleAxis = polar.getAngleAxis(); 72879 var ticksCoords = radiusAxis.getTicksCoords(); 72880 var axisAngle = angleAxis.getExtent()[0]; 72881 var radiusExtent = radiusAxis.getExtent(); 72882 72883 var layout = layoutAxis(polar, radiusAxisModel, axisAngle); 72884 var axisBuilder = new AxisBuilder(radiusAxisModel, layout); 72885 each$1(axisBuilderAttrs$3, axisBuilder.add, axisBuilder); 72886 this.group.add(axisBuilder.getGroup()); 72887 72888 each$1(selfBuilderAttrs$1, function (name) { 72889 if (radiusAxisModel.get(name +'.show') && !radiusAxis.scale.isBlank()) { 72890 this['_' + name](radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords); 72891 } 72892 }, this); 72893 }, 72894 72895 /** 72896 * @private 72897 */ 72898 _splitLine: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { 72899 var splitLineModel = radiusAxisModel.getModel('splitLine'); 72900 var lineStyleModel = splitLineModel.getModel('lineStyle'); 72901 var lineColors = lineStyleModel.get('color'); 72902 var lineCount = 0; 72903 72904 lineColors = lineColors instanceof Array ? lineColors : [lineColors]; 72905 72906 var splitLines = []; 72907 72908 for (var i = 0; i < ticksCoords.length; i++) { 72909 var colorIndex = (lineCount++) % lineColors.length; 72910 splitLines[colorIndex] = splitLines[colorIndex] || []; 72911 splitLines[colorIndex].push(new Circle({ 72912 shape: { 72913 cx: polar.cx, 72914 cy: polar.cy, 72915 r: ticksCoords[i].coord 72916 }, 72917 silent: true 72918 })); 72919 } 72920 72921 // Simple optimization 72922 // Batching the lines if color are the same 72923 for (var i = 0; i < splitLines.length; i++) { 72924 this.group.add(mergePath(splitLines[i], { 72925 style: defaults({ 72926 stroke: lineColors[i % lineColors.length], 72927 fill: null 72928 }, lineStyleModel.getLineStyle()), 72929 silent: true 72930 })); 72931 } 72932 }, 72933 72934 /** 72935 * @private 72936 */ 72937 _splitArea: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { 72938 if (!ticksCoords.length) { 72939 return; 72940 } 72941 72942 var splitAreaModel = radiusAxisModel.getModel('splitArea'); 72943 var areaStyleModel = splitAreaModel.getModel('areaStyle'); 72944 var areaColors = areaStyleModel.get('color'); 72945 var lineCount = 0; 72946 72947 areaColors = areaColors instanceof Array ? areaColors : [areaColors]; 72948 72949 var splitAreas = []; 72950 72951 var prevRadius = ticksCoords[0].coord; 72952 for (var i = 1; i < ticksCoords.length; i++) { 72953 var colorIndex = (lineCount++) % areaColors.length; 72954 splitAreas[colorIndex] = splitAreas[colorIndex] || []; 72955 splitAreas[colorIndex].push(new Sector({ 72956 shape: { 72957 cx: polar.cx, 72958 cy: polar.cy, 72959 r0: prevRadius, 72960 r: ticksCoords[i].coord, 72961 startAngle: 0, 72962 endAngle: Math.PI * 2 72963 }, 72964 silent: true 72965 })); 72966 prevRadius = ticksCoords[i].coord; 72967 } 72968 72969 // Simple optimization 72970 // Batching the lines if color are the same 72971 for (var i = 0; i < splitAreas.length; i++) { 72972 this.group.add(mergePath(splitAreas[i], { 72973 style: defaults({ 72974 fill: areaColors[i % areaColors.length] 72975 }, areaStyleModel.getAreaStyle()), 72976 silent: true 72977 })); 72978 } 72979 } 72980 });
72981 72982 /** 72983 * @inner 72984 */ 72985 function layoutAxis(polar, radiusAxisModel, axisAngle) { 72986 return { 72987 position: [polar.cx, polar.cy], 72988 rotation: axisAngle / 180 * Math.PI, 72989 labelDirection: -1, 72990 tickDirection: -1, 72991 nameDirection: 1, 72992 labelRotate: radiusAxisModel.getModel('axisLabel').get('rotate'), 72993 // Over splitLine and splitArea 72994 z2: 1 72995 }; 72996 } 72997 72998 /* 72999* Licensed to the Apache Software Foundation (ASF) under one 73000* or more contributor license agreements. See the NOTICE file 73001* distributed with this work for additional information 73002* regarding copyright ownership. The ASF licenses this file 73003* to you under the Apache License, Version 2.0 (the 73004* "License"); you may not use this file except in compliance 73005* with the License. You may obtain a copy of the License at 73006* 73007* http://www.apache.org/licenses/LICENSE-2.0 73008* 73009* Unless required by applicable law or agreed to in writing, 73010* software distributed under the License is distributed on an 73011* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73012* KIND, either express or implied. See the License for the 73013* specific language governing permissions and limitations 73014* under the License. 73015*/ 73016 73017 /* 73018* Licensed to the Apache Software Foundation (ASF) under one 73019* or more contributor license agreements. See the NOTICE file 73020* distributed with this work for additional information 73021* regarding copyright ownership. The ASF licenses this file 73022* to you under the Apache License, Version 2.0 (the 73023* "License"); you may not use this file except in compliance 73024* with the License. You may obtain a copy of the License at 73025* 73026* http://www.apache.org/licenses/LICENSE-2.0 73027* 73028* Unless required by applicable law or agreed to in writing, 73029* software distributed under the License is distributed on an 73030* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73031* KIND, either express or implied. See the License for the 73032* specific language governing permissions and limitations 73033* under the License. 73034*/ 73035 73036 var PolarAxisPointer = BaseAxisPointer.extend({ 73037 73038 /** 73039 * @override 73040 */ 73041 makeElOption: function (elOption, value, axisModel, axisPointerModel, api) { 73042 var axis = axisModel.axis; 73043 73044 if (axis.dim === 'angle') { 73045 this.animationThreshold = Math.PI / 18; 73046 } 73047 73048 var polar = axis.polar; 73049 var otherAxis = polar.getOtherAxis(axis); 73050 var otherExtent = otherAxis.getExtent(); 73051 73052 var coordValue; 73053 coordValue = axis['dataTo' + capitalFirst(axis.dim)](value); 73054 73055 var axisPointerType = axisPointerModel.get('type'); 73056 if (axisPointerType && axisPointerType !== 'none') { 73057 var elStyle = buildElStyle(axisPointerModel); 73058 var pointerOption = pointerShapeBuilder$2[axisPointerType]( 73059 axis, polar, coordValue, otherExtent, elStyle 73060 ); 73061 pointerOption.style = elStyle; 73062 elOption.graphicKey = pointerOption.type; 73063 elOption.pointer = pointerOption; 73064 } 73065 73066 var labelMargin = axisPointerModel.get('label.margin'); 73067 var labelPos = getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin); 73068 buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos); 73069 } 73070 73071 // Do not support handle, utill any user requires it. 73072 73073 }); 73074 73075 function getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin) { 73076 var axis = axisModel.axis; 73077 var coord = axis.dataToCoord(value); 73078 var axisAngle = polar.getAngleAxis().getExtent()[0]; 73079 axisAngle = axisAngle / 180 * Math.PI; 73080 var radiusExtent = polar.getRadiusAxis().getExtent(); 73081 var position; 73082 var align; 73083 var verticalAlign; 73084 73085 if (axis.dim === 'radius') { 73086 var transform = create$1(); 73087 rotate(transform, transform, axisAngle); 73088 translate(transform, transform, [polar.cx, polar.cy]); 73089 position = applyTransform$1([coord, -labelMargin], transform); 73090 73091 var labelRotation = axisModel.getModel('axisLabel').get('rotate') || 0; 73092 var labelLayout = AxisBuilder.innerTextLayout( 73093 axisAngle, labelRotation * Math.PI / 180, -1 73094 ); 73095 align = labelLayout.textAlign; 73096 verticalAlign = labelLayout.textVerticalAlign; 73097 } 73098 else { // angle axis 73099 var r = radiusExtent[1];
73100 position = polar.coordToPoint([r + labelMargin, coord]); 73101 var cx = polar.cx; 73102 var cy = polar.cy; 73103 align = Math.abs(position[0] - cx) / r < 0.3 73104 ? 'center' : (position[0] > cx ? 'left' : 'right'); 73105 verticalAlign = Math.abs(position[1] - cy) / r < 0.3 73106 ? 'middle' : (position[1] > cy ? 'top' : 'bottom'); 73107 } 73108 73109 return { 73110 position: position, 73111 align: align, 73112 verticalAlign: verticalAlign 73113 }; 73114 } 73115 73116 73117 var pointerShapeBuilder$2 = { 73118 73119 line: function (axis, polar, coordValue, otherExtent, elStyle) { 73120 return axis.dim === 'angle' 73121 ? { 73122 type: 'Line', 73123 shape: makeLineShape( 73124 polar.coordToPoint([otherExtent[0], coordValue]), 73125 polar.coordToPoint([otherExtent[1], coordValue]) 73126 ) 73127 } 73128 : { 73129 type: 'Circle', 73130 shape: { 73131 cx: polar.cx, 73132 cy: polar.cy, 73133 r: coordValue 73134 } 73135 }; 73136 }, 73137 73138 shadow: function (axis, polar, coordValue, otherExtent, elStyle) { 73139 var bandWidth = Math.max(1, axis.getBandWidth()); 73140 var radian = Math.PI / 180; 73141 73142 return axis.dim === 'angle' 73143 ? { 73144 type: 'Sector', 73145 shape: makeSectorShape( 73146 polar.cx, polar.cy, 73147 otherExtent[0], otherExtent[1], 73148 // In ECharts y is negative if angle is positive 73149 (-coordValue - bandWidth / 2) * radian, 73150 (-coordValue + bandWidth / 2) * radian 73151 ) 73152 } 73153 : { 73154 type: 'Sector', 73155 shape: makeSectorShape( 73156 polar.cx, polar.cy, 73157 coordValue - bandWidth / 2, 73158 coordValue + bandWidth / 2, 73159 0, Math.PI * 2 73160 ) 73161 }; 73162 } 73163 }; 73164 73165 AxisView.registerAxisPointerClass('PolarAxisPointer', PolarAxisPointer); 73166 73167 /* 73168* Licensed to the Apache Software Foundation (ASF) under one 73169* or more contributor license agreements. See the NOTICE file 73170* distributed with this work for additional information 73171* regarding copyright ownership. The ASF licenses this file 73172* to you under the Apache License, Version 2.0 (the 73173* "License"); you may not use this file except in compliance 73174* with the License. You may obtain a copy of the License at 73175* 73176* http://www.apache.org/licenses/LICENSE-2.0 73177* 73178* Unless required by applicable law or agreed to in writing, 73179* software distributed under the License is distributed on an 73180* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73181* KIND, either express or implied. See the License for the 73182* specific language governing permissions and limitations 73183* under the License. 73184*/ 73185 73186// For reducing size of echarts.min, barLayoutPolar is required by polar. 73187 registerLayout(curry(barLayoutPolar, 'bar')); 73188 73189// Polar view 73190 extendComponentView({ 73191 type: 'polar' 73192 }); 73193 73194 /* 73195* Licensed to the Apache Software Foundation (ASF) under one 73196* or more contributor license agreements. See the NOTICE file 73197* distributed with this work for additional information 73198* regarding copyright ownership. The ASF licenses this file 73199* to you under the Apache License, Version 2.0 (the 73200* "License"); you may not use this file except in compliance 73201* with the License. You may obtain a copy of the License at 73202* 73203* http://www.apache.org/licenses/LICENSE-2.0 73204* 73205* Unless required by applicable law or agreed to in writing, 73206* software distributed under the License is distributed on an 73207* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73208* KIND, either express or implied. See the License for the 73209* specific language governing permissions and limitations 73210* under the License. 73211*/ 73212 73213 var GeoModel = ComponentModel.extend({ 73214 73215 type: 'geo', 73216 73217 /** 73218 * @type {module:echarts/coord/geo/Geo} 73219 */ 73220 coordinateSystem: null, 73221 73222 layoutMode: 'box', 73223 73224 init: function (option) {
73225 ComponentModel.prototype.init.apply(this, arguments); 73226 73227 // Default label emphasis `show` 73228 defaultEmphasis(option, 'label', ['show']); 73229 }, 73230 73231 optionUpdated: function () { 73232 var option = this.option; 73233 var self = this; 73234 73235 option.regions = geoCreator.getFilledRegions(option.regions, option.map, option.nameMap); 73236 73237 this._optionModelMap = reduce(option.regions || [], function (optionModelMap, regionOpt) { 73238 if (regionOpt.name) { 73239 optionModelMap.set(regionOpt.name, new Model(regionOpt, self)); 73240 } 73241 return optionModelMap; 73242 }, createHashMap()); 73243 73244 this.updateSelectedMap(option.regions); 73245 }, 73246 73247 defaultOption: { 73248 73249 zlevel: 0, 73250 73251 z: 0, 73252 73253 show: true, 73254 73255 left: 'center', 73256 73257 top: 'center', 73258 73259 73260 // width:, 73261 // height:, 73262 // right 73263 // bottom 73264 73265 // Aspect is width / height. Inited to be geoJson bbox aspect 73266 // This parameter is used for scale this aspect 73267 aspectScale: 0.75, 73268 73269 ///// Layout with center and size 73270 // If you wan't to put map in a fixed size box with right aspect ratio 73271 // This two properties may more conveninet 73272 // layoutCenter: [50%, 50%] 73273 // layoutSize: 100 73274 73275 73276 silent: false, 73277 73278 // Map type 73279 map: '', 73280 73281 // Define left-top, right-bottom coords to control view 73282 // For example, [ [180, 90], [-180, -90] ] 73283 boundingCoords: null, 73284 73285 // Default on center of map 73286 center: null, 73287 73288 zoom: 1, 73289 73290 scaleLimit: null, 73291 73292 // selectedMode: false 73293 73294 label: { 73295 show: false, 73296 color: '#000' 73297 }, 73298 73299 itemStyle: { 73300 // color: Ã¥Ââå¼â, 73301 borderWidth: 0.5, 73302 borderColor: '#444', 73303 color: '#eee' 73304 }, 73305 73306 emphasis: { 73307 label: { 73308 show: true, 73309 color: 'rgb(100,0,0)' 73310 }, 73311 itemStyle: { 73312 color: 'rgba(255,215,0,0.8)' 73313 } 73314 }, 73315 73316 regions: [] 73317 }, 73318 73319 /** 73320 * Get model of region 73321 * @param {string} name 73322 * @return {module:echarts/model/Model} 73323 */ 73324 getRegionModel: function (name) { 73325 return this._optionModelMap.get(name) || new Model(null, this, this.ecModel); 73326 }, 73327 73328 /** 73329 * Format label 73330 * @param {string} name Region name 73331 * @param {string} [status='normal'] 'normal' or 'emphasis' 73332 * @return {string} 73333 */ 73334 getFormattedLabel: function (name, status) { 73335 var regionModel = this.getRegionModel(name); 73336 var formatter = regionModel.get('label.' + status + '.formatter'); 73337 var params = { 73338 name: name 73339 }; 73340 if (typeof formatter === 'function') { 73341 params.status = status; 73342 return formatter(params); 73343 } 73344 else if (typeof formatter === 'string') { 73345 return formatter.replace('{a}', name != null ? name : ''); 73346 } 73347 }, 73348 73349 setZoom: function (zoom) { 73350 this.option.zoom = zoom; 73351 }, 73352 73353 setCenter: function (center) { 73354 this.option.center = center; 73355 } 73356 }); 73357 73358 mixin(GeoModel, selectableMixin); 73359 73360 /* 73361* Licensed to the Apache Software Foundation (ASF) under one 73362* or more contributor license agreements. See the NOTICE file 73363* distributed with this work for additional information 73364* regarding copyright ownership. The ASF licenses this file 73365* to you under the Apache License, Version 2.0 (the 73366* "License");
73366 you may not use this file except in compliance 73367* with the License. You may obtain a copy of the License at 73368* 73369* http://www.apache.org/licenses/LICENSE-2.0 73370* 73371* Unless required by applicable law or agreed to in writing, 73372* software distributed under the License is distributed on an 73373* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73374* KIND, either express or implied. See the License for the 73375* specific language governing permissions and limitations 73376* under the License. 73377*/ 73378 73379 extendComponentView({ 73380 73381 type: 'geo', 73382 73383 init: function (ecModel, api) { 73384 var mapDraw = new MapDraw(api, true); 73385 this._mapDraw = mapDraw; 73386 73387 this.group.add(mapDraw.group); 73388 }, 73389 73390 render: function (geoModel, ecModel, api, payload) { 73391 // Not render if it is an toggleSelect action from self 73392 if (payload && payload.type === 'geoToggleSelect' 73393 && payload.from === this.uid 73394 ) { 73395 return; 73396 } 73397 73398 var mapDraw = this._mapDraw; 73399 if (geoModel.get('show')) { 73400 mapDraw.draw(geoModel, ecModel, api, this, payload); 73401 } 73402 else { 73403 this._mapDraw.group.removeAll(); 73404 } 73405 73406 this.group.silent = geoModel.get('silent'); 73407 }, 73408 73409 dispose: function () { 73410 this._mapDraw && this._mapDraw.remove(); 73411 } 73412 73413 }); 73414 73415 /* 73416* Licensed to the Apache Software Foundation (ASF) under one 73417* or more contributor license agreements. See the NOTICE file 73418* distributed with this work for additional information 73419* regarding copyright ownership. The ASF licenses this file 73420* to you under the Apache License, Version 2.0 (the 73421* "License"); you may not use this file except in compliance 73422* with the License. You may obtain a copy of the License at 73423* 73424* http://www.apache.org/licenses/LICENSE-2.0 73425* 73426* Unless required by applicable law or agreed to in writing, 73427* software distributed under the License is distributed on an 73428* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73429* KIND, either express or implied. See the License for the 73430* specific language governing permissions and limitations 73431* under the License. 73432*/ 73433 73434 function makeAction(method, actionInfo) { 73435 actionInfo.update = 'updateView'; 73436 registerAction(actionInfo, function (payload, ecModel) { 73437 var selected = {}; 73438 73439 ecModel.eachComponent( 73440 { mainType: 'geo', query: payload}, 73441 function (geoModel) { 73442 geoModel[method](payload.name); 73443 var geo = geoModel.coordinateSystem; 73444 each$1(geo.regions, function (region) { 73445 selected[region.name] = geoModel.isSelected(region.name) || false; 73446 }); 73447 } 73448 ); 73449 73450 return { 73451 selected: selected, 73452 name: payload.name 73453 }; 73454 }); 73455 } 73456 73457 makeAction('toggleSelected', { 73458 type: 'geoToggleSelect', 73459 event: 'geoselectchanged' 73460 }); 73461 makeAction('select', { 73462 type: 'geoSelect', 73463 event: 'geoselected' 73464 }); 73465 makeAction('unSelect', { 73466 type: 'geoUnSelect', 73467 event: 'geounselected' 73468 }); 73469 73470 /* 73471* Licensed to the Apache Software Foundation (ASF) under one 73472* or more contributor license agreements. See the NOTICE file 73473* distributed with this work for additional information 73474* regarding copyright ownership. The ASF licenses this file 73475* to you under the Apache License, Version 2.0 (the 73476* "License"); you may not use this file except in compliance 73477* with the License. You may obtain a copy of the License at 73478* 73479* http://www.apache.org/licenses/LICENSE-2.0 73480* 73481* Unless required by applicable law or agreed to in writing, 73482* software distributed under the License is distributed on an 73483* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73484* KIND, either express or implied. See the License for the 73485* specific language governing permissions and limitations 73486* under the License. 73487*/ 73488 73489 var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear']; 73490 73491 var preprocessor$1 = function (option, isNew) { 73492 var brushComponents = option && option.brush; 73493 if (!isArray(brushComponents)) { 73494 brushComponents = brushComponents ? [brushComponents] : []; 73495 } 73496 73497 if (!brushComponents.length) { 73498 return; 73499 } 73500 73501 var brushComponentSpecifiedBtns = []; 73502 73503 each$1(brushComponents, function (brushOpt) { 73504 var tbs = brushOpt.hasOwnProperty('toolbox') 73505 ? brushOpt.toolbox : []; 73506 73507 if (tbs instanceof Array) { 73508 brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs); 73509 } 73510 }); 73511 73512 var toolbox = option && option.toolbox; 73513 73514 if (isArray(toolbox)) { 73515 toolbox = toolbox[0]; 73516 } 73517 if (!toolbox) { 73518 toolbox = {feature: {}}; 73519 option.toolbox = [toolbox]; 73520 } 73521 73522 var toolboxFeature = (toolbox.feature || (toolbox.feature = {})); 73523 var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {}); 73524 var brushTypes = toolboxBrush.type || (toolboxBrush.type = []); 73525 73526 brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns); 73527 73528 removeDuplicate(brushTypes); 73529 73530 if (isNew && !brushTypes.length) { 73531 brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS); 73532 } 73533 }; 73534 73535 function removeDuplicate(arr) { 73536 var map$$1 = {}; 73537 each$1(arr, function (val) { 73538 map$$1[val] = 1; 73539 }); 73540 arr.length = 0; 73541 each$1(map$$1, function (flag, val) { 73542 arr.push(val); 73543 }); 73544 } 73545 73546 /* 73547* Licensed to the Apache Software Foundation (ASF) under one 73548* or more contributor license agreements. See the NOTICE file 73549* distributed with this work for additional information 73550* regarding copyright ownership. The ASF licenses this file 73551* to you under the Apache License, Version 2.0 (the 73552* "License");
73552 you may not use this file except in compliance 73553* with the License. You may obtain a copy of the License at 73554* 73555* http://www.apache.org/licenses/LICENSE-2.0 73556* 73557* Unless required by applicable law or agreed to in writing, 73558* software distributed under the License is distributed on an 73559* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73560* KIND, either express or implied. See the License for the 73561* specific language governing permissions and limitations 73562* under the License. 73563*/ 73564 73565 /** 73566 * @file Visual solution, for consistent option specification. 73567 */ 73568 73569 var each$19 = each$1; 73570 73571 function hasKeys(obj) { 73572 if (obj) { 73573 for (var name in obj){ 73574 if (obj.hasOwnProperty(name)) { 73575 return true; 73576 } 73577 } 73578 } 73579 } 73580 73581 /** 73582 * @param {Object} option 73583 * @param {Array.<string>} stateList 73584 * @param {Function} [supplementVisualOption] 73585 * @return {Object} visualMappings <state, <visualType, module:echarts/visual/VisualMapping>> 73586 */ 73587 function createVisualMappings(option, stateList, supplementVisualOption) { 73588 var visualMappings = {}; 73589 73590 each$19(stateList, function (state) { 73591 var mappings = visualMappings[state] = createMappings(); 73592 73593 each$19(option[state], function (visualData, visualType) { 73594 if (!VisualMapping.isValidType(visualType)) { 73595 return; 73596 } 73597 var mappingOption = { 73598 type: visualType, 73599 visual: visualData 73600 }; 73601 supplementVisualOption && supplementVisualOption(mappingOption, state); 73602 mappings[visualType] = new VisualMapping(mappingOption); 73603 73604 // Prepare a alpha for opacity, for some case that opacity 73605 // is not supported, such as rendering using gradient color. 73606 if (visualType === 'opacity') { 73607 mappingOption = clone(mappingOption); 73608 mappingOption.type = 'colorAlpha'; 73609 mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption); 73610 } 73611 }); 73612 }); 73613 73614 return visualMappings; 73615 73616 function createMappings() { 73617 var Creater = function () {}; 73618 // Make sure hidden fields will not be visited by 73619 // object iteration (with hasOwnProperty checking). 73620 Creater.prototype.__hidden = Creater.prototype; 73621 var obj = new Creater(); 73622 return obj; 73623 } 73624 } 73625 73626 /** 73627 * @param {Object} thisOption 73628 * @param {Object} newOption 73629 * @param {Array.<string>} keys 73630 */ 73631 function replaceVisualOption(thisOption, newOption, keys) { 73632 // Visual attributes merge is not supported, otherwise it 73633 // brings overcomplicated merge logic. See #2853. So if 73634 // newOption has anyone of these keys, all of these keys 73635 // will be reset. Otherwise, all keys remain. 73636 var has; 73637 each$1(keys, function (key) { 73638 if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { 73639 has = true; 73640 } 73641 }); 73642 has && each$1(keys, function (key) { 73643 if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { 73644 thisOption[key] = clone(newOption[key]); 73645 } 73646 else { 73647 delete thisOption[key]; 73648 } 73649 }); 73650 } 73651 73652 /** 73653 * @param {Array.<string>} stateList 73654 * @param {Object} visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>> 73655 * @param {module:echarts/data/List} list 73656 * @param {Function} getValueState param: valueOrIndex, return: state. 73657 * @param {object} [scope] Scope for getValueState 73658 * @param {string} [dimension] Concrete dimension, if used. 73659 */ 73660// ???! handle brush? 73661 function applyVisual(stateList, visualMappings, data, getValueState, scope, dimension) { 73662 var visualTypesMap = {}; 73663 each$1(stateList, function (state) { 73664 var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); 73665 visualTypesMap[state] = visualTypes; 73666 }); 73667 73668 var dataIndex; 73669 73670 function getVisual(key) { 73671 return data.getItemVisual(dataIndex, key); 73672 } 73673 73674 function setVisual(key, value) { 73675 data.setItemVisual(dataIndex, key, value); 73676 } 73677 73678 if (dimension == null) { 73679 data.each(eachItem); 73680 } 73681 else { 73682 data.each([dimension], eachItem); 73683 } 73684 73685 function eachItem(valueOrIndex, index) { 73686 dataIndex = dimension == null ? valueOrIndex : index; 73687 73688 var rawDataItem = data.getRawDataItem(dataIndex); 73689 // Consider performance 73690 if (rawDataItem && rawDataItem.visualMap === false) { 73691 return; 73692 } 73693 73694 var valueState = getValueState.call(scope, valueOrIndex); 73695 var mappings = visualMappings[valueState]; 73696 var visualTypes = visualTypesMap[valueState]; 73697 73698 for (var i = 0, len = visualTypes.length; i < len; i++) { 73699 var type = visualTypes[i];
73700 mappings[type] && mappings[type].applyVisual( 73701 valueOrIndex, getVisual, setVisual 73702 ); 73703 } 73704 } 73705 } 73706 73707 /** 73708 * @param {module:echarts/data/List} data 73709 * @param {Array.<string>} stateList 73710 * @param {Object} visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>> 73711 * @param {Function} getValueState param: valueOrIndex, return: state. 73712 * @param {number} [dim] dimension or dimension index. 73713 */ 73714 function incrementalApplyVisual(stateList, visualMappings, getValueState, dim) { 73715 var visualTypesMap = {}; 73716 each$1(stateList, function (state) { 73717 var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); 73718 visualTypesMap[state] = visualTypes; 73719 }); 73720 73721 function progress(params, data) { 73722 if (dim != null) { 73723 dim = data.getDimension(dim); 73724 } 73725 73726 function getVisual(key) { 73727 return data.getItemVisual(dataIndex, key); 73728 } 73729 73730 function setVisual(key, value) { 73731 data.setItemVisual(dataIndex, key, value); 73732 } 73733 73734 var dataIndex; 73735 while ((dataIndex = params.next()) != null) { 73736 var rawDataItem = data.getRawDataItem(dataIndex); 73737 73738 // Consider performance 73739 if (rawDataItem && rawDataItem.visualMap === false) { 73740 return; 73741 } 73742 73743 var value = dim != null 73744 ? data.get(dim, dataIndex, true) 73745 : dataIndex; 73746 73747 var valueState = getValueState(value); 73748 var mappings = visualMappings[valueState]; 73749 var visualTypes = visualTypesMap[valueState]; 73750 73751 for (var i = 0, len = visualTypes.length; i < len; i++) { 73752 var type = visualTypes[i]; 73753 mappings[type] && mappings[type].applyVisual(value, getVisual, setVisual); 73754 } 73755 } 73756 } 73757 73758 return {progress: progress}; 73759 } 73760 73761 /* 73762* Licensed to the Apache Software Foundation (ASF) under one 73763* or more contributor license agreements. See the NOTICE file 73764* distributed with this work for additional information 73765* regarding copyright ownership. The ASF licenses this file 73766* to you under the Apache License, Version 2.0 (the 73767* "License"); you may not use this file except in compliance 73768* with the License. You may obtain a copy of the License at 73769* 73770* http://www.apache.org/licenses/LICENSE-2.0 73771* 73772* Unless required by applicable law or agreed to in writing, 73773* software distributed under the License is distributed on an 73774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73775* KIND, either express or implied. See the License for the 73776* specific language governing permissions and limitations 73777* under the License. 73778*/ 73779 73780// Key of the first level is brushType: `line`, `rect`, `polygon`. 73781// Key of the second level is chart element type: `point`, `rect`. 73782// See moudule:echarts/component/helper/BrushController 73783// function param: 73784// {Object} itemLayout fetch from data.getItemLayout(dataIndex) 73785// {Object} selectors {point: selector, rect: selector, ...} 73786// {Object} area {range: [[], [], ..], boudingRect} 73787// function return: 73788// {boolean} Whether in the given brush. 73789 var selector = { 73790 lineX: getLineSelectors(0), 73791 lineY: getLineSelectors(1), 73792 rect: { 73793 point: function (itemLayout, selectors, area) { 73794 return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]); 73795 }, 73796 rect: function (itemLayout, selectors, area) { 73797 return itemLayout && area.boundingRect.intersect(itemLayout); 73798 } 73799 }, 73800 polygon: { 73801 point: function (itemLayout, selectors, area) { 73802 return itemLayout 73803 && area.boundingRect.contain(itemLayout[0], itemLayout[1]) 73804 && contain$1(area.range, itemLayout[0], itemLayout[1]); 73805 }, 73806 rect: function (itemLayout, selectors, area) { 73807 var points = area.range; 73808 73809 if (!itemLayout || points.length <= 1) { 73810 return false; 73811 } 73812 73813 var x = itemLayout.x; 73814 var y = itemLayout.y; 73815 var width = itemLayout.width; 73816 var height = itemLayout.height; 73817 var p = points[0]; 73818 73819 if (contain$1(points, x, y) 73820 || contain$1(points, x + width, y) 73821 || contain$1(points, x, y + height) 73822 || contain$1(points, x + width, y + height) 73823 || BoundingRect.create(itemLayout).contain(p[0], p[1]) 73824 || lineIntersectPolygon(x, y, x + width, y, points) 73825 || lineIntersectPolygon(x, y, x, y + height, points) 73826 || lineIntersectPolygon(x + width, y, x + width, y + height, points) 73827 || lineIntersectPolygon(x, y + height, x + width, y + height, points) 73828 ) { 73829 return true; 73830 } 73831 } 73832 } 73833 }; 73834 73835 function getLineSelectors(xyIndex) { 73836 var xy = ['x', 'y']; 73837 var wh = ['width', 'height']; 73838 73839 return { 73840 point: function (itemLayout, selectors, area) { 73841 if (itemLayout) { 73842 var range = area.range; 73843 var p = itemLayout[xyIndex]; 73844 return inLineRange(p, range); 73845 } 73846 }, 73847 rect: function (itemLayout, selectors, area) { 73848 if (itemLayout) { 73849 var range = area.range; 73850 var layoutRange = [ 73851 itemLayout[xy[xyIndex]], 73852 itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]] 73853 ]; 73854 layoutRange[1] < layoutRange[0] && layoutRange.reverse(); 73855 return inLineRange(layoutRange[0], range) 73856 || inLineRange(layoutRange[1], range) 73857 || inLineRange(range[0], layoutRange) 73858 || inLineRange(range[1], layoutRange); 73859 } 73860 } 73861 }; 73862 } 73863 73864 function inLineRange(p, range) { 73865 return range[0] <= p && p <= range[1]; 73866 } 73867 73868 function lineIntersectPolygon(lx, ly, l2x, l2y, points) { 73869 for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) { 73870 var p = points[i]; 73871 if (lineIntersect(lx, ly, l2x, l2y, p[0], p[1], p2[0], p2[1])) { 73872 return true; 73873 } 73874 p2 = p; 73875 } 73876 } 73877 73878// Code from <http://blog.csdn.net/rickliuxiao/article/details/6259322> with some fix. 73879// See <https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection> 73880 function lineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) { 73881 var delta = determinant(a2x - a1x, b1x - b2x, a2y - a1y, b1y - b2y); 73882 if (nearZero(delta)) { // parallel 73883 return false; 73884 } 73885 var namenda = determinant(b1x - a1x, b1x - b2x, b1y - a1y, b1y - b2y) / delta; 73886 if (namenda < 0 || namenda > 1) { 73887 return false; 73888 } 73889 var miu = determinant(a2x - a1x, b1x - a1x, a2y - a1y, b1y - a1y) / delta; 73890 if (miu < 0 || miu > 1) { 73891 return false; 73892 } 73893 return true; 73894 } 73895 73896 function nearZero(val) { 73897 return val <= (1e-6) && val >= -(1e-6); 73898 } 73899 73900 function determinant(v1, v2, v3, v4) { 73901 return v1 * v4 - v2 * v3; 73902 } 73903 73904 /* 73905* Licensed to the Apache Software Foundation (ASF) under one 73906* or more contributor license agreements. See the NOTICE file 73907* distributed with this work for additional information 73908* regarding copyright ownership. The ASF licenses this file 73909* to you under the Apache License, Version 2.0 (the 73910* "License");
73910 you may not use this file except in compliance 73911* with the License. You may obtain a copy of the License at 73912* 73913* http://www.apache.org/licenses/LICENSE-2.0 73914* 73915* Unless required by applicable law or agreed to in writing, 73916* software distributed under the License is distributed on an 73917* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 73918* KIND, either express or implied. See the License for the 73919* specific language governing permissions and limitations 73920* under the License. 73921*/ 73922 73923 var each$20 = each$1; 73924 var indexOf$1 = indexOf; 73925 var curry$5 = curry; 73926 73927 var COORD_CONVERTS = ['dataToPoint', 'pointToData']; 73928 73929// FIXME 73930// how to genarialize to more coordinate systems. 73931 var INCLUDE_FINDER_MAIN_TYPES = [ 73932 'grid', 'xAxis', 'yAxis', 'geo', 'graph', 73933 'polar', 'radiusAxis', 'angleAxis', 'bmap' 73934 ]; 73935 73936 /** 73937 * [option in constructor]: 73938 * { 73939 * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder. 73940 * } 73941 * 73942 * 73943 * [targetInfo]: 73944 * 73945 * There can be multiple axes in a single targetInfo. Consider the case 73946 * of `grid` component, a targetInfo represents a grid which contains one or more 73947 * cartesian and one or more axes. And consider the case of parallel system, 73948 * which has multiple axes in a coordinate system. 73949 * Can be { 73950 * panelId: ..., 73951 * coordSys: <a representitive cartesian in grid (first cartesian by default)>, 73952 * coordSyses: all cartesians. 73953 * gridModel: <grid component> 73954 * xAxes: correspond to coordSyses on index 73955 * yAxes: correspond to coordSyses on index 73956 * } 73957 * or { 73958 * panelId: ..., 73959 * coordSys: <geo coord sys> 73960 * coordSyses: [<geo coord sys>] 73961 * geoModel: <geo component> 73962 * } 73963 * 73964 * 73965 * [panelOpt]: 73966 * 73967 * Make from targetInfo. Input to BrushController. 73968 * { 73969 * panelId: ..., 73970 * rect: ... 73971 * } 73972 * 73973 * 73974 * [area]: 73975 * 73976 * Generated by BrushController or user input. 73977 * { 73978 * panelId: Used to locate coordInfo directly. If user inpput, no panelId. 73979 * brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y'). 73980 * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder. 73981 * range: pixel range. 73982 * coordRange: representitive coord range (the first one of coordRanges). 73983 * coordRanges: <Array> coord ranges, used in multiple cartesian in one grid. 73984 * } 73985 */ 73986 73987 /** 73988 * @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid 73989 * Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]} 73990 * @param {module:echarts/model/Global} ecModel 73991 * @param {Object} [opt] 73992 * @param {Array.<string>} [opt.include] include coordinate system types. 73993 */ 73994 function BrushTargetManager(option, ecModel, opt) { 73995 /** 73996 * @private 73997 * @type {Array.<Object>} 73998 */ 73999 var targetInfoList = this._targetInfoList = []; 74000 var info = {}; 74001 var foundCpts = parseFinder$1(ecModel, option); 74002 74003 each$20(targetInfoBuilders, function (builder, type) { 74004 if (!opt || !opt.include || indexOf$1(opt.include, type) >= 0) { 74005 builder(foundCpts, targetInfoList, info); 74006 } 74007 }); 74008 } 74009 74010 var proto$2 = BrushTargetManager.prototype; 74011 74012 proto$2.setOutputRanges = function (areas, ecModel) { 74013 this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) { 74014 (area.coordRanges || (area.coordRanges = [])).push(coordRange); 74015 // area.coordRange is the first of area.coordRanges 74016 if (!area.coordRange) { 74017 area.coordRange = coordRange; 74018 // In 'category' axis, coord to pixel is not reversible, so we can not 74019 // rebuild range by coordRange accrately, which may bring trouble when 74020 // brushing only one item. So we use __rangeOffset to rebuilding range 74021 // by coordRange. And this it only used in brush component so it is no 74022 // need to be adapted to coordRanges. 74023 var result = coordConvert[area.brushType](0, coordSys, coordRange); 74024 area.__rangeOffset = { 74025 offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]), 74026 xyMinMax: result.xyMinMax 74027 }; 74028 } 74029 }); 74030 }; 74031 74032 proto$2.matchOutputRanges = function (areas, ecModel, cb) { 74033 each$20(areas, function (area) { 74034 var targetInfo = this.findTargetInfo(area, ecModel); 74035 74036 if (targetInfo && targetInfo !== true) { 74037 each$1( 74038 targetInfo.coordSyses, 74039 function (coordSys) { 74040 var result = coordConvert[area.brushType](1, coordSys, area.range); 74041 cb(area, result.values, coordSys, ecModel); 74042 } 74043 ); 74044 } 74045 }, this); 74046 }; 74047 74048 proto$2.setInputRanges = function (areas, ecModel) { 74049 each$20(areas, function (area) { 74050 var targetInfo = this.findTargetInfo(area, ecModel); 74051 74052 if (__DEV__) { 74053 assert$1( 74054 !targetInfo || targetInfo === true || area.coordRange,
vendor: 6,165 bytes, lines 74055-74205
74055 'coordRange must be specified when coord index specified.' 74056 ); 74057 assert$1( 74058 !targetInfo || targetInfo !== true || area.range, 74059 'range must be specified in global brush.' 74060 ); 74061 } 74062 74063 area.range = area.range || []; 74064 74065 // convert coordRange to global range and set panelId. 74066 if (targetInfo && targetInfo !== true) { 74067 area.panelId = targetInfo.panelId; 74068 // (1) area.range shoule always be calculate from coordRange but does 74069 // not keep its original value, for the sake of the dataZoom scenario, 74070 // where area.coordRange remains unchanged but area.range may be changed. 74071 // (2) Only support converting one coordRange to pixel range in brush 74072 // component. So do not consider `coordRanges`. 74073 // (3) About __rangeOffset, see comment above. 74074 var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange); 74075 var rangeOffset = area.__rangeOffset; 74076 area.range = rangeOffset 74077 ? diffProcessor[area.brushType]( 74078 result.values, 74079 rangeOffset.offset, 74080 getScales(result.xyMinMax, rangeOffset.xyMinMax) 74081 ) 74082 : result.values; 74083 } 74084 }, this); 74085 }; 74086 74087 proto$2.makePanelOpts = function (api, getDefaultBrushType) { 74088 return map(this._targetInfoList, function (targetInfo) { 74089 var rect = targetInfo.getPanelRect(); 74090 return { 74091 panelId: targetInfo.panelId, 74092 defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo), 74093 clipPath: makeRectPanelClipPath(rect), 74094 isTargetByCursor: makeRectIsTargetByCursor( 74095 rect, api, targetInfo.coordSysModel 74096 ), 74097 getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect) 74098 }; 74099 }); 74100 }; 74101 74102 proto$2.controlSeries = function (area, seriesModel, ecModel) { 74103 // Check whether area is bound in coord, and series do not belong to that coord. 74104 // If do not do this check, some brush (like lineX) will controll all axes. 74105 var targetInfo = this.findTargetInfo(area, ecModel); 74106 return targetInfo === true || ( 74107 targetInfo && indexOf$1(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0 74108 ); 74109 }; 74110 74111 /** 74112 * If return Object, a coord found. 74113 * If reutrn true, global found. 74114 * Otherwise nothing found. 74115 * 74116 * @param {Object} area 74117 * @param {Array} targetInfoList 74118 * @return {Object|boolean} 74119 */ 74120 proto$2.findTargetInfo = function (area, ecModel) { 74121 var targetInfoList = this._targetInfoList; 74122 var foundCpts = parseFinder$1(ecModel, area); 74123 74124 for (var i = 0; i < targetInfoList.length; i++) { 74125 var targetInfo = targetInfoList[i]; 74126 var areaPanelId = area.panelId; 74127 if (areaPanelId) { 74128 if (targetInfo.panelId === areaPanelId) { 74129 return targetInfo; 74130 } 74131 } 74132 else { 74133 for (var i = 0; i < targetInfoMatchers.length; i++) { 74134 if (targetInfoMatchers[i](foundCpts, targetInfo)) { 74135 return targetInfo; 74136 } 74137 } 74138 } 74139 } 74140 74141 return true; 74142 }; 74143 74144 function formatMinMax(minMax) { 74145 minMax[0] > minMax[1] && minMax.reverse(); 74146 return minMax; 74147 } 74148 74149 function parseFinder$1(ecModel, option) { 74150 return parseFinder( 74151 ecModel, option, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES} 74152 ); 74153 } 74154 74155 var targetInfoBuilders = { 74156 74157 grid: function (foundCpts, targetInfoList) { 74158 var xAxisModels = foundCpts.xAxisModels; 74159 var yAxisModels = foundCpts.yAxisModels; 74160 var gridModels = foundCpts.gridModels; 74161 // Remove duplicated. 74162 var gridModelMap = createHashMap(); 74163 var xAxesHas = {}; 74164 var yAxesHas = {}; 74165 74166 if (!xAxisModels && !yAxisModels && !gridModels) { 74167 return; 74168 } 74169 74170 each$20(xAxisModels, function (axisModel) { 74171 var gridModel = axisModel.axis.grid.model; 74172 gridModelMap.set(gridModel.id, gridModel); 74173 xAxesHas[gridModel.id] = true; 74174 }); 74175 each$20(yAxisModels, function (axisModel) { 74176 var gridModel = axisModel.axis.grid.model; 74177 gridModelMap.set(gridModel.id, gridModel); 74178 yAxesHas[gridModel.id] = true; 74179 }); 74180 each$20(gridModels, function (gridModel) { 74181 gridModelMap.set(gridModel.id, gridModel); 74182 xAxesHas[gridModel.id] = true; 74183 yAxesHas[gridModel.id] = true; 74184 }); 74185 74186 gridModelMap.each(function (gridModel) { 74187 var grid = gridModel.coordinateSystem; 74188 var cartesians = []; 74189 74190 each$20(grid.getCartesians(), function (cartesian, index) { 74191 if (indexOf$1(xAxisModels, cartesian.getAxis('x').model) >= 0 74192 || indexOf$1(yAxisModels, cartesian.getAxis('y').model) >= 0 74193 ) { 74194 cartesians.push(cartesian); 74195 } 74196 }); 74197 targetInfoList.push({ 74198 panelId: 'grid--' + gridModel.id, 74199 gridModel: gridModel, 74200 coordSysModel: gridModel, 74201 // Use the first one as the representitive coordSys. 74202 coordSys: cartesians[0], 74203 coordSyses: cartesians, 74204 getPanelRect: panelRectBuilder.grid, 74205 xAxisDeclared: xAxesHas[gridModel.id],
74206 yAxisDeclared: yAxesHas[gridModel.id] 74207 }); 74208 }); 74209 }, 74210 74211 geo: function (foundCpts, targetInfoList) { 74212 each$20(foundCpts.geoModels, function (geoModel) { 74213 var coordSys = geoModel.coordinateSystem; 74214 targetInfoList.push({ 74215 panelId: 'geo--' + geoModel.id, 74216 geoModel: geoModel, 74217 coordSysModel: geoModel, 74218 coordSys: coordSys, 74219 coordSyses: [coordSys], 74220 getPanelRect: panelRectBuilder.geo 74221 }); 74222 }); 74223 } 74224 }; 74225 74226 var targetInfoMatchers = [ 74227 74228 // grid 74229 function (foundCpts, targetInfo) { 74230 var xAxisModel = foundCpts.xAxisModel; 74231 var yAxisModel = foundCpts.yAxisModel; 74232 var gridModel = foundCpts.gridModel; 74233 74234 !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model); 74235 !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model); 74236 74237 return gridModel && gridModel === targetInfo.gridModel; 74238 }, 74239 74240 // geo 74241 function (foundCpts, targetInfo) { 74242 var geoModel = foundCpts.geoModel; 74243 return geoModel && geoModel === targetInfo.geoModel; 74244 } 74245 ]; 74246 74247 var panelRectBuilder = { 74248 74249 grid: function () { 74250 // grid is not Transformable. 74251 return this.coordSys.grid.getRect().clone(); 74252 }, 74253 74254 geo: function () { 74255 var coordSys = this.coordSys; 74256 var rect = coordSys.getBoundingRect().clone(); 74257 // geo roam and zoom transform 74258 rect.applyTransform(getTransform(coordSys)); 74259 return rect; 74260 } 74261 }; 74262 74263 var coordConvert = { 74264 74265 lineX: curry$5(axisConvert, 0), 74266 74267 lineY: curry$5(axisConvert, 1), 74268 74269 rect: function (to, coordSys, rangeOrCoordRange) { 74270 var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]); 74271 var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]); 74272 var values = [ 74273 formatMinMax([xminymin[0], xmaxymax[0]]), 74274 formatMinMax([xminymin[1], xmaxymax[1]]) 74275 ]; 74276 return {values: values, xyMinMax: values}; 74277 }, 74278 74279 polygon: function (to, coordSys, rangeOrCoordRange) { 74280 var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]]; 74281 var values = map(rangeOrCoordRange, function (item) { 74282 var p = coordSys[COORD_CONVERTS[to]](item); 74283 xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]); 74284 xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]); 74285 xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]); 74286 xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]); 74287 return p; 74288 }); 74289 return {values: values, xyMinMax: xyMinMax}; 74290 } 74291 }; 74292 74293 function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) { 74294 if (__DEV__) { 74295 assert$1( 74296 coordSys.type === 'cartesian2d', 74297 'lineX/lineY brush is available only in cartesian2d.' 74298 ); 74299 } 74300 74301 var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]); 74302 var values = formatMinMax(map([0, 1], function (i) { 74303 return to 74304 ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i])) 74305 : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i])); 74306 })); 74307 var xyMinMax = []; 74308 xyMinMax[axisNameIndex] = values; 74309 xyMinMax[1 - axisNameIndex] = [NaN, NaN]; 74310 74311 return {values: values, xyMinMax: xyMinMax}; 74312 } 74313 74314 var diffProcessor = { 74315 lineX: curry$5(axisDiffProcessor, 0), 74316 74317 lineY: curry$5(axisDiffProcessor, 1), 74318 74319 rect: function (values, refer, scales) { 74320 return [ 74321 [values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], 74322 [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]] 74323 ]; 74324 }, 74325 74326 polygon: function (values, refer, scales) { 74327 return map(values, function (item, idx) { 74328 return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]]; 74329 }); 74330 } 74331 }; 74332 74333 function axisDiffProcessor(axisNameIndex, values, refer, scales) { 74334 return [ 74335 values[0] - scales[axisNameIndex] * refer[0], 74336 values[1] - scales[axisNameIndex] * refer[1] 74337 ]; 74338 } 74339 74340// We have to process scale caused by dataZoom manually, 74341// although it might be not accurate. 74342 function getScales(xyMinMaxCurr, xyMinMaxOrigin) { 74343 var sizeCurr = getSize(xyMinMaxCurr); 74344 var sizeOrigin = getSize(xyMinMaxOrigin); 74345 var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];
74346 isNaN(scales[0]) && (scales[0] = 1); 74347 isNaN(scales[1]) && (scales[1] = 1); 74348 return scales; 74349 } 74350 74351 function getSize(xyMinMax) { 74352 return xyMinMax 74353 ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] 74354 : [NaN, NaN]; 74355 } 74356 74357 /* 74358* Licensed to the Apache Software Foundation (ASF) under one 74359* or more contributor license agreements. See the NOTICE file 74360* distributed with this work for additional information 74361* regarding copyright ownership. The ASF licenses this file 74362* to you under the Apache License, Version 2.0 (the 74363* "License"); you may not use this file except in compliance 74364* with the License. You may obtain a copy of the License at 74365* 74366* http://www.apache.org/licenses/LICENSE-2.0 74367* 74368* Unless required by applicable law or agreed to in writing, 74369* software distributed under the License is distributed on an 74370* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 74371* KIND, either express or implied. See the License for the 74372* specific language governing permissions and limitations 74373* under the License. 74374*/ 74375 74376 var STATE_LIST = ['inBrush', 'outOfBrush']; 74377 var DISPATCH_METHOD = '__ecBrushSelect'; 74378 var DISPATCH_FLAG = '__ecInBrushSelectEvent'; 74379 var PRIORITY_BRUSH = PRIORITY.VISUAL.BRUSH; 74380 74381 /** 74382 * Layout for visual, the priority higher than other layout, and before brush visual. 74383 */ 74384 registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) { 74385 ecModel.eachComponent({mainType: 'brush'}, function (brushModel) { 74386 74387 payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption( 74388 payload.key === 'brush' ? payload.brushOption : {brushType: false} 74389 ); 74390 74391 var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel); 74392 74393 brushTargetManager.setInputRanges(brushModel.areas, ecModel); 74394 }); 74395 }); 74396 74397 /** 74398 * Register the visual encoding if this modules required. 74399 */ 74400 registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) { 74401 74402 var brushSelected = []; 74403 var throttleType; 74404 var throttleDelay; 74405 74406 ecModel.eachComponent({mainType: 'brush'}, function (brushModel, brushIndex) { 74407 74408 var thisBrushSelected = { 74409 brushId: brushModel.id, 74410 brushIndex: brushIndex, 74411 brushName: brushModel.name, 74412 areas: clone(brushModel.areas), 74413 selected: [] 74414 }; 74415 // Every brush component exists in event params, convenient 74416 // for user to find by index. 74417 brushSelected.push(thisBrushSelected); 74418 74419 var brushOption = brushModel.option; 74420 var brushLink = brushOption.brushLink; 74421 var linkedSeriesMap = []; 74422 var selectedDataIndexForLink = []; 74423 var rangeInfoBySeries = []; 74424 var hasBrushExists = 0; 74425 74426 if (!brushIndex) { // Only the first throttle setting works. 74427 throttleType = brushOption.throttleType; 74428 throttleDelay = brushOption.throttleDelay; 74429 } 74430 74431 // Add boundingRect and selectors to range. 74432 var areas = map(brushModel.areas, function (area) { 74433 return bindSelector( 74434 defaults( 74435 {boundingRect: boundingRectBuilders[area.brushType](area)}, 74436 area 74437 ) 74438 ); 74439 }); 74440 74441 var visualMappings = createVisualMappings( 74442 brushModel.option, STATE_LIST, function (mappingOption) { 74443 mappingOption.mappingMethod = 'fixed'; 74444 } 74445 ); 74446 74447 isArray(brushLink) && each$1(brushLink, function (seriesIndex) { 74448 linkedSeriesMap[seriesIndex] = 1; 74449 }); 74450 74451 function linkOthers(seriesIndex) { 74452 return brushLink === 'all' || linkedSeriesMap[seriesIndex]; 74453 } 74454 74455 // If no supported brush or no brush on the series, 74456 // all visuals should be in original state. 74457 function brushed(rangeInfoList) { 74458 return !!rangeInfoList.length; 74459 } 74460 74461 /** 74462 * Logic for each series: (If the logic has to be modified one day, do it carefully!) 74463 * 74464 * ( brushed â⬠&& ââ¬hasBrushExist â⬠&& linkOthers ) =>
74464 StepA: ââ¬record, â⬠StepB: ââ¬visualByRecord. 74465 * !brushedââË Ã¢âÅhasBrushExist â⤠ââânothing,ââË Ã¢âÅvisualByRecord. 74466 * âââ!hasBrushExistââË Ã¢âânothing. 74467 * ( !brushed && ââ¬hasBrushExist â⬠&& linkOthers ) => StepA: nothing, StepB: ââ¬visualByRecord. 74468 * âââ!hasBrushExistââË Ã¢âânothing. 74469 * ( brushed â⬠&& !linkOthers ) => StepA: nothing, StepB: ââ¬visualByCheck. 74470 * !brushedââË Ã¢âânothing. 74471 * ( !brushed && !linkOthers ) => StepA: nothing, StepB: nothing. 74472 */ 74473 74474 // Step A 74475 ecModel.eachSeries(function (seriesModel, seriesIndex) { 74476 var rangeInfoList = rangeInfoBySeries[seriesIndex] = []; 74477 74478 seriesModel.subType === 'parallel' 74479 ? stepAParallel(seriesModel, seriesIndex, rangeInfoList) 74480 : stepAOthers(seriesModel, seriesIndex, rangeInfoList); 74481 }); 74482 74483 function stepAParallel(seriesModel, seriesIndex) { 74484 var coordSys = seriesModel.coordinateSystem; 74485 hasBrushExists |= coordSys.hasAxisBrushed(); 74486 74487 linkOthers(seriesIndex) && coordSys.eachActiveState( 74488 seriesModel.getData(), 74489 function (activeState, dataIndex) { 74490 activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1); 74491 } 74492 ); 74493 } 74494 74495 function stepAOthers(seriesModel, seriesIndex, rangeInfoList) { 74496 var selectorsByBrushType = getSelectorsByBrushType(seriesModel); 74497 if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) { 74498 return; 74499 } 74500 74501 each$1(areas, function (area) { 74502 selectorsByBrushType[area.brushType] 74503 && brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel) 74504 && rangeInfoList.push(area); 74505 hasBrushExists |= brushed(rangeInfoList); 74506 }); 74507 74508 if (linkOthers(seriesIndex) && brushed(rangeInfoList)) { 74509 var data = seriesModel.getData(); 74510 data.each(function (dataIndex) { 74511 if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) { 74512 selectedDataIndexForLink[dataIndex] = 1; 74513 } 74514 }); 74515 } 74516 } 74517 74518 // Step B 74519 ecModel.eachSeries(function (seriesModel, seriesIndex) { 74520 var seriesBrushSelected = { 74521 seriesId: seriesModel.id, 74522 seriesIndex: seriesIndex, 74523 seriesName: seriesModel.name, 74524 dataIndex: [] 74525 }; 74526 // Every series exists in event params, convenient 74527 // for user to find series by seriesIndex. 74528 thisBrushSelected.selected.push(seriesBrushSelected); 74529 74530 var selectorsByBrushType = getSelectorsByBrushType(seriesModel); 74531 var rangeInfoList = rangeInfoBySeries[seriesIndex]; 74532 74533 var data = seriesModel.getData(); 74534 var getValueState = linkOthers(seriesIndex) 74535 ? function (dataIndex) { 74536 return selectedDataIndexForLink[dataIndex] 74537 ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') 74538 : 'outOfBrush'; 74539 } 74540 : function (dataIndex) { 74541 return checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) 74542 ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') 74543 : 'outOfBrush'; 74544 }; 74545 74546 // If no supported brush or no brush, all visuals are in original state. 74547 (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) 74548 && applyVisual( 74549 STATE_LIST, visualMappings, data, getValueState 74550 ); 74551 }); 74552 74553 }); 74554 74555 dispatchAction(api, throttleType, throttleDelay, brushSelected, payload); 74556 });
74557 74558 function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) { 74559 // This event will not be triggered when `setOpion`, otherwise dead lock may 74560 // triggered when do `setOption` in event listener, which we do not find 74561 // satisfactory way to solve yet. Some considered resolutions: 74562 // (a) Diff with prevoius selected data ant only trigger event when changed. 74563 // But store previous data and diff precisely (i.e., not only by dataIndex, but 74564 // also detect value changes in selected data) might bring complexity or fragility. 74565 // (b) Use spectial param like `silent` to suppress event triggering. 74566 // But such kind of volatile param may be weird in `setOption`. 74567 if (!payload) { 74568 return; 74569 } 74570 74571 var zr = api.getZr(); 74572 if (zr[DISPATCH_FLAG]) { 74573 return; 74574 } 74575 74576 if (!zr[DISPATCH_METHOD]) { 74577 zr[DISPATCH_METHOD] = doDispatch; 74578 } 74579 74580 var fn = createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType); 74581 74582 fn(api, brushSelected); 74583 } 74584 74585 function doDispatch(api, brushSelected) { 74586 if (!api.isDisposed()) { 74587 var zr = api.getZr(); 74588 zr[DISPATCH_FLAG] = true; 74589 api.dispatchAction({ 74590 type: 'brushSelect', 74591 batch: brushSelected 74592 }); 74593 zr[DISPATCH_FLAG] = false; 74594 } 74595 } 74596 74597 function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) { 74598 for (var i = 0, len = rangeInfoList.length; i < len; i++) { 74599 var area = rangeInfoList[i]; 74600 if (selectorsByBrushType[area.brushType]( 74601 dataIndex, data, area.selectors, area 74602 )) { 74603 return true; 74604 } 74605 } 74606 } 74607 74608 function getSelectorsByBrushType(seriesModel) { 74609 var brushSelector = seriesModel.brushSelector; 74610 if (isString(brushSelector)) { 74611 var sels = []; 74612 each$1(selector, function (selectorsByElementType, brushType) { 74613 sels[brushType] = function (dataIndex, data, selectors, area) { 74614 var itemLayout = data.getItemLayout(dataIndex); 74615 return selectorsByElementType[brushSelector](itemLayout, selectors, area); 74616 }; 74617 }); 74618 return sels; 74619 } 74620 else if (isFunction$1(brushSelector)) { 74621 var bSelector = {}; 74622 each$1(selector, function (sel, brushType) {
74623 bSelector[brushType] = brushSelector; 74624 }); 74625 return bSelector; 74626 } 74627 return brushSelector; 74628 } 74629 74630 function brushModelNotControll(brushModel, seriesIndex) { 74631 var seriesIndices = brushModel.option.seriesIndex; 74632 return seriesIndices != null 74633 && seriesIndices !== 'all' 74634 && ( 74635 isArray(seriesIndices) 74636 ? indexOf(seriesIndices, seriesIndex) < 0 74637 : seriesIndex !== seriesIndices 74638 ); 74639 } 74640 74641 function bindSelector(area) { 74642 var selectors = area.selectors = {}; 74643 each$1(selector[area.brushType], function (selFn, elType) { 74644 // Do not use function binding or curry for performance. 74645 selectors[elType] = function (itemLayout) { 74646 return selFn(itemLayout, selectors, area); 74647 }; 74648 }); 74649 return area; 74650 } 74651 74652 var boundingRectBuilders = { 74653 74654 lineX: noop, 74655 74656 lineY: noop, 74657 74658 rect: function (area) { 74659 return getBoundingRectFromMinMax(area.range); 74660 }, 74661 74662 polygon: function (area) { 74663 var minMax; 74664 var range = area.range; 74665 74666 for (var i = 0, len = range.length; i < len; i++) { 74667 minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]]; 74668 var rg = range[i]; 74669 rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]); 74670 rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]); 74671 rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]); 74672 rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]); 74673 } 74674 74675 return minMax && getBoundingRectFromMinMax(minMax); 74676 } 74677 }; 74678 74679 function getBoundingRectFromMinMax(minMax) { 74680 return new BoundingRect( 74681 minMax[0][0], 74682 minMax[1][0], 74683 minMax[0][1] - minMax[0][0], 74684 minMax[1][1] - minMax[1][0] 74685 ); 74686 } 74687 74688 /* 74689* Licensed to the Apache Software Foundation (ASF) under one 74690* or more contributor license agreements. See the NOTICE file 74691* distributed with this work for additional information 74692* regarding copyright ownership. The ASF licenses this file 74693* to you under the Apache License, Version 2.0 (the 74694* "License"); you may not use this file except in compliance 74695* with the License. You may obtain a copy of the License at 74696* 74697* http://www.apache.org/licenses/LICENSE-2.0 74698* 74699* Unless required by applicable law or agreed to in writing, 74700* software distributed under the License is distributed on an 74701* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 74702* KIND, either express or implied. See the License for the 74703* specific language governing permissions and limitations 74704* under the License. 74705*/ 74706 74707 var DEFAULT_OUT_OF_BRUSH_COLOR = ['#ddd']; 74708 74709 var BrushModel = extendComponentModel({ 74710 74711 type: 'brush', 74712 74713 dependencies: ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'], 74714 74715 /** 74716 * @protected 74717 */ 74718 defaultOption: { 74719 // inBrush: null, 74720 // outOfBrush: null, 74721 toolbox: null, // Default value see preprocessor. 74722 brushLink: null, // Series indices array, broadcast using dataIndex. 74723 // or 'all', which means all series. 'none' or null means no series. 74724 seriesIndex: 'all', // seriesIndex array, specify series controlled by this brush component. 74725 geoIndex: null, // 74726 xAxisIndex: null, 74727 yAxisIndex: null, 74728 74729 brushType: 'rect', // Default brushType, see BrushController. 74730 brushMode: 'single', // Default brushMode, 'single' or 'multiple' 74731 transformable: true, // Default transformable. 74732 brushStyle: { // Default brushStyle 74733 borderWidth: 1, 74734 color: 'rgba(120,140,180,0.3)', 74735 borderColor: 'rgba(120,140,180,0.8)' 74736 }, 74737 74738 throttleType: 'fixRate',// Throttle in brushSelected event. 'fixRate' or 'debounce'. 74739 // If null, no throttle. Valid only in the first brush component 74740 throttleDelay: 0, // Unit: ms, 0 means every event will be triggered. 74741 74742 // FIXME 74743 // è¯â¢Ã©ÂªÅæâ¢Ë枊74744 removeOnClick: true, 74745 74746 z: 10000 74747 }, 74748 74749 /** 74750 * @readOnly 74751 * @type {Array.<Object>} 74752 */ 74753 areas: [], 74754 74755 /** 74756 * Current activated brush type. 74757 * If null, brush is inactived. 74758 * see module:echarts/component/helper/BrushController 74759 * @readOnly 74760 * @type {string} 74761 */ 74762 brushType: null, 74763 74764 /** 74765 * Current brush opt. 74766 * see module:echarts/component/helper/BrushController 74767 * @readOnly 74768 * @type {Object} 74769 */ 74770 brushOption: {}, 74771 74772 /** 74773 * @readOnly 74774 * @type {Array.<Object>} 74775 */ 74776 coordInfoList: [], 74777 74778 optionUpdated: function (newOption, isInit) { 74779 var thisOption = this.option; 74780 74781 !isInit && replaceVisualOption( 74782 thisOption, newOption, ['inBrush', 'outOfBrush'] 74783 ); 74784 74785 var inBrush = thisOption.inBrush = thisOption.inBrush || {}; 74786 // Always give default visual, consider setOption at the second time. 74787 thisOption.outOfBrush = thisOption.outOfBrush || {color: DEFAULT_OUT_OF_BRUSH_COLOR}; 74788 74789 if (!inBrush.hasOwnProperty('liftZ')) { 74790 // Bigger than the highlight z lift, otherwise it will 74791 // be effected by the highlight z when brush. 74792 inBrush.liftZ = 5; 74793 } 74794 }, 74795 74796 /** 74797 * If ranges is null/undefined, range state remain. 74798 * 74799 * @param {Array.<Object>} [ranges] 74800 */ 74801 setAreas: function (areas) { 74802 if (__DEV__) { 74803 assert$1(isArray(areas)); 74804 each$1(areas, function (area) { 74805 assert$1(area.brushType, 'Illegal areas'); 74806 }); 74807 } 74808 74809 // If ranges is null/undefined, range state remain. 74810 // This helps user to dispatchAction({type: 'brush'}) with no areas 74811 // set but just want to get the current brush select info from a `brush` event. 74812 if (!areas) { 74813 return; 74814 } 74815 74816 this.areas = map(areas, function (area) { 74817 return generateBrushOption(this.option, area); 74818 }, this); 74819 }, 74820 74821 /** 74822 * see module:echarts/component/helper/BrushController 74823 * @param {Object} brushOption 74824 */ 74825 setBrushOption: function (brushOption) { 74826 this.brushOption = generateBrushOption(this.option, brushOption); 74827 this.brushType = this.brushOption.brushType; 74828 } 74829 74830 });
74831 74832 function generateBrushOption(option, brushOption) { 74833 return merge( 74834 { 74835 brushType: option.brushType, 74836 brushMode: option.brushMode, 74837 transformable: option.transformable, 74838 brushStyle: new Model(option.brushStyle).getItemStyle(), 74839 removeOnClick: option.removeOnClick, 74840 z: option.z 74841 }, 74842 brushOption, 74843 true 74844 ); 74845 } 74846 74847 /* 74848* Licensed to the Apache Software Foundation (ASF) under one 74849* or more contributor license agreements. See the NOTICE file 74850* distributed with this work for additional information 74851* regarding copyright ownership. The ASF licenses this file 74852* to you under the Apache License, Version 2.0 (the 74853* "License"); you may not use this file except in compliance 74854* with the License. You may obtain a copy of the License at 74855* 74856* http://www.apache.org/licenses/LICENSE-2.0 74857* 74858* Unless required by applicable law or agreed to in writing, 74859* software distributed under the License is distributed on an 74860* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 74861* KIND, either express or implied. See the License for the 74862* specific language governing permissions and limitations 74863* under the License. 74864*/ 74865 74866 extendComponentView({ 74867 74868 type: 'brush', 74869 74870 init: function (ecModel, api) { 74871 74872 /** 74873 * @readOnly 74874 * @type {module:echarts/model/Global} 74875 */ 74876 this.ecModel = ecModel; 74877 74878 /** 74879 * @readOnly 74880 * @type {module:echarts/ExtensionAPI} 74881 */ 74882 this.api = api; 74883 74884 /** 74885 * @readOnly
74886 * @type {module:echarts/component/brush/BrushModel} 74887 */ 74888 this.model; 74889 74890 /** 74891 * @private 74892 * @type {module:echarts/component/helper/BrushController} 74893 */ 74894 (this._brushController = new BrushController(api.getZr())) 74895 .on('brush', bind(this._onBrush, this)) 74896 .mount(); 74897 }, 74898 74899 /** 74900 * @override 74901 */ 74902 render: function (brushModel) { 74903 this.model = brushModel; 74904 return updateController.apply(this, arguments); 74905 }, 74906 74907 /** 74908 * @override 74909 */ 74910 updateTransform: updateController, 74911 74912 /** 74913 * @override 74914 */ 74915 updateView: updateController, 74916 74917 // /** 74918 // * @override 74919 // */ 74920 // updateLayout: updateController, 74921 74922 // /** 74923 // * @override 74924 // */ 74925 // updateVisual: updateController, 74926 74927 /** 74928 * @override 74929 */ 74930 dispose: function () { 74931 this._brushController.dispose(); 74932 }, 74933 74934 /** 74935 * @private 74936 */ 74937 _onBrush: function (areas, opt) { 74938 var modelId = this.model.id; 74939 74940 this.model.brushTargetManager.setOutputRanges(areas, this.ecModel); 74941 74942 // Action is not dispatched on drag end, because the drag end 74943 // emits the same params with the last drag move event, and 74944 // may have some delay when using touch pad, which makes 74945 // animation not smooth (when using debounce). 74946 (!opt.isEnd || opt.removeOnClick) && this.api.dispatchAction({ 74947 type: 'brush', 74948 brushId: modelId, 74949 areas: clone(areas), 74950 $from: modelId 74951 }); 74952 } 74953 74954 }); 74955 74956 function updateController(brushModel, ecModel, api, payload) { 74957 // Do not update controller when drawing. 74958 (!payload || payload.$from !== brushModel.id) && this._brushController 74959 .setPanels(brushModel.brushTargetManager.makePanelOpts(api)) 74960 .enableBrush(brushModel.brushOption) 74961 .updateCovers(brushModel.areas.slice()); 74962 } 74963 74964 /* 74965* Licensed to the Apache Software Foundation (ASF) under one 74966* or more contributor license agreements. See the NOTICE file 74967* distributed with this work for additional information 74968* regarding copyright ownership. The ASF licenses this file 74969* to you under the Apache License, Version 2.0 (the 74970* "License"); you may not use this file except in compliance 74971* with the License. You may obtain a copy of the License at 74972* 74973* http://www.apache.org/licenses/LICENSE-2.0 74974* 74975* Unless required by applicable law or agreed to in writing, 74976* software distributed under the License is distributed on an 74977* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 74978* KIND, either express or implied. See the License for the 74979* specific language governing permissions and limitations 74980* under the License. 74981*/ 74982 74983 /** 74984 * payload: { 74985 * brushIndex: number, or, 74986 * brushId: string, or, 74987 * brushName: string, 74988 * globalRanges: Array 74989 * } 74990 */ 74991 registerAction( 74992 {type: 'brush', event: 'brush' /*, update: 'updateView' */}, 74993 function (payload, ecModel) { 74994 ecModel.eachComponent({mainType: 'brush', query: payload}, function (brushModel) { 74995 brushModel.setAreas(payload.areas); 74996 }); 74997 } 74998 ); 74999 75000 /** 75001 * payload: { 75002 * brushComponents: [ 75003 * { 75004 * brushId, 75005 * brushIndex, 75006 * brushName, 75007 * series: [ 75008 * { 75009 * seriesId, 75010 * seriesIndex, 75011 * seriesName, 75012 * rawIndices: [21, 34, ...] 75013 * }, 75014 * ... 75015 * ] 75016 * }, 75017 * ... 75018 * ] 75019 * } 75020 */ 75021 registerAction( 75022 {type: 'brushSelect', event: 'brushSelected', update: 'none'}, 75023 function () {} 75024 ); 75025 75026 /* 75027* Licensed to the Apache Software Foundation (ASF) under one 75028* or more contributor license agreements. See the NOTICE file 75029* distributed with this work for additional information 75030* regarding copyright ownership. The ASF licenses this file 75031* to you under the Apache License, Version 2.0 (the 75032* "License");
75032 you may not use this file except in compliance 75033* with the License. You may obtain a copy of the License at 75034* 75035* http://www.apache.org/licenses/LICENSE-2.0 75036* 75037* Unless required by applicable law or agreed to in writing, 75038* software distributed under the License is distributed on an 75039* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 75040* KIND, either express or implied. See the License for the 75041* specific language governing permissions and limitations 75042* under the License. 75043*/ 75044 75045 75046 var features = {}; 75047 75048 function register$1(name, ctor) { 75049 features[name] = ctor; 75050 } 75051 75052 function get$1(name) { 75053 return features[name]; 75054 } 75055 75056 /* 75057* Licensed to the Apache Software Foundation (ASF) under one 75058* or more contributor license agreements. See the NOTICE file 75059* distributed with this work for additional information 75060* regarding copyright ownership. The ASF licenses this file 75061* to you under the Apache License, Version 2.0 (the 75062* "License"); you may not use this file except in compliance 75063* with the License. You may obtain a copy of the License at 75064* 75065* http://www.apache.org/licenses/LICENSE-2.0 75066* 75067* Unless required by applicable law or agreed to in writing, 75068* software distributed under the License is distributed on an 75069* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 75070* KIND, either express or implied. See the License for the 75071* specific language governing permissions and limitations 75072* under the License. 75073*/ 75074 75075 var brushLang = lang.toolbox.brush; 75076 75077 function Brush(model, ecModel, api) { 75078 this.model = model; 75079 this.ecModel = ecModel; 75080 this.api = api; 75081 75082 /** 75083 * @private 75084 * @type {string} 75085 */ 75086 this._brushType; 75087 75088 /** 75089 * @private 75090 * @type {string} 75091 */ 75092 this._brushMode; 75093 } 75094 75095 Brush.defaultOption = { 75096 show: true, 75097 type: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'], 75098 icon: { 75099 rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13', // jshint ignore:line 75100 polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2', // jshint ignore:line 75101 lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4', // jshint ignore:line 75102 lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4', // jshint ignore:line 75103 keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z', // jshint ignore:line 75104 clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line 75105 }, 75106 // `rect`, `polygon`, `lineX`, `lineY`, `keep`, `clear` 75107 title: clone(brushLang.title) 75108 }; 75109 75110 var proto$3 = Brush.prototype; 75111 75112// proto.updateLayout = function (featureModel, ecModel, api) { 75113 proto$3.render = 75114 proto$3.updateView = function (featureModel, ecModel, api) { 75115 var brushType;
75116 var brushMode; 75117 var isBrushed; 75118 75119 ecModel.eachComponent({mainType: 'brush'}, function (brushModel) { 75120 brushType = brushModel.brushType; 75121 brushMode = brushModel.brushOption.brushMode || 'single'; 75122 isBrushed |= brushModel.areas.length; 75123 }); 75124 this._brushType = brushType; 75125 this._brushMode = brushMode; 75126 75127 each$1(featureModel.get('type', true), function (type) { 75128 featureModel.setIconStatus( 75129 type, 75130 ( 75131 type === 'keep' 75132 ? brushMode === 'multiple' 75133 : type === 'clear' 75134 ? isBrushed 75135 : type === brushType 75136 ) ? 'emphasis' : 'normal' 75137 ); 75138 }); 75139 }; 75140 75141 proto$3.getIcons = function () { 75142 var model = this.model; 75143 var availableIcons = model.get('icon', true); 75144 var icons = {}; 75145 each$1(model.get('type', true), function (type) { 75146 if (availableIcons[type]) { 75147 icons[type] = availableIcons[type]; 75148 } 75149 }); 75150 return icons; 75151 }; 75152 75153 proto$3.onclick = function (ecModel, api, type) { 75154 var brushType = this._brushType; 75155 var brushMode = this._brushMode; 75156 75157 if (type === 'clear') { 75158 // Trigger parallel action firstly 75159 api.dispatchAction({ 75160 type: 'axisAreaSelect', 75161 intervals: [] 75162 }); 75163 75164 api.dispatchAction({ 75165 type: 'brush', 75166 command: 'clear', 75167 // Clear all areas of all brush components. 75168 areas: [] 75169 }); 75170 } 75171 else { 75172 api.dispatchAction({ 75173 type: 'takeGlobalCursor', 75174 key: 'brush', 75175 brushOption: { 75176 brushType: type === 'keep' 75177 ? brushType 75178 : (brushType === type ? false : type), 75179 brushMode: type === 'keep' 75180 ? (brushMode === 'multiple' ? 'single' : 'multiple') 75181 : brushMode 75182 } 75183 }); 75184 } 75185 }; 75186 75187 register$1('brush', Brush); 75188 75189 /* 75190* Licensed to the Apache Software Foundation (ASF) under one 75191* or more contributor license agreements. See the NOTICE file 75192* distributed with this work for additional information 75193* regarding copyright ownership. The ASF licenses this file 75194* to you under the Apache License, Version 2.0 (the 75195* "License"); you may not use this file except in compliance 75196* with the License. You may obtain a copy of the License at 75197* 75198* http://www.apache.org/licenses/LICENSE-2.0 75199* 75200* Unless required by applicable law or agreed to in writing, 75201* software distributed under the License is distributed on an 75202* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 75203* KIND, either express or implied. See the License for the 75204* specific language governing permissions and limitations 75205* under the License. 75206*/ 75207 75208 /** 75209 * Brush component entry 75210 */ 75211 75212 registerPreprocessor(preprocessor$1); 75213 75214 /* 75215* Licensed to the Apache Software Foundation (ASF) under one 75216* or more contributor license agreements. See the NOTICE file 75217* distributed with this work for additional information 75218* regarding copyright ownership. The ASF licenses this file 75219* to you under the Apache License, Version 2.0 (the 75220* "License"); you may not use this file except in compliance 75221* with the License. You may obtain a copy of the License at 75222* 75223* http://www.apache.org/licenses/LICENSE-2.0 75224* 75225* Unless required by applicable law or agreed to in writing, 75226* software distributed under the License is distributed on an 75227* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 75228* KIND, either express or implied. See the License for the 75229* specific language governing permissions and limitations 75230* under the License. 75231*/ 75232 75233// (24*60*60*1000) 75234 var PROXIMATE_ONE_DAY = 86400000; 75235 75236 /** 75237 * Calendar 75238 * 75239 * @constructor 75240 * 75241 * @param {Object} calendarModel calendarModel 75242 * @param {Object} ecModel ecModel 75243 * @param {Object} api api 75244 */ 75245 function Calendar(calendarModel, ecModel, api) { 75246 this._model = calendarModel; 75247 } 75248 75249 Calendar.prototype = { 75250 75251 constructor: Calendar, 75252 75253 type: 'calendar', 75254 75255 dimensions: ['time', 'value'], 75256 75257 // Required in createListFromData 75258 getDimensionsInfo: function () { 75259 return [{name: 'time', type: 'time'}, 'value']; 75260 }, 75261 75262 getRangeInfo: function () { 75263 return this._rangeInfo; 75264 }, 75265 75266 getModel: function () { 75267 return this._model; 75268 }, 75269 75270 getRect: function () { 75271 return this._rect; 75272 }, 75273 75274 getCellWidth: function () { 75275 return this._sw; 75276 }, 75277 75278 getCellHeight: function () { 75279 return this._sh; 75280 }, 75281 75282 getOrient: function () { 75283 return this._orient; 75284 }, 75285 75286 /** 75287 * getFirstDayOfWeek 75288 * 75289 * @example 75290 * 0 : start at Sunday 75291 * 1 : start at Monday 75292 * 75293 * @return {number} 75294 */ 75295 getFirstDayOfWeek: function () { 75296 return this._firstDayOfWeek; 75297 }, 75298 75299 /** 75300 * get date info 75301 * 75302 * @param {string|number} date date 75303 * @return {Object} 75304 * { 75305 * y: string, local full year, eg., '1940', 75306 * m: string, local month, from '01' ot '12', 75307 * d: string, local date, from '01' to '31' (if exists), 75308 * day: It is not date.getDay(). It is the location of the cell in a week, from 0 to 6, 75309 * time: timestamp, 75310 * formatedDate: string, yyyy-MM-dd, 75311 * date: original date object. 75312 * } 75313 */ 75314 getDateInfo: function (date) { 75315 75316 date = parseDate(date); 75317 75318 var y = date.getFullYear(); 75319 75320 var m = date.getMonth() + 1; 75321 m = m < 10 ? '0' + m : m; 75322 75323 var d = date.getDate(); 75324 d = d < 10 ? '0' + d : d; 75325 75326 var day = date.getDay(); 75327 75328 day = Math.abs((day + 7 - this.getFirstDayOfWeek()) % 7); 75329 75330 return { 75331 y: y, 75332 m: m, 75333 d: d, 75334 day: day, 75335 time: date.getTime(), 75336 formatedDate: y + '-' + m + '-' + d, 75337 date: date 75338 }; 75339 }, 75340 75341 getNextNDay: function (date, n) { 75342 n = n || 0; 75343 if (n === 0) { 75344 return this.getDateInfo(date); 75345 } 75346 75347 date = new Date(this.getDateInfo(date).time); 75348 date.setDate(date.getDate() + n); 75349 75350 return this.getDateInfo(date); 75351 }, 75352 75353 update: function (ecModel, api) { 75354 75355 this._firstDayOfWeek = +this._model.getModel('dayLabel').get('firstDay'); 75356 this._orient = this._model.get('orient'); 75357 this._lineWidth = this._model.getModel('itemStyle').getItemStyle().lineWidth || 0; 75358 75359 75360 this._rangeInfo = this._getRangeInfo(this._initRangeOption()); 75361 var weeks = this._rangeInfo.weeks || 1;
75362 var whNames = ['width', 'height']; 75363 var cellSize = this._model.get('cellSize').slice(); 75364 var layoutParams = this._model.getBoxLayoutParams(); 75365 var cellNumbers = this._orient === 'horizontal' ? [weeks, 7] : [7, weeks]; 75366 75367 each$1([0, 1], function (idx) { 75368 if (cellSizeSpecified(cellSize, idx)) { 75369 layoutParams[whNames[idx]] = cellSize[idx] * cellNumbers[idx]; 75370 } 75371 }); 75372 75373 var whGlobal = { 75374 width: api.getWidth(), 75375 height: api.getHeight() 75376 }; 75377 var calendarRect = this._rect = getLayoutRect(layoutParams, whGlobal); 75378 75379 each$1([0, 1], function (idx) { 75380 if (!cellSizeSpecified(cellSize, idx)) { 75381 cellSize[idx] = calendarRect[whNames[idx]] / cellNumbers[idx]; 75382 } 75383 }); 75384 75385 function cellSizeSpecified(cellSize, idx) { 75386 return cellSize[idx] != null && cellSize[idx] !== 'auto'; 75387 } 75388 75389 this._sw = cellSize[0]; 75390 this._sh = cellSize[1]; 75391 }, 75392 75393 75394 /** 75395 * Convert a time data(time, value) item to (x, y) point. 75396 * 75397 * @override 75398 * @param {Array|number} data data 75399 * @param {boolean} [clamp=true] out of range 75400 * @return {Array} point 75401 */ 75402 dataToPoint: function (data, clamp) { 75403 isArray(data) && (data = data[0]); 75404 clamp == null && (clamp = true); 75405 75406 var dayInfo = this.getDateInfo(data); 75407 var range = this._rangeInfo; 75408 var date = dayInfo.formatedDate; 75409 75410 // if not in range return [NaN, NaN] 75411 if (clamp && !( 75412 dayInfo.time >= range.start.time 75413 && dayInfo.time < range.end.time + PROXIMATE_ONE_DAY 75414 )) { 75415 return [NaN, NaN]; 75416 } 75417 75418 var week = dayInfo.day; 75419 var nthWeek = this._getRangeInfo([range.start.time, date]).nthWeek; 75420 75421 if (this._orient === 'vertical') { 75422 return [ 75423 this._rect.x + week * this._sw + this._sw / 2, 75424 this._rect.y + nthWeek * this._sh + this._sh / 2 75425 ]; 75426 75427 } 75428 75429 return [ 75430 this._rect.x + nthWeek * this._sw + this._sw / 2, 75431 this._rect.y + week * this._sh + this._sh / 2 75432 ]; 75433 75434 }, 75435 75436 /** 75437 * Convert a (x, y) point to time data 75438 * 75439 * @override 75440 * @param {string} point point 75441 * @return {string} data 75442 */ 75443 pointToData: function (point) { 75444 75445 var date = this.pointToDate(point); 75446 75447 return date && date.time; 75448 }, 75449 75450 /** 75451 * Convert a time date item to (x, y) four point. 75452 * 75453 * @param {Array} data date[0] is date 75454 * @param {boolean} [clamp=true] out of range 75455 * @return {Object} point 75456 */ 75457 dataToRect: function (data, clamp) { 75458 var point = this.dataToPoint(data, clamp); 75459 75460 return { 75461 contentShape: { 75462 x: point[0] - (this._sw - this._lineWidth) / 2, 75463 y: point[1] - (this._sh - this._lineWidth) / 2, 75464 width: this._sw - this._lineWidth, 75465 height: this._sh - this._lineWidth 75466 }, 75467 75468 center: point, 75469 75470 tl: [ 75471 point[0] - this._sw / 2, 75472 point[1] - this._sh / 2 75473 ], 75474 75475 tr: [ 75476 point[0] + this._sw / 2, 75477 point[1] - this._sh / 2 75478 ], 75479 75480 br: [ 75481 point[0] + this._sw / 2, 75482 point[1] + this._sh / 2 75483 ], 75484 75485 bl: [ 75486 point[0] - this._sw / 2, 75487 point[1] + this._sh / 2 75488 ] 75489 75490 }; 75491 }, 75492 75493 /** 75494 * Convert a (x, y) point to time date 75495 * 75496 * @param {Array} point point 75497 * @return {Object} date 75498 */ 75499 pointToDate: function (point) { 75500 var nthX = Math.floor((point[0] - this._rect.x) / this._sw) + 1; 75501 var nthY = Math.floor((point[1] - this._rect.y) / this._sh) + 1; 75502 var range = this._rangeInfo.range; 75503 75504 if (this._orient === 'vertical') { 75505 return this._getDateByWeeksAndDay(nthY, nthX - 1, range); 75506 } 75507 75508 return this._getDateByWeeksAndDay(nthX, nthY - 1, range); 75509 }, 75510 75511 /** 75512 * @inheritDoc 75513 */ 75514 convertToPixel: curry(doConvert$2, 'dataToPoint'), 75515 75516 /** 75517 * @inheritDoc 75518 */ 75519 convertFromPixel: curry(doConvert$2, 'pointToData'), 75520 75521 /** 75522 * initRange 75523 * 75524 * @private 75525 * @return {Array} [start, end] 75526 */ 75527 _initRangeOption: function () { 75528 var range = this._model.get('range'); 75529 75530 var rg = range; 75531 75532 if (isArray(rg) && rg.length === 1) { 75533 rg = rg[0]; 75534 } 75535 75536 if (/^\d{4}$/.test(rg)) { 75537 range = [rg + '-01-01', rg + '-12-31']; 75538 } 75539 75540 if (/^\d{4}[\/|-]\d{1,2}$/.test(rg)) { 75541 75542 var start = this.getDateInfo(rg); 75543 var firstDay = start.date; 75544 firstDay.setMonth(firstDay.getMonth() + 1); 75545 75546 var end = this.getNextNDay(firstDay, -1); 75547 range = [start.formatedDate, end.formatedDate]; 75548 } 75549 75550 if (/^\d{4}[\/|-]\d{1,2}[\/|-]\d{1,2}$/.test(rg)) { 75551 range = [rg, rg]; 75552 } 75553 75554 var tmp = this._getRangeInfo(range); 75555 75556 if (tmp.start.time > tmp.end.time) { 75557 range.reverse(); 75558 } 75559 75560 return range; 75561 }, 75562 75563 /** 75564 * range info 75565 * 75566 * @private 75567 * @param {Array} range range ['2017-01-01', '2017-07-08'] 75568 * If range[0] > range[1], they will not be reversed. 75569 * @return {Object} obj 75570 */ 75571 _getRangeInfo: function (range) { 75572 range = [ 75573 this.getDateInfo(range[0]), 75574 this.getDateInfo(range[1]) 75575 ]; 75576 75577 var reversed; 75578 if (range[0].time > range[1].time) { 75579 reversed = true; 75580 range.reverse(); 75581 } 75582 75583 var allDay = Math.floor(range[1].time / PROXIMATE_ONE_DAY) 75584 - Math.floor(range[0].time / PROXIMATE_ONE_DAY) + 1; 75585 75586 // Consider case: 75587 // Firstly set system timezone as "Time Zone: America/Toronto", 75588 // ``` 75589 // var first = new Date(1478412000000 - 3600 * 1000 * 2.5); 75590 // var second = new Date(1478412000000); 75591 // var allDays = Math.floor(second / ONE_DAY) - Math.floor(first / ONE_DAY) + 1; 75592 // ``` 75593 // will get wrong result because of DST. So we should fix it. 75594 var date = new Date(range[0].time); 75595 var startDateNum = date.getDate(); 75596 var endDateNum = range[1].date.getDate(); 75597 date.setDate(startDateNum + allDay - 1); 75598 // The bias can not over a month, so just compare date. 75599 if (date.getDate() !== endDateNum) { 75600 var sign = date.getTime() - range[1].time > 0 ? 1 : -1; 75601 while (date.getDate() !== endDateNum && (date.getTime() - range[1].time) * sign > 0) { 75602 allDay -= sign; 75603 date.setDate(startDateNum + allDay - 1); 75604 } 75605 } 75606 75607 var weeks = Math.floor((allDay + range[0].day + 6) / 7); 75608 var nthWeek = reversed ? -weeks + 1: weeks - 1; 75609 75610 reversed && range.reverse(); 75611 75612 return { 75613 range: [range[0].formatedDate, range[1].formatedDate], 75614 start: range[0], 75615 end: range[1], 75616 allDay: allDay, 75617 weeks: weeks, 75618 // From 0. 75619 nthWeek: nthWeek, 75620 fweek: range[0].day, 75621 lweek: range[1].day 75622 }; 75623 }, 75624 75625 /** 75626 * get date by nthWeeks and week day in range 75627 * 75628 * @private 75629 * @param {number} nthWeek the week 75630 * @param {number} day the week day 75631 * @param {Array} range [d1, d2] 75632 * @return {Object} 75633 */ 75634 _getDateByWeeksAndDay: function (nthWeek, day, range) { 75635 var rangeInfo = this._getRangeInfo(range); 75636 75637 if (nthWeek > rangeInfo.weeks 75638 || (nthWeek === 0 && day < rangeInfo.fweek) 75639 || (nthWeek === rangeInfo.weeks && day > rangeInfo.lweek) 75640 ) { 75641 return false; 75642 } 75643 75644 var nthDay = (nthWeek - 1) * 7 - rangeInfo.fweek + day; 75645 var date = new Date(rangeInfo.start.time); 75646 date.setDate(rangeInfo.start.d + nthDay); 75647 75648 return this.getDateInfo(date); 75649 } 75650 }; 75651 75652 Calendar.dimensions = Calendar.prototype.dimensions; 75653
75654 Calendar.getDimensionsInfo = Calendar.prototype.getDimensionsInfo; 75655 75656 Calendar.create = function (ecModel, api) { 75657 var calendarList = []; 75658 75659 ecModel.eachComponent('calendar', function (calendarModel) { 75660 var calendar = new Calendar(calendarModel, ecModel, api); 75661 calendarList.push(calendar); 75662 calendarModel.coordinateSystem = calendar; 75663 }); 75664 75665 ecModel.eachSeries(function (calendarSeries) { 75666 if (calendarSeries.get('coordinateSystem') === 'calendar') { 75667 // Inject coordinate system 75668 calendarSeries.coordinateSystem = calendarList[calendarSeries.get('calendarIndex') || 0]; 75669 } 75670 }); 75671 return calendarList; 75672 }; 75673 75674 function doConvert$2(methodName, ecModel, finder, value) { 75675 var calendarModel = finder.calendarModel; 75676 var seriesModel = finder.seriesModel; 75677 75678 var coordSys = calendarModel 75679 ? calendarModel.coordinateSystem 75680 : seriesModel 75681 ? seriesModel.coordinateSystem 75682 : null; 75683 75684 return coordSys === this ? coordSys[methodName](value) : null; 75685 } 75686 75687 CoordinateSystemManager.register('calendar', Calendar); 75688 75689 /* 75690* Licensed to the Apache Software Foundation (ASF) under one 75691* or more contributor license agreements. See the NOTICE file 75692* distributed with this work for additional information 75693* regarding copyright ownership. The ASF licenses this file 75694* to you under the Apache License, Version 2.0 (the 75695* "License"); you may not use this file except in compliance 75696* with the License. You may obtain a copy of the License at 75697* 75698* http://www.apache.org/licenses/LICENSE-2.0 75699* 75700* Unless required by applicable law or agreed to in writing, 75701* software distributed under the License is distributed on an 75702* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 75703* KIND, either express or implied. See the License for the 75704* specific language governing permissions and limitations 75705* under the License. 75706*/ 75707 75708 var CalendarModel = ComponentModel.extend({ 75709 75710 type: 'calendar', 75711 75712 /** 75713 * @type {module:echarts/coord/calendar/Calendar} 75714 */ 75715 coordinateSystem: null, 75716 75717 defaultOption: { 75718 zlevel: 0, 75719 z: 2, 75720 left: 80, 75721 top: 60, 75722 75723 cellSize: 20, 75724 75725 // horizontal vertical 75726 orient: 'horizontal', 75727 75728 // month separate line style 75729 splitLine: { 75730 show: true, 75731 lineStyle: { 75732 color: '#000', 75733 width: 1, 75734 type: 'solid' 75735 } 75736 }, 75737 75738 // rect style temporarily unused emphasis 75739 itemStyle: { 75740 color: '#fff', 75741 borderWidth: 1, 75742 borderColor: '#ccc' 75743 }, 75744 75745 // week text style 75746 dayLabel: { 75747 show: true, 75748 75749 // a week first day 75750 firstDay: 0, 75751 75752 // start end 75753 position: 'start', 75754 margin: '50%', // 50% of cellSize 75755 nameMap: 'en', 75756 color: '#000' 75757 }, 75758 75759 // month text style 75760 monthLabel: { 75761 show: true, 75762 75763 // start end 75764 position: 'start', 75765 margin: 5, 75766 75767 // center or left 75768 align: 'center', 75769 75770 // cn en [] 75771 nameMap: 'en', 75772 formatter: null, 75773 color: '#000' 75774 }, 75775 75776 // year text style 75777 yearLabel: { 75778 show: true, 75779 75780 // top bottom left right 75781 position: null, 75782 margin: 30, 75783 formatter: null, 75784 color: '#ccc', 75785 fontFamily: 'sans-serif', 75786 fontWeight: 'bolder', 75787 fontSize: 20 75788 } 75789 }, 75790 75791 /** 75792 * @override 75793 */ 75794 init: function (option, parentModel, ecModel, extraOpt) { 75795 var inputPositionParams = getLayoutParams(option); 75796 75797 CalendarModel.superApply(this, 'init', arguments); 75798
75799 mergeAndNormalizeLayoutParams$1(option, inputPositionParams); 75800 }, 75801 75802 /** 75803 * @override 75804 */ 75805 mergeOption: function (option, extraOpt) { 75806 CalendarModel.superApply(this, 'mergeOption', arguments); 75807 75808 mergeAndNormalizeLayoutParams$1(this.option, option); 75809 } 75810 }); 75811 75812 function mergeAndNormalizeLayoutParams$1(target, raw) { 75813 // Normalize cellSize 75814 var cellSize = target.cellSize; 75815 75816 if (!isArray(cellSize)) { 75817 cellSize = target.cellSize = [cellSize, cellSize]; 75818 } 75819 else if (cellSize.length === 1) { 75820 cellSize[1] = cellSize[0]; 75821 } 75822 75823 var ignoreSize = map([0, 1], function (hvIdx) { 75824 // If user have set `width` or both `left` and `right`, cellSize 75825 // will be automatically set to 'auto', otherwise the default 75826 // setting of cellSize will make `width` setting not work. 75827 if (sizeCalculable(raw, hvIdx)) { 75828 cellSize[hvIdx] = 'auto'; 75829 } 75830 return cellSize[hvIdx] != null && cellSize[hvIdx] !== 'auto'; 75831 }); 75832 75833 mergeLayoutParam(target, raw, { 75834 type: 'box', ignoreSize: ignoreSize 75835 }); 75836 } 75837 75838 /* 75839* Licensed to the Apache Software Foundation (ASF) under one 75840* or more contributor license agreements. See the NOTICE file 75841* distributed with this work for additional information 75842* regarding copyright ownership. The ASF licenses this file 75843* to you under the Apache License, Version 2.0 (the 75844* "License"); you may not use this file except in compliance 75845* with the License. You may obtain a copy of the License at 75846* 75847* http://www.apache.org/licenses/LICENSE-2.0 75848* 75849* Unless required by applicable law or agreed to in writing, 75850* software distributed under the License is distributed on an 75851* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 75852* KIND, either express or implied. See the License for the 75853* specific language governing permissions and limitations 75854* under the License. 75855*/ 75856 75857 var MONTH_TEXT = { 75858 EN: [ 75859 'Jan', 'Feb', 'Mar', 75860 'Apr', 'May', 'Jun', 75861 'Jul', 'Aug', 'Sep', 75862 'Oct', 'Nov', 'Dec' 75863 ], 75864 CN: [ 75865 'ä¸â¬Ã¦ÅË', 'äºÅæÅË', 'ä¸â°Ã¦ÅË', 75866 'Ã¥âºâºÃ¦ÅË', 'äºâæÅË', 'Ã¥â¦ÂæÅË', 75867 'ä¸ÆÃ¦ÅË', 'Ã¥â¦Â«Ã¦ÅË', 'ä¹ÂæÅË', 75868 'Ã¥ÂÂæÅË', 'Ã¥ÂÂä¸â¬Ã¦ÅË', 'Ã¥ÂÂäºÅæÅË' 75869 ] 75870 }; 75871 75872 var WEEK_TEXT = { 75873 EN: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 75874 CN: ['æâÂ¥', 'ä¸â¬', 'äºÅ', 'ä¸â°', 'Ã¥âºâº', 'äºâ', 'Ã¥â¦Â'] 75875 }; 75876 75877 extendComponentView({ 75878 75879 type: 'calendar', 75880 75881 /** 75882 * top/left line points 75883 * @private 75884 */ 75885 _tlpoints: null, 75886 75887 /** 75888 * bottom/right line points 75889 * @private 75890 */ 75891 _blpoints: null, 75892 75893 /** 75894 * first day of month 75895 * @private 75896 */ 75897 _firstDayOfMonth: null, 75898 75899 /** 75900 * first day point of month 75901 * @private 75902 */ 75903 _firstDayPoints: null, 75904 75905 render: function (calendarModel, ecModel, api) { 75906 75907 var group = this.group; 75908 75909 group.removeAll(); 75910 75911 var coordSys = calendarModel.coordinateSystem; 75912 75913 // range info 75914 var rangeData = coordSys.getRangeInfo(); 75915 var orient = coordSys.getOrient(); 75916 75917 this._renderDayRect(calendarModel, rangeData, group); 75918 75919 // _renderLines must be called prior to following function 75920 this._renderLines(calendarModel, rangeData, orient, group); 75921 75922 this._renderYearText(calendarModel, rangeData, orient, group); 75923 75924 this._renderMonthText(calendarModel, orient, group); 75925 75926 this._renderWeekText(calendarModel, rangeData, orient, group); 75927 }, 75928 75929 // render day rect 75930 _renderDayRect: function (calendarModel, rangeData, group) { 75931 var coordSys = calendarModel.coordinateSystem; 75932 var itemRectStyleModel = calendarModel.getModel('itemStyle').getItemStyle(); 75933 var sw = coordSys.getCellWidth();
vendor: 12,492 bytes, lines 75934-76316
75934 var sh = coordSys.getCellHeight(); 75935 75936 for (var i = rangeData.start.time; 75937 i <= rangeData.end.time; 75938 i = coordSys.getNextNDay(i, 1).time 75939 ) { 75940 75941 var point = coordSys.dataToRect([i], false).tl; 75942 75943 // every rect 75944 var rect = new Rect({ 75945 shape: { 75946 x: point[0], 75947 y: point[1], 75948 width: sw, 75949 height: sh 75950 }, 75951 cursor: 'default', 75952 style: itemRectStyleModel 75953 }); 75954 75955 group.add(rect); 75956 } 75957 75958 }, 75959 75960 // render separate line 75961 _renderLines: function (calendarModel, rangeData, orient, group) { 75962 75963 var self = this; 75964 75965 var coordSys = calendarModel.coordinateSystem; 75966 75967 var lineStyleModel = calendarModel.getModel('splitLine.lineStyle').getLineStyle(); 75968 var show = calendarModel.get('splitLine.show'); 75969 75970 var lineWidth = lineStyleModel.lineWidth; 75971 75972 this._tlpoints = []; 75973 this._blpoints = []; 75974 this._firstDayOfMonth = []; 75975 this._firstDayPoints = []; 75976 75977 75978 var firstDay = rangeData.start; 75979 75980 for (var i = 0; firstDay.time <= rangeData.end.time; i++) { 75981 addPoints(firstDay.formatedDate); 75982 75983 if (i === 0) { 75984 firstDay = coordSys.getDateInfo(rangeData.start.y + '-' + rangeData.start.m); 75985 } 75986 75987 var date = firstDay.date; 75988 date.setMonth(date.getMonth() + 1); 75989 firstDay = coordSys.getDateInfo(date); 75990 } 75991 75992 addPoints(coordSys.getNextNDay(rangeData.end.time, 1).formatedDate); 75993 75994 function addPoints(date) { 75995 75996 self._firstDayOfMonth.push(coordSys.getDateInfo(date)); 75997 self._firstDayPoints.push(coordSys.dataToRect([date], false).tl); 75998 75999 var points = self._getLinePointsOfOneWeek(calendarModel, date, orient); 76000 76001 self._tlpoints.push(points[0]); 76002 self._blpoints.push(points[points.length - 1]); 76003 76004 show && self._drawSplitline(points, lineStyleModel, group); 76005 } 76006 76007 76008 // render top/left line 76009 show && this._drawSplitline(self._getEdgesPoints(self._tlpoints, lineWidth, orient), lineStyleModel, group); 76010 76011 // render bottom/right line 76012 show && this._drawSplitline(self._getEdgesPoints(self._blpoints, lineWidth, orient), lineStyleModel, group); 76013 76014 }, 76015 76016 // get points at both ends 76017 _getEdgesPoints: function (points, lineWidth, orient) { 76018 var rs = [points[0].slice(), points[points.length - 1].slice()]; 76019 var idx = orient === 'horizontal' ? 0 : 1; 76020 76021 // both ends of the line are extend half lineWidth 76022 rs[0][idx] = rs[0][idx] - lineWidth / 2; 76023 rs[1][idx] = rs[1][idx] + lineWidth / 2; 76024 76025 return rs; 76026 }, 76027 76028 // render split line 76029 _drawSplitline: function (points, lineStyleModel, group) { 76030 76031 var poyline = new Polyline({ 76032 z2: 20, 76033 shape: { 76034 points: points 76035 }, 76036 style: lineStyleModel 76037 }); 76038 76039 group.add(poyline); 76040 }, 76041 76042 // render month line of one week points 76043 _getLinePointsOfOneWeek: function (calendarModel, date, orient) { 76044 76045 var coordSys = calendarModel.coordinateSystem; 76046 date = coordSys.getDateInfo(date); 76047 76048 var points = []; 76049 76050 for (var i = 0; i < 7; i++) { 76051 76052 var tmpD = coordSys.getNextNDay(date.time, i); 76053 var point = coordSys.dataToRect([tmpD.time], false); 76054 76055 points[2 * tmpD.day] = point.tl; 76056 points[2 * tmpD.day + 1] = point[orient === 'horizontal' ? 'bl' : 'tr']; 76057 } 76058 76059 return points; 76060 76061 }, 76062 76063 _formatterLabel: function (formatter, params) { 76064 76065 if (typeof formatter === 'string' && formatter) { 76066 return formatTplSimple(formatter, params); 76067 } 76068 76069 if (typeof formatter === 'function') { 76070 return formatter(params); 76071 } 76072 76073 return params.nameMap; 76074 76075 }, 76076 76077 _yearTextPositionControl: function (textEl, point, orient, position, margin) { 76078 76079 point = point.slice(); 76080 var aligns = ['center', 'bottom']; 76081 76082 if (position === 'bottom') { 76083 point[1] += margin; 76084 aligns = ['center', 'top']; 76085 } 76086 else if (position === 'left') { 76087 point[0] -= margin; 76088 } 76089 else if (position === 'right') { 76090 point[0] += margin; 76091 aligns = ['center', 'top']; 76092 } 76093 else { // top 76094 point[1] -= margin; 76095 } 76096 76097 var rotate = 0; 76098 if (position === 'left' || position === 'right') { 76099 rotate = Math.PI / 2; 76100 } 76101 76102 return { 76103 rotation: rotate, 76104 position: point, 76105 style: { 76106 textAlign: aligns[0], 76107 textVerticalAlign: aligns[1] 76108 } 76109 }; 76110 }, 76111 76112 // render year 76113 _renderYearText: function (calendarModel, rangeData, orient, group) { 76114 var yearLabel = calendarModel.getModel('yearLabel'); 76115 76116 if (!yearLabel.get('show')) { 76117 return; 76118 } 76119 76120 var margin = yearLabel.get('margin'); 76121 var pos = yearLabel.get('position'); 76122 76123 if (!pos) { 76124 pos = orient !== 'horizontal' ? 'top' : 'left'; 76125 } 76126 76127 var points = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]]; 76128 var xc = (points[0][0] + points[1][0]) / 2; 76129 var yc = (points[0][1] + points[1][1]) / 2; 76130 76131 var idx = orient === 'horizontal' ? 0 : 1; 76132 76133 var posPoints = { 76134 top: [xc, points[idx][1]], 76135 bottom: [xc, points[1 - idx][1]], 76136 left: [points[1 - idx][0], yc], 76137 right: [points[idx][0], yc] 76138 }; 76139 76140 var name = rangeData.start.y; 76141 76142 if (+rangeData.end.y > +rangeData.start.y) { 76143 name = name + '-' + rangeData.end.y; 76144 } 76145 76146 var formatter = yearLabel.get('formatter'); 76147 76148 var params = { 76149 start: rangeData.start.y, 76150 end: rangeData.end.y, 76151 nameMap: name 76152 }; 76153 76154 var content = this._formatterLabel(formatter, params); 76155 76156 var yearText = new Text({z2: 30}); 76157 setTextStyle(yearText.style, yearLabel, {text: content}), 76158 yearText.attr(this._yearTextPositionControl(yearText, posPoints[pos], orient, pos, margin)); 76159 76160 group.add(yearText); 76161 }, 76162 76163 _monthTextPositionControl: function (point, isCenter, orient, position, margin) { 76164 var align = 'left'; 76165 var vAlign = 'top'; 76166 var x = point[0]; 76167 var y = point[1]; 76168 76169 if (orient === 'horizontal') { 76170 y = y + margin; 76171 76172 if (isCenter) { 76173 align = 'center'; 76174 } 76175 76176 if (position === 'start') { 76177 vAlign = 'bottom'; 76178 } 76179 } 76180 else { 76181 x = x + margin; 76182 76183 if (isCenter) { 76184 vAlign = 'middle'; 76185 } 76186 76187 if (position === 'start') { 76188 align = 'right'; 76189 } 76190 } 76191 76192 return { 76193 x: x, 76194 y: y, 76195 textAlign: align, 76196 textVerticalAlign: vAlign 76197 }; 76198 }, 76199 76200 // render month and year text 76201 _renderMonthText: function (calendarModel, orient, group) { 76202 var monthLabel = calendarModel.getModel('monthLabel'); 76203 76204 if (!monthLabel.get('show')) { 76205 return; 76206 } 76207 76208 var nameMap = monthLabel.get('nameMap'); 76209 var margin = monthLabel.get('margin'); 76210 var pos = monthLabel.get('position'); 76211 var align = monthLabel.get('align'); 76212 76213 var termPoints = [this._tlpoints, this._blpoints]; 76214 76215 if (isString(nameMap)) { 76216 nameMap = MONTH_TEXT[nameMap.toUpperCase()] || []; 76217 } 76218 76219 var idx = pos === 'start' ? 0 : 1; 76220 var axis = orient === 'horizontal' ? 0 : 1; 76221 margin = pos === 'start' ? -margin : margin; 76222 var isCenter = (align === 'center'); 76223 76224 for (var i = 0; i < termPoints[idx].length - 1; i++) { 76225 76226 var tmp = termPoints[idx][i].slice(); 76227 var firstDay = this._firstDayOfMonth[i]; 76228 76229 if (isCenter) { 76230 var firstDayPoints = this._firstDayPoints[i]; 76231 tmp[axis] = (firstDayPoints[axis] + termPoints[0][i + 1][axis]) / 2; 76232 } 76233 76234 var formatter = monthLabel.get('formatter'); 76235 var name = nameMap[+firstDay.m - 1]; 76236 var params = { 76237 yyyy: firstDay.y, 76238 yy: (firstDay.y + '').slice(2), 76239 MM: firstDay.m, 76240 M: +firstDay.m, 76241 nameMap: name 76242 }; 76243 76244 var content = this._formatterLabel(formatter, params); 76245 76246 var monthText = new Text({z2: 30}); 76247 extend( 76248 setTextStyle(monthText.style, monthLabel, {text: content}), 76249 this._monthTextPositionControl(tmp, isCenter, orient, pos, margin) 76250 ); 76251 76252 group.add(monthText); 76253 } 76254 }, 76255 76256 _weekTextPositionControl: function (point, orient, position, margin, cellSize) { 76257 var align = 'center'; 76258 var vAlign = 'middle'; 76259 var x = point[0]; 76260 var y = point[1]; 76261 var isStart = position === 'start'; 76262 76263 if (orient === 'horizontal') { 76264 x = x + margin + (isStart ? 1 : -1) * cellSize[0] / 2; 76265 align = isStart ? 'right' : 'left'; 76266 } 76267 else { 76268 y = y + margin + (isStart ? 1 : -1) * cellSize[1] / 2; 76269 vAlign = isStart ? 'bottom' : 'top'; 76270 } 76271 76272 return { 76273 x: x, 76274 y: y, 76275 textAlign: align, 76276 textVerticalAlign: vAlign 76277 }; 76278 }, 76279 76280 // render weeks 76281 _renderWeekText: function (calendarModel, rangeData, orient, group) { 76282 var dayLabel = calendarModel.getModel('dayLabel'); 76283 76284 if (!dayLabel.get('show')) { 76285 return; 76286 } 76287 76288 var coordSys = calendarModel.coordinateSystem; 76289 var pos = dayLabel.get('position'); 76290 var nameMap = dayLabel.get('nameMap'); 76291 var margin = dayLabel.get('margin'); 76292 var firstDayOfWeek = coordSys.getFirstDayOfWeek(); 76293 76294 if (isString(nameMap)) { 76295 nameMap = WEEK_TEXT[nameMap.toUpperCase()] || []; 76296 } 76297 76298 var start = coordSys.getNextNDay( 76299 rangeData.end.time, (7 - rangeData.lweek) 76300 ).time; 76301 76302 var cellSize = [coordSys.getCellWidth(), coordSys.getCellHeight()]; 76303 margin = parsePercent$1(margin, cellSize[orient === 'horizontal' ? 0 : 1]); 76304 76305 if (pos === 'start') { 76306 start = coordSys.getNextNDay( 76307 rangeData.start.time, -(7 + rangeData.fweek) 76308 ).time; 76309 margin = -margin; 76310 } 76311 76312 for (var i = 0; i < 7; i++) { 76313 76314 var tmpD = coordSys.getNextNDay(start, i); 76315 var point = coordSys.dataToRect([tmpD.time], false).center; 76316 var day = i;
76317 day = Math.abs((i + firstDayOfWeek) % 7); 76318 var weekText = new Text({z2: 30}); 76319 76320 extend( 76321 setTextStyle(weekText.style, dayLabel, {text: nameMap[day]}), 76322 this._weekTextPositionControl(point, orient, pos, margin, cellSize) 76323 ); 76324 group.add(weekText); 76325 } 76326 } 76327 }); 76328 76329 /* 76330* Licensed to the Apache Software Foundation (ASF) under one 76331* or more contributor license agreements. See the NOTICE file 76332* distributed with this work for additional information 76333* regarding copyright ownership. The ASF licenses this file 76334* to you under the Apache License, Version 2.0 (the 76335* "License"); you may not use this file except in compliance 76336* with the License. You may obtain a copy of the License at 76337* 76338* http://www.apache.org/licenses/LICENSE-2.0 76339* 76340* Unless required by applicable law or agreed to in writing, 76341* software distributed under the License is distributed on an 76342* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 76343* KIND, either express or implied. See the License for the 76344* specific language governing permissions and limitations 76345* under the License. 76346*/ 76347 76348 /** 76349 * @file calendar.js 76350 * @author dxh 76351 */ 76352 76353 /* 76354* Licensed to the Apache Software Foundation (ASF) under one 76355* or more contributor license agreements. See the NOTICE file 76356* distributed with this work for additional information 76357* regarding copyright ownership. The ASF licenses this file 76358* to you under the Apache License, Version 2.0 (the 76359* "License"); you may not use this file except in compliance 76360* with the License. You may obtain a copy of the License at 76361* 76362* http://www.apache.org/licenses/LICENSE-2.0 76363* 76364* Unless required by applicable law or agreed to in writing, 76365* software distributed under the License is distributed on an 76366* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 76367* KIND, either express or implied. See the License for the 76368* specific language governing permissions and limitations 76369* under the License. 76370*/ 76371 76372// Model 76373 extendComponentModel({ 76374 76375 type: 'title', 76376 76377 layoutMode: {type: 'box', ignoreSize: true}, 76378 76379 defaultOption: { 76380 // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 76381 zlevel: 0, 76382 // äºÅ级å±âå 76383 z: 6, 76384 show: true, 76385 76386 text: '', 76387 // è¶â¦Ã©â¾æŽ¥è·³è½¬ 76388 // link: null, 76389 // ä»â¦Ã¦â¯æÅÂself | blank 76390 target: 'blank', 76391 subtext: '', 76392 76393 // è¶â¦Ã©â¾æŽ¥è·³è½¬ 76394 // sublink: null, 76395 // ä»â¦Ã¦â¯æÅÂself | blank 76396 subtarget: 'blank', 76397 76398 // 'center' æ 'left' æ 'right' 76399 // æ {number}ï¼ËxÃ¥ÂÂæ â¡Ã¯Â¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Ⱐ76400 left: 0, 76401 // 'top' æ 'bottom' æ 'center' 76402 // æ {number}ï¼ËyÃ¥ÂÂæ â¡Ã¯Â¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Ⱐ76403 top: 0, 76404 76405 // 水平对齠76406 // 'auto' | 'left' | 'right' | 'center' 76407 // é»Ë认æ ¹æÂ® left çšâä½Âç½®åˤæâÂæË¯å·¦å¯¹é½Âè¿ËæË¯å³对齠76408 // textAlign: null 76409 // 76410 // åžâçâºÂ´Ã¥Â¯Â¹Ã©Â½Â 76411 // 'auto' | 'top' | 'bottom' | 'middle' 76412 // é»Ë认æ ¹æÂ® top ä½Âç½®åˤæâÂæË¯ä¸Šå¯¹é½Âè¿ËæË¯ä¸â¹Ã¥Â¯Â¹Ã©Â½Â 76413 // textBaseline: null 76414 76415 backgroundColor: 'rgba(0,0,0,0)', 76416 76417 // æ â¡Ã©Â¢Ëè¾¹æ¡â é¢Åèâ°Â² 76418 borderColor: '#ccc', 76419 76420 // æ â¡Ã©Â¢Ëè¾¹æ¡â 线宽ï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认为0ï¼Ëæâ è¾¹æ¡â ï¼Ⱐ76421 borderWidth: 0, 76422 76423 // æ â¡Ã©Â¢ËÃ¥â â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认åÂâæâ¹åÂâÃ¥â â¦Ã¨Â¾Â¹Ã¨Â·Â为5*76424 // 接åÂâæâ¢Â°Ã§Â»âÃ¥Ëâ å˫设定上å³ä¸â¹Ã¥Â·Â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥ÂÅcss 76425 padding: 5, 76426 76427 // 主åâ°Â¯Ã¦ â¡Ã©Â¢Ë纵åÂâéâ´éšâï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»ËèÂ
76427®Â¤Ã¤Â¸Âº10*76428 itemGap: 10, 76429 textStyle: { 76430 fontSize: 18, 76431 fontWeight: 'bolder', 76432 color: '#333' 76433 }, 76434 subtextStyle: { 76435 color: '#aaa' 76436 } 76437 } 76438 }); 76439 76440// View 76441 extendComponentView({ 76442 76443 type: 'title', 76444 76445 render: function (titleModel, ecModel, api) { 76446 this.group.removeAll(); 76447 76448 if (!titleModel.get('show')) { 76449 return; 76450 } 76451 76452 var group = this.group; 76453 76454 var textStyleModel = titleModel.getModel('textStyle'); 76455 var subtextStyleModel = titleModel.getModel('subtextStyle'); 76456 76457 var textAlign = titleModel.get('textAlign'); 76458 var textBaseline = titleModel.get('textBaseline'); 76459 76460 var textEl = new Text({ 76461 style: setTextStyle({}, textStyleModel, { 76462 text: titleModel.get('text'), 76463 textFill: textStyleModel.getTextColor() 76464 }, {disableBox: true}), 76465 z2: 10 76466 }); 76467 76468 var textRect = textEl.getBoundingRect(); 76469 76470 var subText = titleModel.get('subtext'); 76471 var subTextEl = new Text({ 76472 style: setTextStyle({}, subtextStyleModel, { 76473 text: subText, 76474 textFill: subtextStyleModel.getTextColor(), 76475 y: textRect.height + titleModel.get('itemGap'), 76476 textVerticalAlign: 'top' 76477 }, {disableBox: true}), 76478 z2: 10 76479 }); 76480 76481 var link = titleModel.get('link'); 76482 var sublink = titleModel.get('sublink'); 76483 76484 textEl.silent = !link; 76485 subTextEl.silent = !sublink; 76486 76487 if (link) { 76488 textEl.on('click', function () { 76489 window.open(link, '_' + titleModel.get('target')); 76490 }); 76491 } 76492 if (sublink) { 76493 subTextEl.on('click', function () { 76494 window.open(sublink, '_' + titleModel.get('subtarget')); 76495 }); 76496 } 76497 76498 group.add(textEl); 76499 subText && group.add(subTextEl); 76500 // If no subText, but add subTextEl, there will be an empty line. 76501 76502 var groupRect = group.getBoundingRect(); 76503 var layoutOption = titleModel.getBoxLayoutParams(); 76504 layoutOption.width = groupRect.width; 76505 layoutOption.height = groupRect.height; 76506 var layoutRect = getLayoutRect( 76507 layoutOption, { 76508 width: api.getWidth(), 76509 height: api.getHeight() 76510 }, titleModel.get('padding') 76511 ); 76512 // Adjust text align based on position 76513 if (!textAlign) { 76514 // Align left if title is on the left. center and right is same 76515 textAlign = titleModel.get('left') || titleModel.get('r
76515ight'); 76516 if (textAlign === 'middle') { 76517 textAlign = 'center'; 76518 } 76519 // Adjust layout by text align 76520 if (textAlign === 'right') { 76521 layoutRect.x += layoutRect.width; 76522 } 76523 else if (textAlign === 'center') { 76524 layoutRect.x += layoutRect.width / 2; 76525 } 76526 } 76527 if (!textBaseline) { 76528 textBaseline = titleModel.get('top') || titleModel.get('bottom'); 76529 if (textBaseline === 'center') { 76530 textBaseline = 'middle'; 76531 } 76532 if (textBaseline === 'bottom') { 76533 layoutRect.y += layoutRect.height; 76534 } 76535 else if (textBaseline === 'middle') { 76536 layoutRect.y += layoutRect.height / 2; 76537 } 76538 76539 textBaseline = textBaseline || 'top'; 76540 } 76541 76542 group.attr('position', [layoutRect.x, layoutRect.y]); 76543 var alignStyle = { 76544 textAlign: textAlign, 76545 textVerticalAlign: textBaseline 76546 }; 76547 textEl.setStyle(alignStyle); 76548 subTextEl.setStyle(alignStyle); 76549 76550 // Render background 76551 // Get groupRect again because textAlign has been changed 76552 groupRect = group.getBoundingRect(); 76553 var padding = layoutRect.margin; 76554 var style = titleModel.getItemStyle(['color', 'opacity']); 76555 style.fill = titleModel.get('backgroundColor'); 76556 var rect = new Rect({ 76557 shape: { 76558 x: groupRect.x - padding[3], 76559 y: groupRect.y - padding[0], 76560 width: groupRect.width + padding[1] + padding[3], 76561 height: groupRect.height + padding[0] + padding[2], 76562 r: titleModel.get('borderRadius') 76563 }, 76564 style: style, 76565 silent: true 76566 }); 76567 subPixelOptimizeRect(rect); 76568 76569 group.add(rect); 76570 } 76571 }); 76572 76573 /* 76574* Licensed to the Apache Software Foundation (ASF) under one 76575* or more contributor license agreements. See the NOTICE file 76576* distributed with this work for additional information 76577* regarding copyright ownership. The ASF licenses this file 76578* to you under the Apache License, Version 2.0 (the 76579* "License"); you may not use this file except in compliance 76580* with the License. You may obtain a copy of the License at 76581* 76582* http://www.apache.org/licenses/LICENSE-2.0 76583* 76584* Unless required by applicable law or agreed to in writing, 76585* software distributed under the License is distributed on an 76586* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 76587* KIND, either express or implied. See the License for the 76588* specific language governing permissions and limitations 76589* under the License. 76590*/ 76591 76592 ComponentModel.registerSubTypeDefaulter('dataZoom', function () { 76593 // Default 'slider' when no type specified. 76594 return 'slider'; 76595 }); 76596 76597 /* 76598* Licensed to the Apache Software Foundation (ASF) under one 76599* or more contributor license agreements. See the NOTICE file 76600* distributed with this work for additional information 76601* regarding copyright ownership. The ASF licenses this file 76602* to you under the Apache License, Version 2.0 (the 76603* "License"); you may not use this file except in compliance 76604* with the License. You may obtain a copy of the License at 76605* 76606* http://www.apache.org/licenses/LICENSE-2.0 76607* 76608* Unless required by applicable law or agreed to in writing, 76609* software distributed under the License is distributed on an 76610* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 76611* KIND, either express or implied. See the License for the 76612* specific language governing permissions and limitations 76613* under the License. 76614*/ 76615 76616 var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle', 'single']; 76617// Supported coords. 76618 var COORDS = ['cartesian2d', 'polar', 'singleAxis']; 76619 76620 /** 76621 * @param {string} coordType 76622 * @return {boolean} 76623 */ 76624 function isCoordSupported(coordType) { 76625 return indexOf(COORDS, coordType) >= 0; 76626 } 76627 76628 /** 76629 * Create "each" method to iterate names. 76630 * 76631 * @pubilc 76632 * @param {Array.<string>} names 76633 * @param {Array.<string>=} attrs 76634 * @return {Function} 76635 */ 76636 function createNameEach(names, attrs) { 76637 names = names.slice(); 76638 var capitalNames = map(names, capitalFirst); 76639 attrs = (attrs || []).slice(); 76640 var capitalAttrs = map(attrs, capitalFirst); 76641 76642 return function (callback, context) { 76643 each$1(names, function (name, index) { 76644 var nameObj = {name: name, capital: capitalNames[index]};
76645 76646 for (var j = 0; j < attrs.length; j++) { 76647 nameObj[attrs[j]] = name + capitalAttrs[j]; 76648 } 76649 76650 callback.call(context, nameObj); 76651 }); 76652 }; 76653 } 76654 76655 /** 76656 * Iterate each dimension name. 76657 * 76658 * @public 76659 * @param {Function} callback The parameter is like: 76660 * { 76661 * name: 'angle', 76662 * capital: 'Angle', 76663 * axis: 'angleAxis', 76664 * axisIndex: 'angleAixs', 76665 * index: 'angleIndex' 76666 * } 76667 * @param {Object} context 76668 */ 76669 var eachAxisDim$1 = createNameEach(AXIS_DIMS, ['axisIndex', 'axis', 'index', 'id']); 76670 76671 /** 76672 * If tow dataZoomModels has the same axis controlled, we say that they are 'linked'. 76673 * dataZoomModels and 'links' make up one or more graphics. 76674 * This function finds the graphic where the source dataZoomModel is in. 76675 * 76676 * @public 76677 * @param {Function} forEachNode Node iterator. 76678 * @param {Function} forEachEdgeType edgeType iterator 76679 * @param {Function} edgeIdGetter Giving node and edgeType, return an array of edge id. 76680 * @return {Function} Input: sourceNode, Output: Like {nodes: [], dims: {}} 76681 */ 76682 function createLinkedNodesFinder(forEachNode, forEachEdgeType, edgeIdGetter) { 76683 76684 return function (sourceNode) { 76685 var result = { 76686 nodes: [], 76687 records: {} // key: edgeType.name, value: Object (key: edge id, value: boolean). 76688 }; 76689 76690 forEachEdgeType(function (edgeType) { 76691 result.records[edgeType.name] = {}; 76692 }); 76693 76694 if (!sourceNode) { 76695 return result; 76696 } 76697 76698 absorb(sourceNode, result); 76699 76700 var existsLink; 76701 do { 76702 existsLink = false; 76703 forEachNode(processSingleNode); 76704 } 76705 while (existsLink); 76706 76707 function processSingleNode(node) { 76708 if (!isNodeAbsorded(node, result) && isLinked(node, result)) { 76709 absorb(node, result); 76710 existsLink = true; 76711 } 76712 } 76713 76714 return result; 76715 }; 76716 76717 function isNodeAbsorded(node, result) { 76718 return indexOf(result.nodes, node) >= 0; 76719 } 76720 76721 function isLinked(node, result) { 76722 var hasLink = false; 76723 forEachEdgeType(function (edgeType) { 76724 each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) { 76725 result.records[edgeType.name][edgeId] && (hasLink = true); 76726 }); 76727 }); 76728 return hasLink; 76729 } 76730 76731 function absorb(node, result) { 76732 result.nodes.push(node); 76733 forEachEdgeType(function (edgeType) { 76734 each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) { 76735 result.records[edgeType.name][edgeId] = true; 76736 }); 76737 }); 76738 } 76739 } 76740 76741 /* 76742* Licensed to the Apache Software Foundation (ASF) under one 76743* or more contributor license agreements. See the NOTICE file 76744* distributed with this work for additional information 76745* regarding copyright ownership. The ASF licenses this file 76746* to you under the Apache License, Version 2.0 (the 76747* "License"); you may not use this file except in compliance 76748* with the License. You may obtain a copy of the License at 76749* 76750* http://www.apache.org/licenses/LICENSE-2.0 76751* 76752* Unless required by applicable law or agreed to in writing, 76753* software distributed under the License is distributed on an 76754* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 76755* KIND, either express or implied. See the License for the 76756* specific language governing permissions and limitations 76757* under the License. 76758*/ 76759 76760 var each$22 = each$1; 76761 var asc$1 = asc; 76762 76763 /** 76764 * Operate single axis. 76765 * One axis can only operated by one axis operator. 76766 * Different dataZoomModels may be defined to operate the same axis. 76767 * (i.e. 'inside' data zoom and 'slider' data zoom components) 76768 * So dataZoomModels share one axisProxy in that case. 76769 * 76770 * @class 76771 */ 76772 var AxisProxy = function (dimName, axisIndex, dataZoomModel, ecModel) { 76773 76774 /** 76775 * @private 76776 * @type {string} 76777 */ 76778 this._dimName = dimName; 76779 76780 /** 76781 * @private 76782 */ 76783 this._axisIndex = axisIndex; 76784 76785 /** 76786 * @private 76787 * @type {Array.<number>} 76788 */ 76789 this._valueWindow; 76790 76791 /** 76792 * @private 76793 * @type {Array.<number>} 76794 */ 76795 this._percentWindow; 76796 76797 /** 76798 * @private 76799 * @type {Array.<number>} 76800 */ 76801 this._dataExtent; 76802 76803 /**
76804 * {minSpan, maxSpan, minValueSpan, maxValueSpan} 76805 * @private 76806 * @type {Object} 76807 */ 76808 this._minMaxSpan; 76809 76810 /** 76811 * @readOnly 76812 * @type {module: echarts/model/Global} 76813 */ 76814 this.ecModel = ecModel; 76815 76816 /** 76817 * @private 76818 * @type {module: echarts/component/dataZoom/DataZoomModel} 76819 */ 76820 this._dataZoomModel = dataZoomModel; 76821 76822 // /** 76823 // * @readOnly 76824 // * @private 76825 // */ 76826 // this.hasSeriesStacked; 76827 }; 76828 76829 AxisProxy.prototype = { 76830 76831 constructor: AxisProxy, 76832 76833 /** 76834 * Whether the axisProxy is hosted by dataZoomModel. 76835 * 76836 * @public 76837 * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel 76838 * @return {boolean} 76839 */ 76840 hostedBy: function (dataZoomModel) { 76841 return this._dataZoomModel === dataZoomModel; 76842 }, 76843 76844 /** 76845 * @return {Array.<number>} Value can only be NaN or finite value. 76846 */ 76847 getDataValueWindow: function () { 76848 return this._valueWindow.slice(); 76849 }, 76850 76851 /** 76852 * @return {Array.<number>} 76853 */ 76854 getDataPercentWindow: function () { 76855 return this._percentWindow.slice(); 76856 }, 76857 76858 /** 76859 * @public 76860 * @param {number} axisIndex 76861 * @return {Array} seriesModels 76862 */ 76863 getTargetSeriesModels: function () { 76864 var seriesModels = []; 76865 var ecModel = this.ecModel; 76866 76867 ecModel.eachSeries(function (seriesModel) { 76868 if (isCoordSupported(seriesModel.get('coordinateSystem'))) { 76869 var dimName = this._dimName; 76870 var axisModel = ecModel.queryComponents({ 76871 mainType: dimName + 'Axis', 76872 index: seriesModel.get(dimName + 'AxisIndex'), 76873 id: seriesModel.get(dimName + 'AxisId') 76874 })[0]; 76875 if (this._axisIndex === (axisModel && axisModel.componentIndex)) {
76876 seriesModels.push(seriesModel); 76877 } 76878 } 76879 }, this); 76880 76881 return seriesModels; 76882 }, 76883 76884 getAxisModel: function () { 76885 return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex); 76886 }, 76887 76888 getOtherAxisModel: function () { 76889 var axisDim = this._dimName; 76890 var ecModel = this.ecModel; 76891 var axisModel = this.getAxisModel(); 76892 var isCartesian = axisDim === 'x' || axisDim === 'y'; 76893 var otherAxisDim; 76894 var coordSysIndexName; 76895 if (isCartesian) { 76896 coordSysIndexName = 'gridIndex'; 76897 otherAxisDim = axisDim === 'x' ? 'y' : 'x'; 76898 } 76899 else { 76900 coordSysIndexName = 'polarIndex'; 76901 otherAxisDim = axisDim === 'angle' ? 'radius' : 'angle'; 76902 } 76903 var foundOtherAxisModel; 76904 ecModel.eachComponent(otherAxisDim + 'Axis', function (otherAxisModel) { 76905 if ((otherAxisModel.get(coordSysIndexName) || 0) 76906 === (axisModel.get(coordSysIndexName) || 0) 76907 ) { 76908 foundOtherAxisModel = otherAxisModel; 76909 } 76910 }); 76911 return foundOtherAxisModel; 76912 }, 76913 76914 getMinMaxSpan: function () { 76915 return clone(this._minMaxSpan); 76916 }, 76917 76918 /** 76919 * Only calculate by given range and this._dataExtent, do not change anything. 76920 * 76921 * @param {Object} opt 76922 * @param {number} [opt.start] 76923 * @param {number} [opt.end] 76924 * @param {number} [opt.startValue] 76925 * @param {number} [opt.endValue] 76926 */ 76927 calculateDataWindow: function (opt) { 76928 var dataExtent = this._dataExtent; 76929 var axisModel = this.getAxisModel(); 76930 var scale = axisModel.axis.scale; 76931 var rangePropMode = this._dataZoomModel.getRangePropMode(); 76932 var percentExtent = [0, 100]; 76933 var percentWindow = [ 76934 opt.start, 76935 opt.end 76936 ]; 76937 var valueWindow = []; 76938 76939 each$22(['startValue', 'endValue'], function (prop) { 76940 valueWindow.push(opt[prop] != null ? scale.parse(opt[prop]) : null); 76941 }); 76942 76943 // Normalize bound. 76944 each$22([0, 1], function (idx) { 76945 var boundValue = valueWindow[idx]; 76946 var boundPercent = percentWindow[idx]; 76947 76948 // Notice: dataZoom is based either on `percentProp` ('start', 'end') or 76949 // on `valueProp` ('startValue', 'endValue'). The former one is suitable 76950 // for cases that a dataZoom component controls multiple axes with different 76951 // unit or extent, and the latter one is suitable for accurate zoom by pixel 76952 // (e.g., in dataZoomSelect). `valueProp` can be calculated from `percentProp`, 76953 // but it is awkward that `percentProp` can not be obtained from `valueProp` 76954 // accurately (because all of values that are overflow the `dataExtent` will 76955 // be calculated to percent '100%'). So we have to use 76956 // `dataZoom.getRangePropMode()` to mark which prop is used. 76957 // `rangePropMode` is updated only when setOption or dispatchAction, otherwise 76958 // it remains its original value. 76959 76960 if (rangePropMode[idx] === 'percent') { 76961 if (boundPercent == null) { 76962 boundPercent = percentExtent[idx]; 76963 } 76964 // Use scale.parse to math round for category or time axis. 76965 boundValue = scale.parse(linearMap( 76966 boundPercent, percentExtent, dataExtent, true 76967 )); 76968 } 76969 else { 76970 // Calculating `percent` from `value` may be not accurate, because 76971 // This calculation can not be inversed, because all of values that 76972 // are overflow the `dataExtent` will be calculated to percent '100%' 76973 boundPercent = linearMap( 76974 boundValue, dataExtent, percentExtent, true 76975 ); 76976 } 76977 76978 // valueWindow[idx] = round(boundValue); 76979 // percentWindow[idx] = round(boundPercent); 76980 valueWindow[idx] = boundValue; 76981 percentWindow[idx] = boundPercent; 76982 }); 76983 76984 return { 76985 valueWindow: asc$1(valueWindow), 76986 percentWindow: asc$1(percentWindow) 76987 }; 76988 }, 76989 76990 /** 76991 * Notice: reset should not be called before series.restoreData() called, 76992 * so it is recommanded to be called in "process stage" but not "model init 76993 * stage". 76994 * 76995 * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel 76996 */ 76997 reset: function (dataZoomModel) { 76998 if (dataZoomModel !== this._dataZoomModel) { 76999 return; 77000 } 77001 77002 var targetSeries = this.getTargetSeriesModels(); 77003 // Culculate data window and data extent, and record them. 77004 this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries); 77005 77006 // this.hasSeriesStacked = false; 77007 // each(targetSeries, function (series) { 77008 // var data = series.getData(); 77009 // var dataDim = data.mapDimension(this._dimName); 77010 // var stackedDimension = data.getCalculationInfo('stackedDimension'); 77011 // if (stackedDimension && stackedDimension === dataDim) { 77012 // this.hasSeriesStacked = true; 77013 // } 77014 // }, this); 77015 77016 var dataWindow = this.calculateDataWindow(dataZoomModel.option); 77017 77018 this._valueWindow = dataWindow.valueWindow; 77019 this._percentWindow = dataWindow.percentWindow; 77020 77021 setMinMaxSpan(this); 77022 77023 // Update axis setting then. 77024 setAxisModel(this); 77025 }, 77026 77027 /** 77028 * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel 77029 */ 77030 restore: function (dataZoomModel) { 77031 if (dataZoomModel !== this._dataZoomModel) { 77032 return; 77033 } 77034 77035 this._valueWindow = this._percentWindow = null; 77036 setAxisModel(this, true); 77037 }, 77038 77039 /** 77040 * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel 77041 */ 77042 filterData: function (dataZoomModel, api) { 77043 if (dataZoomModel !== this._dataZoomModel) { 77044 return; 77045 } 77046 77047 var axisDim = this._dimName; 77048 var seriesModels = this.getTargetSeriesModels(); 77049 var filterMode = dataZoomModel.get('filterMode'); 77050 var valueWindow = this._valueWindow; 77051 77052 if (filterMode === 'none') { 77053 return; 77054 } 77055 77056 // FIXME 77057 // Toolbox may has dataZoom injected. And if there are stacked bar chart 77058 // with NaN data, NaN will be filtered and stack will be wrong. 77059 // So we need to force the mode to be set empty. 77060 // In fect, it is not a big deal that do not support filterMode-'filter' 77061 // when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis 77062 // selection" some day, which might need "adapt to data extent on the 77063 // otherAxis", which is disabled by filterMode-'empty'. 77064 // But currently, stack has been fixed to based on value but not index, 77065 // so this is not an issue any more. 77066 // var otherAxisModel = this.getOtherAxisModel(); 77067 // if (dataZoomModel.get('$fromToolbox') 77068 // && otherAxisModel 77069 // && otherAxisModel.hasSeriesStacked 77070 // ) { 77071 // filterMode = 'empty'; 77072 // } 77073 77074 // TODO 77075 // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet. 77076 77077 // Process series data 77078 each$22(seriesModels, function (seriesModel) { 77079 var seriesData = seriesModel.getData(); 77080 var dataDims = seriesData.mapDimension(axisDim, true); 77081 77082 if (filterMode === 'weakFilter') { 77083 seriesData.filterSelf(function (dataIndex) { 77084 var leftOut; 77085 var rightOut; 77086 var hasValue; 77087 for (var i = 0; i < dataDims.length; i++) { 77088 var value = seriesData.get(dataDims[i], dataIndex); 77089 var thisHasValue = !isNaN(value); 77090 var thisLeftOut = value < valueWindow[0]; 77091 var thisRightOut = value > valueWindow[1]; 77092 if (thisHasValue && !thisLeftOut && !thisRightOut) { 77093 return true; 77094 } 77095 thisHasValue && (hasValue = true); 77096 thisLeftOut && (leftOut = true); 77097 thisRightOut && (rightOut = true); 77098 } 77099 // If both left out and right out, do not filter. 77100 return hasValue && leftOut && rightOut; 77101 }); 77102 } 77103 else { 77104 each$22(dataDims, function (dim) { 77105 if (filterMode === 'empty') { 77106 seriesModel.setData( 77107 seriesData.map(dim, function (value) { 77108 return !isInWindow(value) ? NaN : value; 77109 }) 77110 ); 77111 } 77112 else { 77113 var range = {}; 77114 range[dim] = valueWindow; 77115 77116 // console.time('select'); 77117 seriesData.selectRange(range); 77118 // console.timeEnd('select'); 77119 } 77120 }); 77121 } 77122 77123 each$22(dataDims, function (dim) { 77124 seriesData.setApproximateExtent(valueWindow, dim); 77125 }); 77126 }); 77127 77128 function isInWindow(value) { 77129 return value >= valueWindow[0] && value <= valueWindow[1]; 77130 } 77131 } 77132 }; 77133 77134 function calculateDataExtent(axisProxy, axisDim, seriesModels) { 77135 var dataExtent = [Infinity, -Infinity]; 77136 77137 each$22(seriesModels, function (seriesModel) { 77138 var seriesData = seriesModel.getData(); 77139 if (seriesData) { 77140 each$22(seriesData.mapDimension(axisDim, true), function (dim) { 77141 var seriesExtent = seriesData.getApproximateExtent(dim); 77142 seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]); 77143 seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]); 77144 }); 77145 } 77146 }); 77147 77148 if (dataExtent[1] < dataExtent[0]) { 77149 dataExtent = [NaN, NaN]; 77150 } 77151 77152 // It is important to get "consistent" extent when more then one axes is 77153 // controlled by a `dataZoom`, otherwise those axes will not be synchronized 77154 // when zooming. But it is difficult to know what is "consistent", considering 77155 // axes have different type or even different meanings (For example, two 77156 // time axes are used to compare data of the same date in different years). 77157 // So basically dataZoom just obtains extent by series.data (in category axis 77158 // extent can be obtained from axis.data). 77159 // Nevertheless, user can set min/max/scale on axes to make extent of axes 77160 // consistent. 77161 fixExtentByAxis(axisProxy, dataExtent); 77162 77163 return dataExtent; 77164 } 77165 77166 function fixExtentByAxis(axisProxy, dataExtent) { 77167 var axisModel = axisProxy.getAxisModel(); 77168 var min = axisModel.getMin(true); 77169 77170 // For category axis, if min/max/scale are not set, extent is determined 77171 // by axis.data by default. 77172 var isCategoryAxis = axisModel.get('type') === 'category'; 77173 var axisDataLen = isCategoryAxis && axisModel.getCategories().length; 77174 77175 if (min != null && min !== 'dataMin' && typeof min !== 'function') { 77176 dataExtent[0] = min; 77177 } 77178 else if (isCategoryAxis) { 77179 dataExtent[0] = axisDataLen > 0 ? 0 : NaN; 77180 } 77181 77182 var max = axisModel.getMax(true); 77183 if (max != null && max !== 'dataMax' && typeof max !== 'function') { 77184 dataExtent[1] = max; 77185 } 77186 else if (isCategoryAxis) { 77187 dataExtent[1] = axisDataLen > 0 ? axisDataLen - 1 : NaN; 77188 } 77189 77190 if (!axisModel.get('scale', true)) { 77191 dataExtent[0] > 0 && (dataExtent[0] = 0); 77192 dataExtent[1] < 0 && (dataExtent[1] = 0); 77193 } 77194 77195 // For value axis, if min/max/scale are not set, we just use the extent obtained 77196 // by series data, which may be a little different from the extent calculated by 77197 // `axisHelper.getScaleExtent`. But the different just affects the experience a 77198 // little when zooming. So it will not be fixed until some users require it strongly. 77199 77200 return dataExtent; 77201 } 77202 77203 function setAxisModel(axisProxy, isRestore) { 77204 var axisModel = axisProxy.getAxisModel(); 77205 77206 var percentWindow = axisProxy._percentWindow; 77207 var valueWindow = axisProxy._valueWindow; 77208 77209 if (!percentWindow) { 77210 return; 77211 } 77212 77213 // [0, 500]: arbitrary value, guess axis extent. 77214 var precision = getPixelPrecision(valueWindow, [0, 500]); 77215 precision = Math.min(precision, 20); 77216 // isRestore or isFull 77217 var useOrigin = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100); 77218 77219 axisModel.setRange( 77220 useOrigin ? null : +valueWindow[0].toFixed(precision), 77221 useOrigin ? null : +valueWindow[1].toFixed(precision) 77222 ); 77223 } 77224 77225 function setMinMaxSpan(axisProxy) { 77226 var minMaxSpan = axisProxy._minMaxSpan = {}; 77227 var dataZoomModel = axisProxy._dataZoomModel; 77228 77229 each$22(['min', 'max'], function (minMax) { 77230 minMaxSpan[minMax + 'Span'] = dataZoomModel.get(minMax + 'Span'); 77231 77232 // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan 77233 var valueSpan = dataZoomModel.get(minMax + 'ValueSpan'); 77234 77235 if (valueSpan != null) { 77236 minMaxSpan[minMax + 'ValueSpan'] = valueSpan; 77237 valueSpan = axisProxy.getAxisModel().axis.scale.parse(valueSpan); 77238 77239 if (valueSpan != null) { 77240 var dataExtent = axisProxy._dataExtent; 77241 minMaxSpan[minMax + 'Span'] = linearMap( 77242 dataExtent[0] + valueSpan, dataExtent, [0, 100], true 77243 ); 77244 } 77245 } 77246 }); 77247 } 77248 77249 /* 77250* Licensed to the Apache Software Foundation (ASF) under one 77251* or more contributor license agreements. See the NOTICE file 77252* distributed with this work for additional information 77253* regarding copyright ownership. The ASF licenses this file 77254* to you under the Apache License, Version 2.0 (the 77255* "License");
77255 you may not use this file except in compliance 77256* with the License. You may obtain a copy of the License at 77257* 77258* http://www.apache.org/licenses/LICENSE-2.0 77259* 77260* Unless required by applicable law or agreed to in writing, 77261* software distributed under the License is distributed on an 77262* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 77263* KIND, either express or implied. See the License for the 77264* specific language governing permissions and limitations 77265* under the License. 77266*/ 77267 77268 var each$21 = each$1; 77269 var eachAxisDim = eachAxisDim$1; 77270 77271 var DataZoomModel = extendComponentModel({ 77272 77273 type: 'dataZoom', 77274 77275 dependencies: [ 77276 'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series' 77277 ], 77278 77279 /** 77280 * @protected 77281 */ 77282 defaultOption: { 77283 zlevel: 0, 77284 z: 4, // Higher than normal component (z: 2). 77285 orient: null, // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'. 77286 xAxisIndex: null, // Default the first horizontal category axis. 77287 yAxisIndex: null, // Default the first vertical category axis. 77288 77289 filterMode: 'filter', // Possible values: 'filter' or 'empty' or 'weakFilter'. 77290 // 'filter': data items which are out of window will be removed. This option is 77291 // applicable when filtering outliers. For each data item, it will be 77292 // filtered if one of the relevant dimensions is out of the window. 77293 // 'weakFilter': data items which are out of window will be removed. This option 77294 // is applicable when filtering outliers. For each data item, it will be 77295 // filtered only if all of the relevant dimensions are out of the same 77296 // side of the window. 77297 // 'empty': data items which are out of window will be set to empty. 77298 // This option is applicable when user should not neglect 77299 // that there are some data items out of window. 77300 // 'none': Do not filter. 77301 // Taking line chart as an example, line will be broken in 77302 // the filtered points when filterModel is set to 'empty', but 77303 // be connected when set to 'filter'. 77304 77305 throttle: null, // Dispatch action by the fixed rate, avoid frequency. 77306 // default 100. Do not throttle when use null/undefined. 77307 // If animation === true and animationDurationUpdate > 0, 77308 // default value is 100, otherwise 20. 77309 start: 0, // Start percent. 0 ~ 100 77310 end: 100, // End percent. 0 ~ 100 77311 startValue: null, // Start value. If startValue specified, start is ignored. 77312 endValue: null, // End value. If endValue specified, end is ignored. 77313 minSpan: null, // 0 ~ 100 77314 maxSpan: null, // 0 ~ 100 77315 minValueSpan: null, // The range of dataZoom can not be smaller than that. 77316 maxValueSpan: null, // The range of dataZoom can not be larger than that. 77317 rangeMode: null // Array, can be 'value' or 'percent'. 77318 }, 77319 77320 /** 77321 * @override 77322 */ 77323 init: function (option, parentModel, ecModel) { 77324 77325 /** 77326 * key like x_0, y_1 77327 * @private 77328 * @type {Object} 77329 */ 77330 this._dataIntervalByAxis = {}; 77331 77332 /** 77333 * @private 77334 */ 77335 this._dataInfo = {}; 77336 77337 /** 77338 * key like x_0, y_1 77339 * @private 77340 */ 77341 this._axisProxies = {}; 77342 77343 /** 77344 * @readOnly 77345 */ 77346 this.textStyleModel; 77347 77348 /** 77349 * @private 77350 */ 77351 this._autoThrottle = true; 77352 77353 /** 77354 * 'percent' or 'value' 77355 * @private 77356 */ 77357 this._rangePropMode = ['percent', 'percent']; 77358 77359 var rawOption = retrieveRaw(option); 77360 77361 this.mergeDefaultAndTheme(option, ecModel); 77362 77363 this.doInit(rawOption); 77364 }, 77365 77366 /** 77367 * @override 77368 */ 77369 mergeOption: function (newOption) { 77370 var rawOption = retrieveRaw(newOption); 77371 77372 //FIX #2591 77373 merge(this.option, newOption, true); 77374 77375 this.doInit(rawOption); 77376 }, 77377 77378 /** 77379 * @protected 77380 */ 77381 doInit: function (rawOption) { 77382 var thisOption = this.option; 77383 77384 // Disable realtime view update if canvas is not supported. 77385 if (!env$1.canvasSupported) { 77386 thisOption.realtime = false; 77387 } 77388 77389 this._setDefaultThrottle(rawOption); 77390 77391 updateRangeUse(this, rawOption); 77392 77393 each$21([['start', 'startValue'], ['end', 'endValue']], function (names, index) { 77394 // start/end has higher priority over startValue/endValue if they 77395 // both set, but we should make chart.setOption({endValue: 1000}) 77396 // effective, rather than chart.setOption({endValue: 1000, end: null}). 77397 if (this._rangePropMode[index] === 'value') { 77398 thisOption[names[0]] = null; 77399 } 77400 // Otherwise do nothing and use the merge result. 77401 }, this); 77402 77403 this.textStyleModel = this.getModel('textStyle'); 77404 77405 this._resetTarget(); 77406 77407 this._giveAxisProxies(); 77408 }, 77409 77410 /** 77411 * @private 77412 */ 77413 _giveAxisProxies: function () { 77414 var axisProxies = this._axisProxies; 77415 77416 this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) { 77417 var axisModel = this.dependentModels[dimNames.axis][axisIndex]; 77418 77419 // If exists, share axisProxy with other dataZoomModels. 77420 var axisProxy = axisModel.__dzAxisProxy || ( 77421 // Use the first dataZoomModel as the main model of axisProxy. 77422 axisModel.__dzAxisProxy = new AxisProxy(
77423 dimNames.name, axisIndex, this, ecModel 77424 ) 77425 ); 77426 // FIXME 77427 // dispose __dzAxisProxy 77428 77429 axisProxies[dimNames.name + '_' + axisIndex] = axisProxy; 77430 }, this); 77431 }, 77432 77433 /** 77434 * @private 77435 */ 77436 _resetTarget: function () { 77437 var thisOption = this.option; 77438 77439 var autoMode = this._judgeAutoMode(); 77440 77441 eachAxisDim(function (dimNames) { 77442 var axisIndexName = dimNames.axisIndex; 77443 thisOption[axisIndexName] = normalizeToArray( 77444 thisOption[axisIndexName] 77445 ); 77446 }, this); 77447 77448 if (autoMode === 'axisIndex') { 77449 this._autoSetAxisIndex(); 77450 } 77451 else if (autoMode === 'orient') { 77452 this._autoSetOrient(); 77453 } 77454 }, 77455 77456 /** 77457 * @private 77458 */ 77459 _judgeAutoMode: function () { 77460 // Auto set only works for setOption at the first time. 77461 // The following is user's reponsibility. So using merged 77462 // option is OK. 77463 var thisOption = this.option; 77464 77465 var hasIndexSpecified = false; 77466 eachAxisDim(function (dimNames) { 77467 // When user set axisIndex as a empty array, we think that user specify axisIndex 77468 // but do not want use auto mode. Because empty array may be encountered when 77469 // some error occured.
77470 if (thisOption[dimNames.axisIndex] != null) { 77471 hasIndexSpecified = true; 77472 } 77473 }, this); 77474 77475 var orient = thisOption.orient; 77476 77477 if (orient == null && hasIndexSpecified) { 77478 return 'orient'; 77479 } 77480 else if (!hasIndexSpecified) { 77481 if (orient == null) { 77482 thisOption.orient = 'horizontal'; 77483 } 77484 return 'axisIndex'; 77485 } 77486 }, 77487 77488 /** 77489 * @private 77490 */ 77491 _autoSetAxisIndex: function () { 77492 var autoAxisIndex = true; 77493 var orient = this.get('orient', true); 77494 var thisOption = this.option; 77495 var dependentModels = this.dependentModels; 77496 77497 if (autoAxisIndex) { 77498 // Find axis that parallel to dataZoom as default. 77499 var dimName = orient === 'vertical' ? 'y' : 'x'; 77500 77501 if (dependentModels[dimName + 'Axis'].length) {
77502 thisOption[dimName + 'AxisIndex'] = [0]; 77503 autoAxisIndex = false; 77504 } 77505 else { 77506 each$21(dependentModels.singleAxis, function (singleAxisModel) { 77507 if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) { 77508 thisOption.singleAxisIndex = [singleAxisModel.componentIndex]; 77509 autoAxisIndex = false; 77510 } 77511 }); 77512 } 77513 } 77514 77515 if (autoAxisIndex) { 77516 // Find the first category axis as default. (consider polar) 77517 eachAxisDim(function (dimNames) { 77518 if (!autoAxisIndex) { 77519 return; 77520 } 77521 var axisIndices = []; 77522 var axisModels = this.dependentModels[dimNames.axis]; 77523 if (axisModels.length && !axisIndices.length) { 77524 for (var i = 0, len = axisModels.length; i < len; i++) { 77525 if (axisModels[i].get('type') === 'category') { 77526 axisIndices.push(i); 77527 } 77528 } 77529 }
77530 thisOption[dimNames.axisIndex] = axisIndices; 77531 if (axisIndices.length) { 77532 autoAxisIndex = false; 77533 } 77534 }, this); 77535 } 77536 77537 if (autoAxisIndex) { 77538 // FIXME 77539 // è¿â¢Ã©â¡ÅæË¯åâ¦Â¼Ã¥Â®Â¹ec2çšâÃ¥â â¢Ã¦Â³â¢Ã¯Â¼Ë没æÅâ¡Ã¥Â®Å¡xAxisIndexÃ¥âÅyAxisIndexæâ¶æŠŠscatterÃ¥âÅÃ¥ÂÅæâ¢Â°Ã¥â¬Â¼Ã¨Â½Â´Ã¦Å Ë柱纳åâ¦Â¥dataZoom控å˶ï¼â°Ã¯Â¼Å 77540 // ä½â æË¯å®žéâ¢â¦Ã¦Ë¯å¦éÅâ¬Ã¨Â¦ÂGrid.js#getScaleByOptionæÂ¥åˤæâÂï¼ËÃ¨â¬ÆÃ¨â¢âtimeï¼ÅlogçÂâ°axis typeï¼â°Ã¯Â¼Å¸ 77541 77542 // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified, 77543 // dataZoom component auto adopts series that reference to 77544 // both xAxis and yAxis which type is 'value'. 77545 this.ecModel.eachSeries(function (seriesModel) { 77546 if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) { 77547 eachAxisDim(function (dimNames) {
77548 var axisIndices = thisOption[dimNames.axisIndex]; 77549 77550 var axisIndex = seriesModel.get(dimNames.axisIndex); 77551 var axisId = seriesModel.get(dimNames.axisId); 77552 77553 var axisModel = seriesModel.ecModel.queryComponents({ 77554 mainType: dimNames.axis, 77555 index: axisIndex, 77556 id: axisId 77557 })[0]; 77558 77559 if (__DEV__) { 77560 if (!axisModel) { 77561 throw new Error( 77562 dimNames.axis + ' "' + retrieve( 77563 axisIndex, 77564 axisId, 77565 0 77566 ) + '" not found' 77567 ); 77568 } 77569 } 77570 axisIndex = axisModel.componentIndex; 77571 77572 if (indexOf(axisIndices, axisIndex) < 0) { 77573 axisIndices.push(axisIndex); 77574 } 77575 }); 77576 } 77577 }, this); 77578 } 77579 }, 77580 77581 /** 77582 * @private 77583 */ 77584 _autoSetOrient: function () { 77585 var dim; 77586 77587 // Find the first axis 77588 this.eachTargetAxis(function (dimNames) { 77589 !dim && (dim = dimNames.name); 77590 }, this); 77591 77592 this.option.orient = dim === 'y' ? 'vertical' : 'horizontal'; 77593 }, 77594 77595 /** 77596 * @private 77597 */ 77598 _isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) { 77599 // FIXME 77600 // éÅâ¬Ã¨Â¦ÂseriesçšâxAxisIndexÃ¥âÅyAxisIndexéÆÂ½Ã©Â¦âÃ¥â¦Ëèâ¡ÂªÃ¥Å ¨è®¾ç½®ä¸Šãâ¬â 77601 // ä¾â¹Ã¥Â¦âseries.type === scatteræâ¶ãâ¬â 77602 77603 var is = true; 77604 eachAxisDim(function (dimNames) { 77605 var seriesAxisIndex = seriesModel.get(dimNames.axisIndex); 77606 var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex]; 77607 77608 if (!axisModel || axisModel.get('type') !== axisType) { 77609 is = false; 77610 } 77611 }, this); 77612 return is; 77613 }, 77614 77615 /** 77616 * @private 77617 */ 77618 _setDefaultThrottle: function (rawOption) { 77619 // When first time user set throttle, auto throttle ends. 77620 if (rawOption.hasOwnProperty('throttle')) { 77621 this._autoThrottle = false; 77622 } 77623 if (this._autoThrottle) { 77624 var globalOption = this.ecModel.option; 77625 this.option.throttle = 77626 (globalOption.animation && globalOption.animationDurationUpdate > 0) 77627 ? 100 : 20; 77628 } 77629 }, 77630 77631 /** 77632 * @public 77633 */ 77634 getFirstTargetAxisModel: function () { 77635 var firstAxisModel; 77636 eachAxisDim(function (dimNames) { 77637 if (firstAxisModel == null) { 77638 var indices = this.get(dimNames.axisIndex); 77639 if (indices.length) { 77640 firstAxisModel = this.dependentModels[dimNames.axis][indices[0]]; 77641 } 77642 } 77643 }, this); 77644 77645 return firstAxisModel; 77646 }, 77647 77648 /** 77649 * @public 77650 * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel 77651 */ 77652 eachTargetAxis: function (callback, context) { 77653 var ecModel = this.ecModel; 77654 eachAxisDim(function (dimNames) { 77655 each$21( 77656 this.get(dimNames.axisIndex), 77657 function (axisIndex) { 77658 callback.call(context, dimNames, axisIndex, this, ecModel); 77659 }, 77660 this 77661 ); 77662 }, this); 77663 }, 77664 77665 /** 77666 * @param {string} dimName 77667 * @param {number} axisIndex 77668 * @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined. 77669 */ 77670 getAxisProxy: function (dimName, axisIndex) { 77671 return this._axisProxies[dimName + '_' + axisIndex]; 77672 }, 77673 77674 /** 77675 * @param {string} dimName 77676 * @param {number} axisIndex 77677 * @return {module:echarts/model/Model} If not found, return null/undefined. 77678 */ 77679 getAxisModel: function (dimName, axisIndex) { 77680 var axisProxy = this.getAxisProxy(dimName, axisIndex); 77681 return axisProxy && axisProxy.getAxisModel(); 77682 }, 77683 77684 /** 77685 * If not specified, set to undefined. 77686 * 77687 * @public 77688 * @param {Object} opt 77689 * @param {number} [opt.start] 77690 * @param {number} [opt.end] 77691 * @param {number} [opt.startValue] 77692 * @param {number} [opt.endValue] 77693 * @param {boolean} [ignoreUpdateRangeUsg=false] 77694 */ 77695 setRawRange: function (opt, ignoreUpdateRangeUsg) { 77696 var option = this.option; 77697 each$21([['start', 'startValue'], ['end', 'endValue']], function (names) { 77698 // If only one of 'start' and 'startValue' is not null/undefined, the other 77699 // should be cleared, which enable clear the option. 77700 // If both of them are not set, keep option with the original value, which 77701 // enable use only set start but not set end when calling `dispatchAction`. 77702 // The same as 'end' and 'endValue'. 77703 if (opt[names[0]] != null || opt[names[1]] != null) { 77704 option[names[0]] = opt[names[0]]; 77705 option[names[1]] = opt[names[1]]; 77706 } 77707 }, this); 77708 77709 !ignoreUpdateRangeUsg && updateRangeUse(this, opt); 77710 }, 77711 77712 /** 77713 * @public 77714 * @return {Array.<number>} [startPercent, endPercent] 77715 */ 77716 getPercentRange: function () { 77717 var axisProxy = this.findRepresentativeAxisProxy(); 77718 if (axisProxy) { 77719 return axisProxy.getDataPercentWindow(); 77720 } 77721 }, 77722 77723 /** 77724 * @public 77725 * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0); 77726 * 77727 * @param {string} [axisDimName] 77728 * @param {number} [axisIndex] 77729 * @return {Array.<number>} [startValue, endValue] value can only be '-' or finite number. 77730 */ 77731 getValueRange: function (axisDimName, axisIndex) { 77732 if (axisDimName == null && axisIndex == null) { 77733 var axisProxy = this.findRepresentativeAxisProxy(); 77734 if (axisProxy) { 77735 return axisProxy.getDataValueWindow(); 77736 } 77737 } 77738 else { 77739 return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow(); 77740 } 77741 }, 77742 77743 /** 77744 * @public 77745 * @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy 77746 * corresponding to the axisModel 77747 * @return {module:echarts/component/dataZoom/AxisProxy} 77748 */ 77749 findRepresentativeAxisProxy: function (axisModel) { 77750 if (axisModel) { 77751 return axisModel.__dzAxisProxy; 77752 } 77753 77754 // Find the first hosted axisProxy 77755 var axisProxies = this._axisProxies; 77756 for (var key in axisProxies) { 77757 if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) { 77758 return axisProxies[key]; 77759 } 77760 } 77761 77762 // If no hosted axis find not hosted axisProxy. 77763 // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis, 77764 // and the option.start or option.end settings are different. The percentRange 77765 // should follow axisProxy. 77766 // (We encounter this problem in toolbox data zoom.) 77767 for (var key in axisProxies) { 77768 if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) { 77769 return axisProxies[key]; 77770 } 77771 } 77772 }, 77773 77774 /** 77775 * @return {Array.<string>} 77776 */ 77777 getRangePropMode: function () { 77778 return this._rangePropMode.slice(); 77779 } 77780 77781 });
77782 77783 function retrieveRaw(option) { 77784 var ret = {}; 77785 each$21( 77786 ['start', 'end', 'startValue', 'endValue', 'throttle'], 77787 function (name) { 77788 option.hasOwnProperty(name) && (ret[name] = option[name]); 77789 } 77790 ); 77791 return ret; 77792 } 77793 77794 function updateRangeUse(dataZoomModel, rawOption) { 77795 var rangePropMode = dataZoomModel._rangePropMode; 77796 var rangeModeInOption = dataZoomModel.get('rangeMode'); 77797 77798 each$21([['start', 'startValue'], ['end', 'endValue']], function (names, index) { 77799 var percentSpecified = rawOption[names[0]] != null; 77800 var valueSpecified = rawOption[names[1]] != null; 77801 if (percentSpecified && !valueSpecified) { 77802 rangePropMode[index] = 'percent'; 77803 } 77804 else if (!percentSpecified && valueSpecified) { 77805 rangePropMode[index] = 'value'; 77806 } 77807 else if (rangeModeInOption) { 77808 rangePropMode[index] = rangeModeInOption[index]; 77809 } 77810 else if (percentSpecified) { // percentSpecified && valueSpecified 77811 rangePropMode[index] = 'percent'; 77812 } 77813 // else remain its original setting. 77814 }); 77815 } 77816 77817 /* 77818* Licensed to the Apache Software Foundation (ASF) under one 77819* or more contributor license agreements. See the NOTICE file 77820* distributed with this work for additional information 77821* regarding copyright ownership. The ASF licenses this file 77822* to you under the Apache License, Version 2.0 (the 77823* "License"); you may not use this file except in compliance 77824* with the License. You may obtain a copy of the License at 77825* 77826* http://www.apache.org/licenses/LICENSE-2.0 77827* 77828* Unless required by applicable law or agreed to in writing, 77829* software distributed under the License is distributed on an 77830* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 77831* KIND, either express or implied. See the License for the 77832* specific language governing permissions and limitations 77833* under the License. 77834*/ 77835 77836 var DataZoomView = Component.extend({ 77837 77838 type: 'dataZoom', 77839 77840 render: function (dataZoomModel, ecModel, api, payload) { 77841 this.dataZoomModel = dataZoomModel; 77842 this.ecModel = ecModel; 77843 this.api = api; 77844 }, 77845 77846 /** 77847 * Find the first target coordinate system. 77848 * 77849 * @protected 77850 * @return {Object} { 77851 * grid: [ 77852 * {model: coord0, axisModels: [axis1, axis3], coordIndex: 1}, 77853 * {model: coord1, axisModels: [axis0, axis2], coordIndex: 0}, 77854 * ... 77855 * ], // cartesians must not be null/undefined. 77856 * polar: [ 77857 * {model: coord0, axisModels: [axis4], coordIndex: 0}, 77858 * ... 77859 * ], // polars must not be null/undefined. 77860 * singleAxis: [ 77861 * {model: coord0, axisModels: [], coordIndex: 0} 77862 * ] 77863 */ 77864 getTargetCoordInfo: function () { 77865 var dataZoomModel = this.dataZoomModel; 77866 var ecModel = this.ecModel; 77867 var coordSysLists = {}; 77868 77869 dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { 77870 var axisModel = ecModel.getComponent(dimNames.axis, axisIndex); 77871 if (axisModel) { 77872 var coordModel = axisModel.getCoordSysModel(); 77873 coordModel && save( 77874 coordModel, 77875 axisModel, 77876 coordSysLists[coordModel.mainType] || (coordSysLists[coordModel.mainType] = []), 77877 coordModel.componentIndex 77878 ); 77879 } 77880 }, this); 77881 77882 function save(coordModel, axisModel, store, coordIndex) { 77883 var item; 77884 for (var i = 0; i < store.length; i++) { 77885 if (store[i].model === coordModel) { 77886 item = store[i]; 77887 break; 77888 } 77889 } 77890 if (!item) { 77891 store.push(item = { 77892 model: coordModel, axisModels: [], coordIndex: coordIndex 77893 }); 77894 } 77895 item.axisModels.push(axisModel); 77896 } 77897 77898 return coordSysLists; 77899 } 77900 77901 });
77902 77903 /* 77904* Licensed to the Apache Software Foundation (ASF) under one 77905* or more contributor license agreements. See the NOTICE file 77906* distributed with this work for additional information 77907* regarding copyright ownership. The ASF licenses this file 77908* to you under the Apache License, Version 2.0 (the 77909* "License"); you may not use this file except in compliance 77910* with the License. You may obtain a copy of the License at 77911* 77912* http://www.apache.org/licenses/LICENSE-2.0 77913* 77914* Unless required by applicable law or agreed to in writing, 77915* software distributed under the License is distributed on an 77916* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 77917* KIND, either express or implied. See the License for the 77918* specific language governing permissions and limitations 77919* under the License. 77920*/ 77921 77922 var SliderZoomModel = DataZoomModel.extend({ 77923 77924 type: 'dataZoom.slider', 77925 77926 layoutMode: 'box', 77927 77928 /** 77929 * @protected 77930 */ 77931 defaultOption: { 77932 show: true, 77933 77934 // ph => placeholder. Using placehoder here because 77935 // deault value can only be drived in view stage. 77936 right: 'ph', // Default align to grid rect. 77937 top: 'ph', // Default align to grid rect. 77938 width: 'ph', // Default align to grid rect. 77939 height: 'ph', // Default align to grid rect. 77940 left: null, // Default align to grid rect. 77941 bottom: null, // Default align to grid rect. 77942 77943 backgroundColor: 'rgba(47,69,84,0)', // Background of slider zoom component. 77944 // dataBackgroundColor: '#ddd', // Background coor of data shadow and border of box, 77945 // highest priority, remain for compatibility of 77946 // previous version, but not recommended any more. 77947 dataBackground: { 77948 lineStyle: { 77949 color: '#2f4554', 77950 width: 0.5, 77951 opacity: 0.3 77952 }, 77953 areaStyle: { 77954 color: 'rgba(47,69,84,0.3)', 77955 opacity: 0.3 77956 } 77957 }, 77958 borderColor: '#ddd', // border color of the box. For compatibility, 77959 // if dataBackgroundColor is set, borderColor 77960 // is ignored. 77961 77962 fillerColor: 'rgba(167,183,204,0.4)', // Color of selected area. 77963 // handleColor: 'rgba(89,170,216,0.95)', // Color of handle. 77964 // handleIcon: 'path://M4.9,17.8c0-1.4,4.5-10.5,5.5-12.4c0-0.1,0.6-1.1,0.9-1.1c0.4,0,0.9,1,0.9,1.1c1.1,2.2,5.4,11,5.4,12.4v17.8c0,1.5-0.
779646,2.1-1.3,2.1H6.1c-0.7,0-1.3-0.6-1.3-2.1V17.8z', 77965 handleIcon: 'M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z', 77966 // Percent of the slider height 77967 handleSize: '100%', 77968 77969 handleStyle: { 77970 color: '#a7b7cc' 77971 }, 77972 77973 labelPrecision: null, 77974 labelFormatter: null, 77975 showDetail: true, 77976 showDataShadow: 'auto', // Default auto decision. 77977 realtime: true, 77978 zoomLock: false, // Whether disable zoom. 77979 textStyle: { 77980 color: '#333' 77981 } 77982 } 77983 77984 }); 77985 77986 /* 77987* Licensed to the Apache Software Foundation (ASF) under one 77988* or more contributor license agreements. See the NOTICE file 77989* distributed with this work for additional information 77990* regarding copyright ownership. The ASF licenses this file 77991* to you under the Apache License, Version 2.0 (the 77992* "License"); you may not use this file except in compliance 77993* with the License. You may obtain a copy of the License at 77994* 77995* http://www.apache.org/licenses/LICENSE-2.0 77996* 77997* Unless required by applicable law or agreed to in writing, 77998* software distributed under the License is distributed on an 77999* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 78000* KIND, either express or implied. See the License for the 78001* specific language governing permissions and limitations 78002* under the License. 78003*/ 78004 78005 var Rect$2 = Rect; 78006 var linearMap$1 = linearMap; 78007 var asc$2 = asc; 78008 var bind$4 = bind; 78009 var each$23 = each$1; 78010 78011// Constants 78012 var DEFAULT_LOCATION_EDGE_GAP = 7; 78013 var DEFAULT_FRAME_BORDER_WIDTH = 1; 78014 var DEFAULT_FILLER_SIZE = 30; 78015 var HORIZONTAL = 'horizontal'; 78016 var VERTICAL = 'vertical'; 78017 var LABEL_GAP = 5; 78018 var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; 78019 78020 var SliderZoomView = DataZoomView.extend({ 78021 78022 type: 'dataZoom.slider', 78023 78024 init: function (ecModel, api) { 78025 78026 /** 78027 * @private 78028 * @type {Object} 78029 */ 78030 this._displayables = {}; 78031 78032 /** 78033 * @private 78034 * @type {string} 78035 */ 78036 this._orient; 78037 78038 /** 78039 * [0, 100] 78040 * @private 78041 */ 78042 this._range; 78043 78044 /** 78045 * [coord of the first handle, coord of the second handle] 78046 * @private 78047 */ 78048 this._handleEnds; 78049 78050 /** 78051 * [length, thick] 78052 * @private 78053 * @type {Array.<number>} 78054 */ 78055 this._size; 78056 78057 /** 78058 * @private 78059 * @type {number} 78060 */ 78061 this._handleWidth; 78062 78063 /** 78064 * @private 78065 * @type {number} 78066 */ 78067 this._handleHeight; 78068 78069 /** 78070 * @private 78071 */ 78072 this._location; 78073 78074 /** 78075 * @private 78076 */ 78077 this._dragging; 78078 78079 /** 78080 * @private 78081 */ 78082 this._dataShadowInfo; 78083 78084 this.api = api; 78085 }, 78086 78087 /** 78088 * @override 78089 */ 78090 render: function (dataZoomModel, ecModel, api, payload) { 78091 SliderZoomView.superApply(this, 'render', arguments); 78092 78093 createOrUpdate( 78094 this, 78095 '_dispatchZoomAction', 78096 this.dataZoomModel.get('throttle'), 78097 'fixRate' 78098 ); 78099 78100 this._orient = dataZoomModel.get('orient'); 78101 78102 if (this.dataZoomModel.get('show') === false) { 78103 this.group.removeAll(); 78104 return; 78105 } 78106 78107 // Notice: this._resetInterval() should not be executed when payload.type 78108 // is 'dataZoom', origin this._range should be maintained, otherwise 'pan' 78109 // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction, 78110 if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) { 78111 this._buildView(); 78112 } 78113 78114 this._updateView(); 78115 }, 78116 78117 /** 78118 * @override 78119 */ 78120 remove: function () { 78121 SliderZoomView.superApply(this, 'remove', arguments); 78122 clear(this, '_dispatchZoomAction'); 78123 }, 78124 78125 /** 78126 * @override 78127 */ 78128 dispose: function () { 78129 SliderZoomView.superApply(this, 'dispose', arguments); 78130 clear(this, '_dispatchZoomAction'); 78131 }, 78132 78133 _buildView: function () { 78134 var thisGroup = this.group; 78135 78136 thisGroup.removeAll(); 78137 78138 this._resetLocation(); 78139 this._resetInterval(); 78140 78141 var barGroup = this._displayables.barGroup = new Group(); 78142
78143 this._renderBackground(); 78144 78145 this._renderHandle(); 78146 78147 this._renderDataShadow(); 78148 78149 thisGroup.add(barGroup); 78150 78151 this._positionGroup(); 78152 }, 78153 78154 /** 78155 * @private 78156 */ 78157 _resetLocation: function () { 78158 var dataZoomModel = this.dataZoomModel; 78159 var api = this.api; 78160 78161 // If some of x/y/width/height are not specified, 78162 // auto-adapt according to target grid. 78163 var coordRect = this._findCoordRect(); 78164 var ecSize = {width: api.getWidth(), height: api.getHeight()}; 78165 // Default align by coordinate system rect. 78166 var positionInfo = this._orient === HORIZONTAL 78167 ? { 78168 // Why using 'right', because right should be used in vertical, 78169 // and it is better to be consistent for dealing with position param merge. 78170 right: ecSize.width - coordRect.x - coordRect.width, 78171 top: (ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP), 78172 width: coordRect.width, 78173 height: DEFAULT_FILLER_SIZE 78174 } 78175 : { // vertical 78176 right: DEFAULT_LOCATION_EDGE_GAP, 78177 top: coordRect.y, 78178 width: DEFAULT_FILLER_SIZE, 78179 height: coordRect.height 78180 }; 78181 78182 // Do not write back to option and replace value 'ph', because 78183 // the 'ph' value should be recalculated when resize. 78184 var layoutParams = getLayoutParams(dataZoomModel.option); 78185 78186 // Replace the placeholder value. 78187 each$1(['right', 'top', 'width', 'height'], function (name) { 78188 if (layoutParams[name] === 'ph') { 78189 layoutParams[name] = positionInfo[name]; 78190 } 78191 }); 78192 78193 var layoutRect = getLayoutRect( 78194 layoutParams, 78195 ecSize, 78196 dataZoomModel.padding 78197 ); 78198 78199 this._location = {x: layoutRect.x, y: layoutRect.y}; 78200 this._size = [layoutRect.width, layoutRect.height]; 78201 this._orient === VERTICAL && this._size.reverse(); 78202 }, 78203 78204 /** 78205 * @private 78206 */ 78207 _positionGroup: function () { 78208 var thisGroup = this.group; 78209 var location = this._location; 78210 var orient = this._orient; 78211 78212 // Just use the first axis to determine mapping. 78213 var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel(); 78214 var inverse = targetAxisModel && targetAxisModel.get('inverse'); 78215 78216 var barGroup = this._displayables.barGroup; 78217 var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; 78218 78219 // Transform barGroup. 78220 barGroup.attr( 78221 (orient === HORIZONTAL && !inverse) 78222 ? {scale: otherAxisInverse ? [1, 1] : [1, -1]} 78223 : (orient === HORIZONTAL && inverse) 78224 ? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]} 78225 : (orient === VERTICAL && !inverse) 78226 ? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2} 78227 // Dont use Math.PI, considering shadow direction. 78228 : {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2} 78229 ); 78230 78231 // Position barGroup 78232 var rect = thisGroup.getBoundingRect([barGroup]); 78233 thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]); 78234 }, 78235 78236 /** 78237 * @private 78238 */ 78239 _getViewExtent: function () { 78240 return [0, this._size[0]]; 78241 }, 78242 78243 _renderBackground: function () { 78244 var dataZoomModel = this.dataZoomModel; 78245 var size = this._size; 78246 var barGroup = this._displayables.barGroup; 78247 78248 barGroup.add(new Rect$2({ 78249 silent: true, 78250 shape: { 78251 x: 0, y: 0, width: size[0], height: size[1] 78252 }, 78253 style: { 78254 fill: dataZoomModel.get('backgroundColor') 78255 }, 78256 z2: -40 78257 })); 78258 78259 // Click panel, over shadow, below handles. 78260 barGroup.add(new Rect$2({ 78261 shape: { 78262 x: 0, y: 0, width: size[0], height: size[1] 78263 }, 78264 style: { 78265 fill: 'transparent'
78266 }, 78267 z2: 0, 78268 onclick: bind(this._onClickPanelClick, this) 78269 })); 78270 }, 78271 78272 _renderDataShadow: function () { 78273 var info = this._dataShadowInfo = this._prepareDataShadowInfo(); 78274 78275 if (!info) { 78276 return; 78277 } 78278 78279 var size = this._size; 78280 var seriesModel = info.series; 78281 var data = seriesModel.getRawData(); 78282 78283 var otherDim = seriesModel.getShadowDim 78284 ? seriesModel.getShadowDim() // @see candlestick 78285 : info.otherDim; 78286 78287 if (otherDim == null) { 78288 return; 78289 } 78290 78291 var otherDataExtent = data.getDataExtent(otherDim); 78292 // Nice extent. 78293 var otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3; 78294 otherDataExtent = [ 78295 otherDataExtent[0] - otherOffset, 78296 otherDataExtent[1] + otherOffset 78297 ]; 78298 var otherShadowExtent = [0, size[1]]; 78299 78300 var thisShadowExtent = [0, size[0]]; 78301 78302 var areaPoints = [[size[0], 0], [0, 0]]; 78303 var linePoints = []; 78304 var step = thisShadowExtent[1] / (data.count() - 1); 78305 var thisCoord = 0; 78306 78307 // Optimize for large data shadow 78308 var stride = Math.round(data.count() / size[0]); 78309 var lastIsEmpty; 78310 data.each([otherDim], function (value, index) { 78311 if (stride > 0 && (index % stride)) { 78312 thisCoord += step; 78313 return; 78314 } 78315 78316 // FIXME 78317 // Should consider axis.min/axis.max when drawing dataShadow. 78318 78319 // FIXME 78320 // åºâ该使çâ¨ç»Ÿä¸â¬Ã§Å¡â空åˤæâÂ?è¿ËæË¯åŨlistéâ¡Åè¿âºÃ¨Â¡Å空åˤæâÂ? 78321 var isEmpty = value == null || isNaN(value) || value === ''; 78322 // See #4235. 78323 var otherCoord = isEmpty 78324 ? 0 : linearMap$1(value, otherDataExtent, otherShadowExtent, true); 78325 78326 // Attempt to draw data shadow precisely when there are empty value. 78327 if (isEmpty && !lastIsEmpty && index) { 78328 areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]); 78329 linePoints.push([linePoints[linePoints.length - 1][0], 0]); 78330 } 78331 else if (!isEmpty && lastIsEmpty) { 78332 areaPoints.push([thisCoord, 0]); 78333 linePoints.push([thisCoord, 0]); 78334 } 78335 78336 areaPoints.push([thisCoord, otherCoord]); 78337 linePoints.push([thisCoord, otherCoord]); 78338 78339 thisCoord += step; 78340 lastIsEmpty = isEmpty; 78341 }); 78342 78343 var dataZoomModel = this.dataZoomModel; 78344 // var dataBackgroundModel = dataZoomModel.getModel('dataBackground'); 78345 this._displayables.barGroup.add(new Polygon({ 78346 shape: {points: areaPoints}, 78347 style: defaults( 78348 {fill: dataZoomModel.get('dataBackgroundColor')}, 78349 dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle() 78350 ), 78351 silent: true, 78352 z2: -20 78353 })); 78354 this._displayables.barGroup.add(new Polyline({ 78355 shape: {points: linePoints}, 78356 style: dataZoomModel.getModel('dataBackground.lineStyle').getLineStyle(), 78357 silent: true, 78358 z2: -19 78359 })); 78360 }, 78361 78362 _prepareDataShadowInfo: function () { 78363 var dataZoomModel = this.dataZoomModel; 78364 var showDataShadow = dataZoomModel.get('showDataShadow'); 78365 78366 if (showDataShadow === false) { 78367 return; 78368 } 78369 78370 // Find a representative series. 78371 var result; 78372 var ecModel = this.ecModel; 78373 78374 dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { 78375 var seriesModels = dataZoomModel 78376 .getAxisProxy(dimNames.name, axisIndex) 78377 .getTargetSeriesModels(); 78378
78379 each$1(seriesModels, function (seriesModel) { 78380 if (result) { 78381 return; 78382 } 78383 78384 if (showDataShadow !== true && indexOf( 78385 SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type') 78386 ) < 0 78387 ) { 78388 return; 78389 } 78390 78391 var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis; 78392 var otherDim = getOtherDim(dimNames.name); 78393 var otherAxisInverse; 78394 var coordSys = seriesModel.coordinateSystem; 78395 78396 if (otherDim != null && coordSys.getOtherAxis) { 78397 otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; 78398 } 78399 78400 otherDim = seriesModel.getData().mapDimension(otherDim); 78401 78402 result = { 78403 thisAxis: thisAxis, 78404 series: seriesModel, 78405 thisDim: dimNames.name, 78406 otherDim: otherDim, 78407 otherAxisInverse: otherAxisInverse 78408 }; 78409 78410 }, this); 78411 78412 }, this); 78413 78414 return result; 78415 }, 78416 78417 _renderHandle: function () { 78418 var displaybles = this._displayables; 78419 var handles = displaybles.handles = []; 78420 var handleLabels = displaybles.handleLabels = []; 78421 var barGroup = this._displayables.barGroup; 78422 var size = this._size; 78423 var dataZoomModel = this.dataZoomModel; 78424 78425 barGroup.add(displaybles.filler = new Rect$2({ 78426 draggable: true, 78427 cursor: getCursor(this._orient), 78428 drift: bind$4(this._onDragMove, this, 'all'), 78429 onmousemove: function (e) { 78430 // Fot mobile devicem, prevent screen slider on the button. 78431 stop(e.event); 78432 }, 78433 ondragstart: bind$4(this._showDataInfo, this, true), 78434 ondragend: bind$4(this._onDragEnd, this), 78435 onmouseover: bind$4(this._showDataInfo, this, true), 78436 onmouseout: bind$4(this._showDataInfo, this, false), 78437 style: { 78438 fill: dataZoomModel.get('fillerColor'), 78439 textPosition : 'inside' 78440 } 78441 })); 78442 78443 // Frame border. 78444 barGroup.add(new Rect$2(subPixelOptimizeRect({ 78445 silent: true, 78446 shape: { 78447 x: 0, 78448 y: 0, 78449 width: size[0], 78450 height: size[1] 78451 }, 78452 style: { 78453 stroke: dataZoomModel.get('dataBackgroundColor') 78454 || dataZoomModel.get('borderColor'), 78455 lineWidth: DEFAULT_FRAME_BORDER_WIDTH, 78456 fill: 'rgba(0,0,0,0)' 78457 } 78458 }))); 78459 78460 each$23([0, 1], function (handleIndex) { 78461 var path = createIcon( 78462 dataZoomModel.get('handleIcon'), 78463 { 78464 cursor: getCursor(this._orient), 78465 draggable: true, 78466 drift: bind$4(this._onDragMove, this, handleIndex), 78467 onmousemove: function (e) { 78468 // Fot mobile devicem, prevent screen slider on the button. 78469 stop(e.event); 78470 }, 78471 ondragend: bind$4(this._onDragEnd, this), 78472 onmouseover: bind$4(this._showDataInfo, this, true), 78473 onmouseout: bind$4(this._showDataInfo, this, false) 78474 }, 78475 {x: -1, y: 0, width: 2, height: 2} 78476 ); 78477 78478 var bRect = path.getBoundingRect(); 78479 this._handleHeight = parsePercent$1(dataZoomModel.get('handleSize'), this._size[1]); 78480 this._handleWidth = bRect.width / bRect.height * this._handleHeight; 78481 78482 path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle()); 78483 var handleColor = dataZoomModel.get('handleColor'); 78484 // Compatitable with previous version 78485 if (handleColor != null) { 78486 path.style.fill = handleColor; 78487 } 78488 78489 barGroup.add(handles[handleIndex] = path); 78490 78491 var textStyleModel = dataZoomModel.textStyleModel; 78492 78493 this.group.add( 78494 handleLabels[handleIndex] = new Text({ 78495 silent: true, 78496 invisible: true, 78497 style: { 78498 x: 0, y: 0, text: '', 78499 textVerticalAlign: 'middle', 78500 textAlign: 'center', 78501 textFill: textStyleModel.getTextColor(), 78502 textFont: textStyleModel.getFont() 78503 }, 78504 z2: 10 78505 })); 78506 78507 }, this); 78508 }, 78509 78510 /** 78511 * @private 78512 */ 78513 _resetInterval: function () { 78514 var range = this._range = this.dataZoomModel.getPercentRange(); 78515 var viewExtent = this._getViewExtent(); 78516 78517 this._handleEnds = [ 78518 linearMap$1(range[0], [0, 100], viewExtent, true), 78519 linearMap$1(range[1], [0, 100], viewExtent, true) 78520 ]; 78521 }, 78522 78523 /** 78524 * @private 78525 * @param {(number|string)} handleIndex 0 or 1 or 'all'
78526 * @param {number} delta 78527 * @return {boolean} changed 78528 */ 78529 _updateInterval: function (handleIndex, delta) { 78530 var dataZoomModel = this.dataZoomModel; 78531 var handleEnds = this._handleEnds; 78532 var viewExtend = this._getViewExtent(); 78533 var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); 78534 var percentExtent = [0, 100]; 78535 78536 sliderMove( 78537 delta, 78538 handleEnds, 78539 viewExtend, 78540 dataZoomModel.get('zoomLock') ? 'all' : handleIndex, 78541 minMaxSpan.minSpan != null 78542 ? linearMap$1(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, 78543 minMaxSpan.maxSpan != null 78544 ? linearMap$1(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null 78545 ); 78546 78547 var lastRange = this._range; 78548 var range = this._range = asc$2([ 78549 linearMap$1(handleEnds[0], viewExtend, percentExtent, true), 78550 linearMap$1(handleEnds[1], viewExtend, percentExtent, true) 78551 ]); 78552 78553 return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; 78554 }, 78555 78556 /** 78557 * @private 78558 */ 78559 _updateView: function (nonRealtime) { 78560 var displaybles = this._displayables; 78561 var handleEnds = this._handleEnds; 78562 var handleInterval = asc$2(handleEnds.slice()); 78563 var size = this._size; 78564 78565 each$23([0, 1], function (handleIndex) { 78566 // Handles 78567 var handle = displaybles.handles[handleIndex]; 78568 var handleHeight = this._handleHeight; 78569 handle.attr({ 78570 scale: [handleHeight / 2, handleHeight / 2], 78571 position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2] 78572 }); 78573 }, this); 78574 78575 // Filler 78576 displaybles.filler.setShape({ 78577 x: handleInterval[0], 78578 y: 0, 78579 width: handleInterval[1] - handleInterval[0], 78580 height: size[1] 78581 }); 78582 78583 this._updateDataInfo(nonRealtime); 78584 }, 78585 78586 /** 78587 * @private 78588 */ 78589 _updateDataInfo: function (nonRealtime) { 78590 var dataZoomModel = this.dataZoomModel; 78591 var displaybles = this._displayables; 78592 var handleLabels = displaybles.handleLabels; 78593 var orient = this._orient; 78594 var labelTexts = ['', '']; 78595 78596 // FIXME 78597 // dateåžâ¹Ã¯Â¼Åæâ¯æÅÂformatterï¼Åautoformatterï¼Ëec2 date.getAutoFormatterï¼Ⱐ78598 if (dataZoomModel.get('showDetail')) { 78599 var axisProxy = dataZoomModel.findRepresentativeAxisProxy(); 78600 78601 if (axisProxy) { 78602 var axis = axisProxy.getAxisModel().axis; 78603 var range = this._range; 78604 78605 var dataInterval = nonRealtime 78606 // See #4434, data and axis are not processed and reset yet in non-realtime mode. 78607 ? axisProxy.calculateDataWindow({ 78608 start: range[0], end: range[1] 78609 }).valueWindow 78610 : axisProxy.getDataValueWindow(); 78611 78612 labelTexts = [ 78613 this._formatLabel(dataInterval[0], axis), 78614 this._formatLabel(dataInterval[1], axis) 78615 ]; 78616 } 78617 } 78618 78619 var orderedHandleEnds = asc$2(this._handleEnds.slice()); 78620 78621 setLabel.call(this, 0); 78622 setLabel.call(this, 1); 78623 78624 function setLabel(handleIndex) { 78625 // Label 78626 // Text should not transform by barGroup. 78627 // Ignore handlers transform 78628 var barTransform = getTransform( 78629 displaybles.handles[handleIndex].parent, this.group 78630 ); 78631 var direction = transformDirection( 78632 handleIndex === 0 ? 'right' : 'left', barTransform 78633 ); 78634 var offset = this._handleWidth / 2 + LABEL_GAP; 78635 var textPoint = applyTransform$1( 78636 [ 78637 orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), 78638 this._size[1] / 2 78639 ], 78640 barTransform 78641 ); 78642 handleLabels[handleIndex].setStyle({ 78643 x: textPoint[0], 78644 y: textPoint[1], 78645 textVerticalAlign: orient === HORIZONTAL ? 'middle' : direction, 78646 textAlign: orient === HORIZONTAL ? direction : 'center', 78647 text: labelTexts[handleIndex] 78648 }); 78649 } 78650 }, 78651 78652 /** 78653 * @private 78654 */ 78655 _formatLabel: function (value, axis) { 78656 var dataZoomModel = this.dataZoomModel; 78657 var labelFormatter = dataZoomModel.get('labelFormatter'); 78658 78659 var labelPrecision = dataZoomModel.get('labelPrecision'); 78660 if (labelPrecision == null || labelPrecision === 'auto') { 78661 labelPrecision = axis.getPixelPrecision(); 78662 } 78663 78664 var valueStr = (value == null || isNaN(value)) 78665 ? '' 78666 // FIXME Glue code 78667 : (axis.type === 'category' || axis.type === 'time') 78668 ? axis.scale.getLabel(Math.round(value)) 78669 // param of toFixed should less then 20. 78670 : value.toFixed(Math.min(labelPrecision, 20)); 78671 78672 return isFunction$1(labelFormatter) 78673 ? labelFormatter(value, valueStr) 78674 : isString(labelFormatter) 78675 ? labelFormatter.replace('{value}', valueStr) 78676 : valueStr; 78677 }, 78678 78679 /** 78680 * @private 78681 * @param {boolean} showOrHide true: show, false: hide 78682 */ 78683 _showDataInfo: function (showOrHide) { 78684 // Always show when drgging. 78685 showOrHide = this._dragging || showOrHide; 78686 78687 var handleLabels = this._displayables.handleLabels;
78688 handleLabels[0].attr('invisible', !showOrHide); 78689 handleLabels[1].attr('invisible', !showOrHide); 78690 }, 78691 78692 _onDragMove: function (handleIndex, dx, dy) { 78693 this._dragging = true; 78694 78695 // Transform dx, dy to bar coordination. 78696 var barTransform = this._displayables.barGroup.getLocalTransform(); 78697 var vertex = applyTransform$1([dx, dy], barTransform, true); 78698 78699 var changed = this._updateInterval(handleIndex, vertex[0]); 78700 78701 var realtime = this.dataZoomModel.get('realtime'); 78702 78703 this._updateView(!realtime); 78704 78705 // Avoid dispatch dataZoom repeatly but range not changed, 78706 // which cause bad visual effect when progressive enabled. 78707 changed && realtime && this._dispatchZoomAction(); 78708 }, 78709 78710 _onDragEnd: function () { 78711 this._dragging = false; 78712 this._showDataInfo(false); 78713 78714 // While in realtime mode and stream mode, dispatch action when 78715 // drag end will cause the whole view rerender, which is unnecessary. 78716 var realtime = this.dataZoomModel.get('realtime'); 78717 !realtime && this._dispatchZoomAction(); 78718 }, 78719 78720 _onClickPanelClick: function (e) { 78721 var size = this._size; 78722 var localPoint = this._displayables.barGroup.transformCoordToLocal(e.offsetX, e.offsetY); 78723 78724 if (localPoint[0] < 0 || localPoint[0] > size[0] 78725 || localPoint[1] < 0 || localPoint[1] > size[1] 78726 ) { 78727 return; 78728 } 78729 78730 var handleEnds = this._handleEnds; 78731 var center = (handleEnds[0] + handleEnds[1]) / 2; 78732 78733 var changed = this._updateInterval('all', localPoint[0] - center); 78734 this._updateView(); 78735 changed && this._dispatchZoomAction(); 78736 }, 78737 78738 /** 78739 * This action will be throttled. 78740 * @private 78741 */ 78742 _dispatchZoomAction: function () { 78743 var range = this._range; 78744 78745 this.api.dispatchAction({ 78746 type: 'dataZoom', 78747 from: this.uid, 78748 dataZoomId: this.dataZoomModel.id, 78749 start: range[0], 78750 end: range[1] 78751 }); 78752 }, 78753 78754 /** 78755 * @private 78756 */ 78757 _findCoordRect: function () { 78758 // Find the grid coresponding to the first axis referred by dataZoom. 78759 var rect; 78760 each$23(this.getTargetCoordInfo(), function (coordInfoList) { 78761 if (!rect && coordInfoList.length) { 78762 var coordSys = coordInfoList[0].model.coordinateSystem; 78763 rect = coordSys.getRect && coordSys.getRect(); 78764 } 78765 }); 78766 if (!rect) { 78767 var width = this.api.getWidth(); 78768 var height = this.api.getHeight(); 78769 rect = { 78770 x: width * 0.2, 78771 y: height * 0.2, 78772 width: width * 0.6, 78773 height: height * 0.6 78774 }; 78775 } 78776 78777 return rect; 78778 } 78779 78780 }); 78781 78782 function getOtherDim(thisDim) { 78783 // FIXME 78784 // è¿â¢Ã¤Â¸ÂªÃ©â¬Â»Ã¨Â¾âÃ¥âÅgetOtherAxiséâ¡Åä¸â¬Ã¨â¡Â´Ã¯Â¼Åä½â æË¯åâ â¢Ã¥Å¨è¿â¢Ã©â¡ÅæË¯å¦ä¸Â好 78785 var map$$1 = {x: 'y', y: 'x', radius: 'angle', angle: 'radius'}; 78786 return map$$1[thisDim]; 78787 } 78788 78789 function getCursor(orient) { 78790 return orient === 'vertical' ? 'ns-resize' : 'ew-resize'; 78791 } 78792 78793 /* 78794* Licensed to the Apache Software Foundation (ASF) under one 78795* or more contributor license agreements. See the NOTICE file 78796* distributed with this work for additional information 78797* regarding copyright ownership. The ASF licenses this file 78798* to you under the Apache License, Version 2.0 (the 78799* "License"); you may not use this file except in compliance 78800* with the License. You may obtain a copy of the License at 78801* 78802* http://www.apache.org/licenses/LICENSE-2.0 78803* 78804* Unless required by applicable law or agreed to in writing, 78805* software distributed under the License is distributed on an 78806* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 78807* KIND, either express or implied. See the License for the 78808* specific language governing permissions and limitations 78809* under the License. 78810*/ 78811 78812 DataZoomModel.extend({ 78813 78814 type: 'dataZoom.inside', 78815 78816 /** 78817 * @protected 78818 */ 78819 defaultOption: { 78820 disabled: false, // Whether disable this inside zoom. 78821 zoomLock: false, // Whether disable zoom but only pan. 78822 zoomOnMouseWheel: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'. 78823 moveOnMouseMove: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'. 78824 preventDefaultMouseMove: true 78825 } 78826 });
78827 78828 /* 78829* Licensed to the Apache Software Foundation (ASF) under one 78830* or more contributor license agreements. See the NOTICE file 78831* distributed with this work for additional information 78832* regarding copyright ownership. The ASF licenses this file 78833* to you under the Apache License, Version 2.0 (the 78834* "License"); you may not use this file except in compliance 78835* with the License. You may obtain a copy of the License at 78836* 78837* http://www.apache.org/licenses/LICENSE-2.0 78838* 78839* Unless required by applicable law or agreed to in writing, 78840* software distributed under the License is distributed on an 78841* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 78842* KIND, either express or implied. See the License for the 78843* specific language governing permissions and limitations 78844* under the License. 78845*/ 78846 78847// Only create one roam controller for each coordinate system. 78848// one roam controller might be refered by two inside data zoom 78849// components (for example, one for x and one for y). When user 78850// pan or zoom, only dispatch one action for those data zoom 78851// components. 78852 78853 var curry$6 = curry; 78854 78855 var ATTR$1 = '\0_ec_dataZoom_roams'; 78856 78857 78858 /** 78859 * @public 78860 * @param {module:echarts/ExtensionAPI} api 78861 * @param {Object} dataZoomInfo 78862 * @param {string} dataZoomInfo.coordId 78863 * @param {Function} dataZoomInfo.containsPoint 78864 * @param {Array.<string>} dataZoomInfo.allCoordIds 78865 * @param {string} dataZoomInfo.dataZoomId 78866 * @param {number} dataZoomInfo.throttleRate 78867 * @param {Function} dataZoomInfo.panGetRange 78868 * @param {Function} dataZoomInfo.zoomGetRange 78869 * @param {boolean} [dataZoomInfo.zoomLock] 78870 * @param {boolean} [dataZoomInfo.disabled] 78871 */ 78872 function register$2(api, dataZoomInfo) { 78873 var store = giveStore(api); 78874 var theDataZoomId = dataZoomInfo.dataZoomId; 78875 var theCoordId = dataZoomInfo.coordId; 78876 78877 // Do clean when a dataZoom changes its target coordnate system. 78878 // Avoid memory leak, dispose all not-used-registered. 78879 each$1(store, function (record, coordId) { 78880 var dataZoomInfos = record.dataZoomInfos; 78881 if (dataZoomInfos[theDataZoomId] 78882 && indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0 78883 ) { 78884 delete dataZoomInfos[theDataZoomId]; 78885 record.count--; 78886 } 78887 }); 78888 78889 cleanStore(store); 78890 78891 var record = store[theCoordId]; 78892 // Create if needed. 78893 if (!record) { 78894 record = store[theCoordId] = { 78895 coordId: theCoordId, 78896 dataZoomInfos: {}, 78897 count: 0 78898 }; 78899 record.controller = createController(api, record); 78900 record.dispatchAction = curry(dispatchAction$1, api); 78901 } 78902 78903 // Update reference of dataZoom. 78904 !(record.dataZoomInfos[theDataZoomId]) && record.count++; 78905 record.dataZoomInfos[theDataZoomId] = dataZoomInfo; 78906 78907 var controllerParams = mergeControllerParams(record.dataZoomInfos); 78908 record.controller.enable(controllerParams.controlType, controllerParams.opt); 78909 78910 // Consider resize, area should be always updated. 78911 record.controller.setPointerChecker(dataZoomInfo.containsPoint); 78912 78913 // Update throttle. 78914 createOrUpdate( 78915 record, 78916 'dispatchAction', 78917 dataZoomInfo.throttleRate, 78918 'fixRate' 78919 ); 78920 } 78921 78922 /** 78923 * @public 78924 * @param {module:echarts/ExtensionAPI} api 78925 * @param {string} dataZoomId 78926 */ 78927 function unregister$1(api, dataZoomId) { 78928 var store = giveStore(api); 78929 78930 each$1(store, function (record) { 78931 record.controller.dispose(); 78932 var dataZoomInfos = record.dataZoomInfos; 78933 if (dataZoomInfos[dataZoomId]) { 78934 delete dataZoomInfos[dataZoomId]; 78935 record.count--; 78936 } 78937 }); 78938 78939 cleanStore(store); 78940 } 78941 78942 /** 78943 * @public 78944 */ 78945 function generateCoordId(coordModel) { 78946 return coordModel.type + '\0_' + coordModel.id; 78947 } 78948 78949 /** 78950 * Key: coordId, value: {dataZoomInfos: [], count, controller} 78951 * @type {Array.<Object>} 78952 */ 78953 function giveStore(api) { 78954 // Mount store on zrender instance, so that we do not 78955 // need to worry about dispose. 78956 var zr = api.getZr(); 78957 return zr[ATTR$1] || (zr[ATTR$1] = {}); 78958 } 78959 78960 function createController(api, newRecord) { 78961 var controller = new RoamController(api.getZr()); 78962 controller.on('pan', curry$6(onPan, newRecord)); 78963 controller.on('zoom', curry$6(onZoom, newRecord)); 78964 78965 return controller; 78966 } 78967 78968 function cleanStore(store) { 78969 each$1(store, function (record, coordId) { 78970 if (!record.count) { 78971 record.controller.dispose(); 78972 delete store[coordId]; 78973 } 78974 }); 78975 } 78976 78977 function onPan(record, dx, dy, oldX, oldY, newX, newY) { 78978 wrapAndDispatch(record, function (info) { 78979 return info.panGetRange(record.controller, dx, dy, oldX, oldY, newX, newY); 78980 }); 78981 } 78982 78983 function onZoom(record, scale, mouseX, mouseY) { 78984 wrapAndDispatch(record, function (info) { 78985 return info.zoomGetRange(record.controller, scale, mouseX, mouseY); 78986 }); 78987 } 78988 78989 function wrapAndDispatch(record, getRange) { 78990 var batch = []; 78991 78992 each$1(record.dataZoomInfos, function (info) { 78993 var range = getRange(info); 78994 !info.disabled && range && batch.push({ 78995 dataZoomId: info.dataZoomId, 78996 start: range[0], 78997 end: range[1] 78998 }); 78999 }); 79000 79001 batch.length && record.dispatchAction(batch); 79002 } 79003 79004 /** 79005 * This action will be throttled. 79006 */ 79007 function dispatchAction$1(api, batch) { 79008 api.dispatchAction({ 79009 type: 'dataZoom', 79010 batch: batch 79011 }); 79012 } 79013 79014 /** 79015 * Merge roamController settings when multiple dataZooms share one roamController. 79016 */ 79017 function mergeControllerParams(dataZoomInfos) { 79018 var controlType; 79019 var opt = {}; 79020 // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated 79021 // as string, it is probably revert to reserved word by compress tool. See #7411. 79022 var prefix = 'type_'; 79023 var typePriority = { 79024 'type_true': 2, 79025 'type_move': 1, 79026 'type_false': 0, 79027 'type_undefined': -1 79028 }; 79029 each$1(dataZoomInfos, function (dataZoomInfo) { 79030 var oneType = dataZoomInfo.disabled ? false : dataZoomInfo.zoomLock ? 'move' : true; 79031 if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) { 79032 controlType = oneType; 79033 }
79034 // Do not support that different 'shift'/'ctrl'/'alt' setting used in one coord sys. 79035 extend(opt, dataZoomInfo.roamControllerOpt); 79036 }); 79037 79038 return { 79039 controlType: controlType, 79040 opt: opt 79041 }; 79042 } 79043 79044 /* 79045* Licensed to the Apache Software Foundation (ASF) under one 79046* or more contributor license agreements. See the NOTICE file 79047* distributed with this work for additional information 79048* regarding copyright ownership. The ASF licenses this file 79049* to you under the Apache License, Version 2.0 (the 79050* "License"); you may not use this file except in compliance 79051* with the License. You may obtain a copy of the License at 79052* 79053* http://www.apache.org/licenses/LICENSE-2.0 79054* 79055* Unless required by applicable law or agreed to in writing, 79056* software distributed under the License is distributed on an 79057* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79058* KIND, either express or implied. See the License for the 79059* specific language governing permissions and limitations 79060* under the License. 79061*/ 79062 79063 var bind$5 = bind; 79064 79065 var InsideZoomView = DataZoomView.extend({ 79066 79067 type: 'dataZoom.inside', 79068 79069 /** 79070 * @override 79071 */ 79072 init: function (ecModel, api) { 79073 /** 79074 * 'throttle' is used in this.dispatchAction, so we save range 79075 * to avoid missing some 'pan' info. 79076 * @private 79077 * @type {Array.<number>} 79078 */ 79079 this._range; 79080 }, 79081 79082 /** 79083 * @override 79084 */ 79085 render: function (dataZoomModel, ecModel, api, payload) { 79086 InsideZoomView.superApply(this, 'render', arguments); 79087 79088 // Hance the `throttle` util ensures to preserve command order, 79089 // here simply updating range all the time will not cause missing 79090 // any of the the roam change. 79091 this._range = dataZoomModel.getPercentRange(); 79092 79093 // Reset controllers. 79094 each$1(this.getTargetCoordInfo(), function (coordInfoList, coordSysName) { 79095 79096 var allCoordIds = map(coordInfoList, function (coordInfo) { 79097 return generateCoordId(coordInfo.model); 79098 }); 79099 79100 each$1(coordInfoList, function (coordInfo) { 79101 var coordModel = coordInfo.model; 79102 var dataZoomOption = dataZoomModel.option; 79103 79104 register$2( 79105 api, 79106 { 79107 coordId: generateCoordId(coordModel), 79108 allCoordIds: allCoordIds, 79109 containsPoint: function (e, x, y) { 79110 return coordModel.coordinateSystem.containPoint([x, y]); 79111 }, 79112 dataZoomId: dataZoomModel.id, 79113 throttleRate: dataZoomModel.get('throttle', true), 79114 panGetRange: bind$5(this._onPan, this, coordInfo, coordSysName), 79115 zoomGetRange: bind$5(this._onZoom, this, coordInfo, coordSysName), 79116 zoomLock: dataZoomOption.zoomLock, 79117 disabled: dataZoomOption.disabled, 79118 roamControllerOpt: { 79119 zoomOnMouseWheel: dataZoomOption.zoomOnMouseWheel, 79120 moveOnMouseMove: dataZoomOption.moveOnMouseMove, 79121 preventDefaultMouseMove: dataZoomOption.preventDefaultMouseMove 79122 } 79123 } 79124 ); 79125 }, this); 79126 79127 }, this); 79128 }, 79129 79130 /** 79131 * @override 79132 */ 79133 dispose: function () { 79134 unregister$1(this.api, this.dataZoomModel.id); 79135 InsideZoomView.superApply(this, 'dispose', arguments); 79136 this._range = null; 79137 }, 79138 79139 /** 79140 * @private 79141 */ 79142 _onPan: function (coordInfo, coordSysName, controller, dx, dy, oldX, oldY, newX, newY) { 79143 var lastRange = this._range; 79144 var range = lastRange.slice(); 79145 79146 // Calculate transform by the first axis. 79147 var axisModel = coordInfo.axisModels[0]; 79148 if (!axisModel) { 79149 return; 79150 } 79151 79152 var directionInfo = getDirectionInfo[coordSysName]( 79153 [oldX, oldY], [newX, newY], axisModel, controller, coordInfo 79154 ); 79155 79156 var percentDelta = directionInfo.signal 79157 * (range[1] - range[0]) 79158 * directionInfo.pixel / directionInfo.pixelLength; 79159 79160 sliderMove(percentDelta, range, [0, 100], 'all'); 79161 79162 this._range = range; 79163 79164 if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { 79165 return range; 79166 } 79167 }, 79168 79169 /** 79170 * @private 79171 */ 79172 _onZoom: function (coordInfo, coordSysName, controller, scale, mouseX, mouseY) { 79173 var lastRange = this._range; 79174 var range = lastRange.slice(); 79175 79176 // Calculate transform by the first axis. 79177 var axisModel = coordInfo.axisModels[0]; 79178 if (!axisModel) { 79179 return; 79180 } 79181 79182 var directionInfo = getDirectionInfo[coordSysName]( 79183 null, [mouseX, mouseY], axisModel, controller, coordInfo 79184 ); 79185 var percentPoint = ( 79186 directionInfo.signal > 0 79187 ? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel) 79188 : (directionInfo.pixel - directionInfo.pixelStart) 79189 ) / directionInfo.pixelLength * (range[1] - range[0]) + range[0]; 79190 79191 scale = Math.max(1 / scale, 0); 79192 range[0] = (range[0] - percentPoint) * scale + percentPoint; 79193 range[1] = (range[1] - percentPoint) * scale + percentPoint; 79194 79195 // Restrict range. 79196 var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); 79197 79198 sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); 79199 79200 this._range = range; 79201 79202 if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { 79203 return range; 79204 } 79205 } 79206 79207 });
vendor: 1,494 bytes, lines 79208-79245
79208 79209 var getDirectionInfo = { 79210 79211 grid: function (oldPoint, newPoint, axisModel, controller, coordInfo) { 79212 var axis = axisModel.axis; 79213 var ret = {}; 79214 var rect = coordInfo.model.coordinateSystem.getRect(); 79215 oldPoint = oldPoint || [0, 0]; 79216 79217 if (axis.dim === 'x') { 79218 ret.pixel = newPoint[0] - oldPoint[0]; 79219 ret.pixelLength = rect.width; 79220 ret.pixelStart = rect.x; 79221 ret.signal = axis.inverse ? 1 : -1; 79222 } 79223 else { // axis.dim === 'y' 79224 ret.pixel = newPoint[1] - oldPoint[1]; 79225 ret.pixelLength = rect.height; 79226 ret.pixelStart = rect.y; 79227 ret.signal = axis.inverse ? -1 : 1; 79228 } 79229 79230 return ret; 79231 }, 79232 79233 polar: function (oldPoint, newPoint, axisModel, controller, coordInfo) { 79234 var axis = axisModel.axis; 79235 var ret = {}; 79236 var polar = coordInfo.model.coordinateSystem; 79237 var radiusExtent = polar.getRadiusAxis().getExtent(); 79238 var angleExtent = polar.getAngleAxis().getExtent(); 79239 79240 oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0]; 79241 newPoint = polar.pointToCoord(newPoint); 79242 79243 if (axisModel.mainType === 'radiusAxis') { 79244 ret.pixel = newPoint[0] - oldPoint[0]; 79245 // ret.pixelLength = Math.abs(radiusExtent[1] - radiu
79245sExtent[0]); 79246 // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]); 79247 ret.pixelLength = radiusExtent[1] - radiusExtent[0]; 79248 ret.pixelStart = radiusExtent[0]; 79249 ret.signal = axis.inverse ? 1 : -1; 79250 } 79251 else { // 'angleAxis' 79252 ret.pixel = newPoint[1] - oldPoint[1]; 79253 // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]); 79254 // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]); 79255 ret.pixelLength = angleExtent[1] - angleExtent[0]; 79256 ret.pixelStart = angleExtent[0]; 79257 ret.signal = axis.inverse ? -1 : 1; 79258 } 79259 79260 return ret; 79261 }, 79262 79263 singleAxis: function (oldPoint, newPoint, axisModel, controller, coordInfo) { 79264 var axis = axisModel.axis; 79265 var rect = coordInfo.model.coordinateSystem.getRect(); 79266 var ret = {}; 79267 79268 oldPoint = oldPoint || [0, 0]; 79269 79270 if (axis.orient === 'horizontal') { 79271 ret.pixel = newPoint[0] - oldPoint[0]; 79272 ret.pixelLength = rect.width; 79273 ret.pixelStart = rect.x; 79274 ret.signal = axis.inverse ? 1 : -1; 79275 } 79276 else { // 'vertical' 79277 ret.pixel = newPoint[1] - oldPoint[1]; 79278 ret.pixelLength = rect.height; 79279 ret.pixelStart = rect.y; 79280 ret.signal = axis.inverse ? -1 : 1; 79281 } 79282 79283 return ret; 79284 } 79285 }; 79286 79287 /* 79288* Licensed to the Apache Software Foundation (ASF) under one 79289* or more contributor license agreements. See the NOTICE file 79290* distributed with this work for additional information 79291* regarding copyright ownership. The ASF licenses this file 79292* to you under the Apache License, Version 2.0 (the 79293* "License"); you may not use this file except in compliance 79294* with the License. You may obtain a copy of the License at 79295* 79296* http://www.apache.org/licenses/LICENSE-2.0 79297* 79298* Unless required by applicable law or agreed to in writing, 79299* software distributed under the License is distributed on an 79300* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79301* KIND, either express or implied. See the License for the 79302* specific language governing permissions and limitations 79303* under the License. 79304*/ 79305 79306 registerProcessor({ 79307 79308 // `dataZoomProcessor` will only be performed in needed series. Consider if 79309 // there is a line series and a pie series, it is better not to update the 79310 // line series if only pie series is needed to be updated. 79311 getTargetSeries: function (ecModel) { 79312 var seriesModelMap = createHashMap(); 79313 79314 ecModel.eachComponent('dataZoom', function (dataZoomModel) { 79315 dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) { 79316 var axisProxy = dataZoomModel.getAxisProxy(dimNames.name, axisIndex); 79317 each$1(axisProxy.getTargetSeriesModels(), function (seriesModel) { 79318 seriesModelMap.set(seriesModel.uid, seriesModel); 79319 }); 79320 }); 79321 }); 79322 79323 return seriesModelMap; 79324 }, 79325 79326 modifyOutputEnd: true, 79327 79328 // Consider appendData, where filter should be performed. Because data process is 79329 // in block mode currently, it is not need to worry about that the overallProgress 79330 // execute every frame. 79331 overallReset: function (ecModel, api) { 79332 79333 ecModel.eachComponent('dataZoom', function (dataZoomModel) { 79334 // We calculate window and reset axis here but not in model 79335 // init stage and not after action dispatch handler, because 79336 // reset should be called after seriesData.restoreData. 79337 dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) { 79338 dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel, api); 79339 }); 79340 79341 // Caution: data zoom filtering is order sensitive when using 79342 // percent range and no min/max/scale set on axis. 79343 // For example, we have dataZoom definition: 79344 // [ 79345 // {xAxisIndex: 0, start: 30, end: 70}, 79346 // {yAxisIndex: 0, start: 20, end: 80} 79347 // ] 79348 // In this case, [20, 80] of y-dataZoom should be based on data 79349 // that have filtered by x-dataZoom using range of [30, 70], 79350 // but should not be based on full raw data. Thus sliding 79351 // x-dataZoom will change both ranges of xAxis and yAxis, 79352 // while sliding y-dataZoom will only change the range of yAxis. 79353 // So we should filter x-axis after reset x-axis immediately, 79354 // and then reset y-axis and filter y-axis. 79355 dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) { 79356 dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel, api); 79357 }); 79358 }); 79359 79360 ecModel.eachComponent('dataZoom', function (dataZoomModel) { 79361 // Fullfill all of the range props so that user 79362 // is able to get them from chart.getOption(). 79363 var axisProxy = dataZoomModel.findRepresentativeAxisProxy(); 79364 var percentRange = axisProxy.getDataPercentWindow(); 79365 var valueRange = axisProxy.getDataValueWindow(); 79366 79367 dataZoomModel.setRawRange({ 79368 start: percentRange[0], 79369 end: percentRange[1], 79370 startValue: valueRange[0], 79371 endValue: valueRange[1] 79372 }, true); 79373 }); 79374 } 79375 });
79376 79377 /* 79378* Licensed to the Apache Software Foundation (ASF) under one 79379* or more contributor license agreements. See the NOTICE file 79380* distributed with this work for additional information 79381* regarding copyright ownership. The ASF licenses this file 79382* to you under the Apache License, Version 2.0 (the 79383* "License"); you may not use this file except in compliance 79384* with the License. You may obtain a copy of the License at 79385* 79386* http://www.apache.org/licenses/LICENSE-2.0 79387* 79388* Unless required by applicable law or agreed to in writing, 79389* software distributed under the License is distributed on an 79390* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79391* KIND, either express or implied. See the License for the 79392* specific language governing permissions and limitations 79393* under the License. 79394*/ 79395 79396 registerAction('dataZoom', function (payload, ecModel) { 79397 79398 var linkedNodesFinder = createLinkedNodesFinder( 79399 bind(ecModel.eachComponent, ecModel, 'dataZoom'), 79400 eachAxisDim$1, 79401 function (model, dimNames) { 79402 return model.get(dimNames.axisIndex); 79403 } 79404 ); 79405 79406 var effectedModels = []; 79407 79408 ecModel.eachComponent( 79409 {mainType: 'dataZoom', query: payload}, 79410 function (model, index) { 79411 effectedModels.push.apply( 79412 effectedModels, linkedNodesFinder(model).nodes 79413 ); 79414 } 79415 ); 79416 79417 each$1(effectedModels, function (dataZoomModel, index) { 79418 dataZoomModel.setRawRange({ 79419 start: payload.start, 79420 end: payload.end, 79421 startValue: payload.startValue, 79422 endValue: payload.endValue 79423 }); 79424 }); 79425 79426 }); 79427 79428 /* 79429* Licensed to the Apache Software Foundation (ASF) under one 79430* or more contributor license agreements. See the NOTICE file 79431* distributed with this work for additional information 79432* regarding copyright ownership. The ASF licenses this file 79433* to you under the Apache License, Version 2.0 (the 79434* "License"); you may not use this file except in compliance 79435* with the License. You may obtain a copy of the License at 79436* 79437* http://www.apache.org/licenses/LICENSE-2.0 79438* 79439* Unless required by applicable law or agreed to in writing, 79440* software distributed under the License is distributed on an 79441* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79442* KIND, either express or implied. See the License for the 79443* specific language governing permissions and limitations 79444* under the License. 79445*/ 79446 79447 /** 79448 * DataZoom component entry 79449 */ 79450 79451 /* 79452* Licensed to the Apache Software Foundation (ASF) under one 79453* or more contributor license agreements. See the NOTICE file 79454* distributed with this work for additional information 79455* regarding copyright ownership. The ASF licenses this file 79456* to you under the Apache License, Version 2.0 (the 79457* "License"); you may not use this file except in compliance 79458* with the License. You may obtain a copy of the License at 79459* 79460* http://www.apache.org/licenses/LICENSE-2.0 79461* 79462* Unless required by applicable law or agreed to in writing, 79463* software distributed under the License is distributed on an 79464* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79465* KIND, either express or implied. See the License for the 79466* specific language governing permissions and limitations 79467* under the License. 79468*/ 79469 79470 var each$24 = each$1; 79471 79472 var preprocessor$2 = function (option) { 79473 var visualMap = option && option.visualMap; 79474 79475 if (!isArray(visualMap)) { 79476 visualMap = visualMap ? [visualMap] : []; 79477 } 79478 79479 each$24(visualMap, function (opt) { 79480 if (!opt) { 79481 return; 79482 } 79483 79484 // rename splitList to pieces 79485 if (has$1(opt, 'splitList') && !has$1(opt, 'pieces')) { 79486 opt.pieces = opt.splitList; 79487 delete opt.splitList; 79488 } 79489 79490 var pieces = opt.pieces; 79491 if (pieces && isArray(pieces)) { 79492 each$24(pieces, function (piece) { 79493 if (isObject$1(piece)) { 79494 if (has$1(piece, 'start') && !has$1(piece, 'min')) { 79495 piece.min = piece.start; 79496 } 79497 if (has$1(piece, 'end') && !has$1(piece, 'max')) { 79498 piece.max = piece.end; 79499 } 79500 } 79501 }); 79502 } 79503 }); 79504 }; 79505 79506 function has$1(obj, name) { 79507 return obj && obj.hasOwnProperty && obj.hasOwnProperty(name); 79508 } 79509 79510 /* 79511* Licensed to the Apache Software Foundation (ASF) under one 79512* or more contributor license agreements. See the NOTICE file 79513* distributed with this work for additional information 79514* regarding copyright ownership. The ASF licenses this file 79515* to you under the Apache License, Version 2.0 (the 79516* "License");
79516 you may not use this file except in compliance 79517* with the License. You may obtain a copy of the License at 79518* 79519* http://www.apache.org/licenses/LICENSE-2.0 79520* 79521* Unless required by applicable law or agreed to in writing, 79522* software distributed under the License is distributed on an 79523* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79524* KIND, either express or implied. See the License for the 79525* specific language governing permissions and limitations 79526* under the License. 79527*/ 79528 79529 ComponentModel.registerSubTypeDefaulter('visualMap', function (option) { 79530 // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used. 79531 return ( 79532 !option.categories 79533 && ( 79534 !( 79535 option.pieces 79536 ? option.pieces.length > 0 79537 : option.splitNumber > 0 79538 ) 79539 || option.calculable 79540 ) 79541 ) 79542 ? 'continuous' : 'piecewise'; 79543 }); 79544 79545 /* 79546* Licensed to the Apache Software Foundation (ASF) under one 79547* or more contributor license agreements. See the NOTICE file 79548* distributed with this work for additional information 79549* regarding copyright ownership. The ASF licenses this file 79550* to you under the Apache License, Version 2.0 (the 79551* "License"); you may not use this file except in compliance 79552* with the License. You may obtain a copy of the License at 79553* 79554* http://www.apache.org/licenses/LICENSE-2.0 79555* 79556* Unless required by applicable law or agreed to in writing, 79557* software distributed under the License is distributed on an 79558* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79559* KIND, either express or implied. See the License for the 79560* specific language governing permissions and limitations 79561* under the License. 79562*/ 79563 79564 var VISUAL_PRIORITY = PRIORITY.VISUAL.COMPONENT; 79565 79566 registerVisual(VISUAL_PRIORITY, { 79567 createOnAllSeries: true, 79568 reset: function (seriesModel, ecModel) { 79569 var resetDefines = []; 79570 ecModel.eachComponent('visualMap', function (visualMapModel) { 79571 var pipelineContext = seriesModel.pipelineContext; 79572 if (!visualMapModel.isTargetSeries(seriesModel) 79573 || (pipelineContext && pipelineContext.large) 79574 ) { 79575 return; 79576 } 79577 79578 resetDefines.push(incrementalApplyVisual( 79579 visualMapModel.stateList, 79580 visualMapModel.targetVisuals, 79581 bind(visualMapModel.getValueState, visualMapModel), 79582 visualMapModel.getDataDimension(seriesModel.getData()) 79583 )); 79584 }); 79585 79586 return resetDefines; 79587 } 79588 }); 79589 79590// Only support color. 79591 registerVisual(VISUAL_PRIORITY, { 79592 createOnAllSeries: true, 79593 reset: function (seriesModel, ecModel) { 79594 var data = seriesModel.getData(); 79595 var visualMetaList = []; 79596 79597 ecModel.eachComponent('visualMap', function (visualMapModel) { 79598 if (visualMapModel.isTargetSeries(seriesModel)) { 79599 var visualMeta = visualMapModel.getVisualMeta( 79600 bind(getColorVisual, null, seriesModel, visualMapModel) 79601 ) || {stops: [], outerColors: []}; 79602 79603 var concreteDim = visualMapModel.getDataDimension(data); 79604 var dimInfo = data.getDimensionInfo(concreteDim); 79605 if (dimInfo != null) { 79606 // visualMeta.dimension should be dimension index, but not concrete dimension. 79607 visualMeta.dimension = dimInfo.index; 79608 visualMetaList.push(visualMeta); 79609 } 79610 } 79611 }); 79612 79613 // console.log(JSON.stringify(visualMetaList.map(a => a.stops))); 79614 seriesModel.getData().setVisual('visualMeta', visualMetaList); 79615 } 79616 }); 79617 79618// FIXME 79619// performance and export for heatmap? 79620// value can be Infinity or -Infinity 79621 function getColorVisual(seriesModel, visualMapModel, value, valueState) { 79622 var mappings = visualMapModel.targetVisuals[valueState]; 79623 var visualTypes = VisualMapping.prepareVisualTypes(mappings); 79624 var resultVisual = { 79625 color: seriesModel.getData().getVisual('color') // default color. 79626 }; 79627 79628 for (var i = 0, len = visualTypes.length; i < len; i++) { 79629 var type = visualTypes[i];
79630 var mapping = mappings[ 79631 type === 'opacity' ? '__alphaForOpacity' : type 79632 ]; 79633 mapping && mapping.applyVisual(value, getVisual, setVisual); 79634 } 79635 79636 return resultVisual.color; 79637 79638 function getVisual(key) { 79639 return resultVisual[key]; 79640 } 79641 79642 function setVisual(key, value) { 79643 resultVisual[key] = value; 79644 } 79645 } 79646 79647 /* 79648* Licensed to the Apache Software Foundation (ASF) under one 79649* or more contributor license agreements. See the NOTICE file 79650* distributed with this work for additional information 79651* regarding copyright ownership. The ASF licenses this file 79652* to you under the Apache License, Version 2.0 (the 79653* "License"); you may not use this file except in compliance 79654* with the License. You may obtain a copy of the License at 79655* 79656* http://www.apache.org/licenses/LICENSE-2.0 79657* 79658* Unless required by applicable law or agreed to in writing, 79659* software distributed under the License is distributed on an 79660* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79661* KIND, either express or implied. See the License for the 79662* specific language governing permissions and limitations 79663* under the License. 79664*/ 79665 79666 /** 79667 * @file Visual mapping. 79668 */ 79669 79670 var visualDefault = { 79671 79672 /** 79673 * @public 79674 */ 79675 get: function (visualType, key, isCategory) { 79676 var value = clone( 79677 (defaultOption$3[visualType] || {})[key] 79678 ); 79679 79680 return isCategory 79681 ? (isArray(value) ? value[value.length - 1] : value) 79682 : value; 79683 } 79684 79685 }; 79686 79687 var defaultOption$3 = { 79688 79689 color: { 79690 active: ['#006edd', '#e0ffff'], 79691 inactive: ['rgba(0,0,0,0)'] 79692 }, 79693 79694 colorHue: { 79695 active: [0, 360], 79696 inactive: [0, 0] 79697 }, 79698 79699 colorSaturation: { 79700 active: [0.3, 1], 79701 inactive: [0, 0] 79702 }, 79703 79704 colorLightness: { 79705 active: [0.9, 0.5], 79706 inactive: [0, 0] 79707 }, 79708 79709 colorAlpha: { 79710 active: [0.3, 1], 79711 inactive: [0, 0] 79712 }, 79713 79714 opacity: { 79715 active: [0.3, 1], 79716 inactive: [0, 0] 79717 }, 79718 79719 symbol: { 79720 active: ['circle', 'roundRect', 'diamond'], 79721 inactive: ['none'] 79722 }, 79723 79724 symbolSize: { 79725 active: [10, 50], 79726 inactive: [0, 0] 79727 } 79728 }; 79729 79730 /* 79731* Licensed to the Apache Software Foundation (ASF) under one 79732* or more contributor license agreements. See the NOTICE file 79733* distributed with this work for additional information 79734* regarding copyright ownership. The ASF licenses this file 79735* to you under the Apache License, Version 2.0 (the 79736* "License"); you may not use this file except in compliance 79737* with the License. You may obtain a copy of the License at 79738* 79739* http://www.apache.org/licenses/LICENSE-2.0 79740* 79741* Unless required by applicable law or agreed to in writing, 79742* software distributed under the License is distributed on an 79743* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 79744* KIND, either express or implied. See the License for the 79745* specific language governing permissions and limitations 79746* under the License. 79747*/ 79748 79749 var mapVisual$2 = VisualMapping.mapVisual; 79750 var eachVisual = VisualMapping.eachVisual; 79751 var isArray$3 = isArray; 79752 var each$25 = each$1; 79753 var asc$3 = asc; 79754 var linearMap$2 = linearMap; 79755 var noop$2 = noop; 79756 79757 var VisualMapModel = extendComponentModel({ 79758 79759 type: 'visualMap', 79760 79761 dependencies: ['series'], 79762 79763 /** 79764 * @readOnly 79765 * @type {Array.<string>} 79766 */ 79767 stateList: ['inRange', 'outOfRange'], 79768 79769 /** 79770 * @readOnly 79771 * @type {Array.<string>} 79772 */ 79773 replacableOptionKeys: [ 79774 'inRange', 'outOfRange', 'target', 'controller', 'color' 79775 ], 79776 79777 /** 79778 * [lowerBound, upperBound] 79779 * 79780 * @readOnly 79781 * @type {Array.<number>} 79782 */ 79783 dataBound: [-Infinity, Infinity], 79784 79785 /** 79786 * @readOnly 79787 * @type {string|Object} 79788 */ 79789 layoutMode: {type: 'box', ignoreSize: true}, 79790 79791 /** 79792 * @protected 79793 */ 79794 defaultOption: { 79795 show: true, 79796 79797 zlevel: 0, 79798 z: 4, 79799 79800 seriesIndex: 'all', // 'all' or null/undefined: all series. 79801 // A number or an array of number: the specified series. 79802 79803 // set min: 0, max: 200, only for campatible with ec2. 79804 // In fact min max should not have default value. 79805 min: 0, // min value, must specified if pieces is not specified. 79806 max: 200, // max value, must specified if pieces is not specified. 79807 79808 dimension: null, 79809 inRange: null, // 'color', 'colorHue', 'colorSaturation', 'colorLightness', 'colorAlpha', 79810 // 'symbol', 'symbolSize' 79811 outOfRange: null, // 'color', 'colorHue', 'colorSaturation', 79812 // 'colorLightness', 'colorAlpha', 79813 // 'symbol', 'symbolSize' 79814 79815 left: 0, // 'center' æ 'left' æ 'right' æ {number} (px) 79816 right: null, // The same as left. 79817 top: null, // 'top' æ 'bottom' æ 'center' æ {number} (px) 79818 bottom: 0, // The same as top. 79819 79820 itemWidth: null, 79821 itemHeight: null, 79822 inverse: false, 79823 orient: 'vertical', // 'horizontal' æ 'vertical' 79824 79825 backgroundColor: 'rgba(0,0,0,0)', 79826 borderColor: '#ccc', // Ã¥â¬Â¼Ã¥Å¸Å¸Ã¨Â¾Â¹Ã¦Â¡â é¢Åèâ°Â² 79827 contentColor: '#5793f3', 79828 inactiveColor: '#aaa', 79829 borderWidth: 0, // Ã¥â¬Â¼Ã¥Å¸Å¸Ã¨Â¾Â¹Ã¦Â¡â 线宽ï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»ËèÂ
79829®Â¤Ã¤Â¸Âº0ï¼Ëæâ è¾¹æ¡â ï¼Ⱐ79830 padding: 5, // Ã¥â¬Â¼Ã¥Å¸Å¸Ã¥â â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认åÂâæâ¹åÂâÃ¥â â¦Ã¨Â¾Â¹Ã¨Â·Â为5*79831 // 接åÂâæâ¢Â°Ã§Â»âÃ¥Ëâ å˫设定上å³ä¸â¹Ã¥Â·Â¦Ã¨Â¾Â¹Ã¨Â·Âï¼ÅÃ¥ÂÅcss 79832 textGap: 10, // 79833 precision: 0, // å°Âæâ¢Â°Ã§Â²Â¾Ã¥ÂºÂ¦Ã¯Â¼Åé»Ë认为0ï¼Åæâ å°Âæâ¢Â°Ã§â¹ 79834 color: null, //é¢Åèâ°Â²Ã¯Â¼Ëdeprecatedï¼ÅÃ¥â¦Â¼Ã¥Â®Â¹ec2ï¼Å顺åºÂÃ¥ÂÅpiecesï¼Åä¸ÂÃ¥ÂÅ于inRange/outOfRangeï¼Ⱐ79835 79836 formatter: null, 79837 text: null, // æââ¡Ã¦Å¬ï¼Åå¦â['é«Ë', '低']ï¼ÅÃ¥â¦Â¼Ã¥Â®Â¹ec2ï¼Åtext[0]对åºâé«ËÃ¥â¬Â¼Ã¯Â¼Åtext[1]对åºâ低åâ¬Â¼ 79838 textStyle: { 79839 color: '#333' // Ã¥â¬Â¼Ã¥Å¸Å¸Ã¦ââ¡Ã¥Ââé¢Åèâ°Â² 79840 } 79841 }, 79842 79843 /** 79844 * @protected 79845 */ 79846 init: function (option, parentModel, ecModel) { 79847 79848 /** 79849 * @private 79850 * @type {Array.<number>} 79851 */ 79852 this._dataExtent; 79853 79854 /** 79855 * @readOnly 79856 */ 79857 this.targetVisuals = {}; 79858 79859 /** 79860 * @readOnly 79861 */ 79862 this.controllerVisuals = {}; 79863 79864 /** 79865 * @readOnly 79866 */ 79867 this.textStyleModel; 79868 79869 /** 79870 * [width, height] 79871 * @readOnly 79872 * @type {Array.<number>} 79873 */ 79874 this.itemSize; 79875 79876 this.mergeDefaultAndTheme(option, ecModel); 79877 }, 79878 79879 /** 79880 * @protected 79881 */ 79882 optionUpdated: function (newOption, isInit) { 79883 var thisOption = this.option; 79884 79885 // FIXME 79886 // necessary? 79887 // Disable realtime view update if canvas is not supported. 79888 if (!env$1.canvasSupported) { 79889 thisOption.realtime = false; 79890 } 79891 79892 !isInit && replaceVisualOption( 79893 thisOption, newOption, this.replacableOptionKeys 79894 ); 79895 79896 this.textStyleModel = this.getModel('textStyle'); 79897 79898 this.resetItemSize(); 79899 79900 this.completeVisualOption(); 79901 }, 79902 79903 /** 79904 * @protected 79905 */ 79906 resetVisual: function (supplementVisualOption) { 79907 var stateList = this.stateList; 79908 supplementVisualOption = bind(supplementVisualOption, this); 79909 79910 this.controllerVisuals = createVisualMappings( 79911 this.option.controller, stateList, supplementVisualOption 79912 ); 79913 this.targetVisuals = createVisualMappings( 79914 this.option.target, stateList, supplementVisualOption 79915 ); 79916 }, 79917 79918 /** 79919 * @protected 79920 * @return {Array.<number>} An array of series indices. 79921 */ 79922 getTargetSeriesIndices: function () { 79923 var optionSeriesIndex = this.option.seriesIndex; 79924 var seriesIndices = []; 79925 79926 if (optionSeriesIndex == null || optionSeriesIndex === 'all') { 79927 this.ecModel.eachSeries(function (seriesModel, index) { 79928 seriesIndices.push(index); 79929 }); 79930 } 79931 else { 79932 seriesIndices = normalizeToArray(optionSeriesIndex); 79933 } 79934 79935 return seriesIndices; 79936 }, 79937 79938 /** 79939 * @public 79940 */ 79941 eachTargetSeries: function (callback, context) { 79942 each$1(this.getTargetSeriesIndices(), function (seriesIndex) { 79943 callback.call(context, this.ecModel.getSeriesByIndex(seriesIndex)); 79944 }, this); 79945 }, 79946 79947 /** 79948 * @pubilc 79949 */ 79950 isTargetSeries: function (seriesModel) { 79951 var is = false; 79952 this.eachTargetSeries(function (model) { 79953 model === seriesModel && (is = true); 79954 }); 79955 return is; 79956 }, 79957 79958 /** 79959 * @example 79960 * this.formatValueText(someVal); // format single numeric value to text. 79961 * this.formatValueText(someVal, true); // format single category value to text. 79962 * this.formatValueText([min, max]); // format numeric min-max to text. 79963 * this.formatValueText([this.dataBound[0], max]); // using data lower bound. 79964 * this.formatValueText([min, this.dataBound[1]]); // using data upper bound. 79965 * 79966 * @param {number|Array.<number>} value Real value, or this.dataBound[0 or 1]. 79967 * @param {boolean} [isCategory=false] Only available when value is number. 79968 * @param {Array.<string>} edgeSymbols Open-close symbol when value is interval. 79969 * @return {string} 79970 * @protected 79971 */ 79972 formatValueText: function(value, isCategory, edgeSymbols) { 79973 var option = this.option; 79974 var precision = option.precision; 79975 var dataBound = this.dataBound; 79976 var formatter = option.formatter; 79977 var isMinMax; 79978 var textValue; 79979 edgeSymbols = edgeSymbols || ['<', '>']; 79980 79981 if (isArray(value)) { 79982 value = value.slice(); 79983 isMinMax = true; 79984 } 79985 79986 textValue = isCategory 79987 ? value 79988 : (isMinMax 79989 ? [toFixed(value[0]), toFixed(value[1])] 79990 : toFixed(value) 79991 ); 79992 79993 if (isString(formatter)) { 79994 return formatter 79995 .replace('{value}', isMinMax ? textValue[0] : textValue) 79996 .replace('{value2}', isMinMax ? textValue[1] : textValue); 79997 } 79998 else if (isFunction$1(formatter)) { 79999 return isMinMax 80000 ? formatter(value[0], value[1]) 80001 : formatter(value); 80002 } 80003 80004 if (isMinMax) { 80005 if (value[0] === dataBound[0]) { 80006 return edgeSymbols[0] + ' ' + textValue[1]; 80007 } 80008 else if (value[1] === dataBound[1]) { 80009 return edgeSymbols[1] + ' ' + textValue[0]; 80010 } 80011 else { 80012 return textValue[0] + ' - ' + textValue[1]; 80013 } 80014 } 80015 else { // Format single value (includes category case). 80016 return textValue; 80017 } 80018 80019 function toFixed(val) { 80020 return val === dataBound[0] 80021 ? 'min' 80022 : val === dataBound[1] 80023 ? 'max' 80024 : (+val).toFixed(Math.min(precision, 20)); 80025 } 80026 }, 80027 80028 /** 80029 * @protected 80030 */ 80031 resetExtent: function () { 80032 var thisOption = this.option; 80033 80034 // Can not calculate data extent by data here. 80035 // Because series and data may be modified in processing stage. 80036 // So we do not support the feature "auto min/max". 80037 80038 var extent = asc$3([thisOption.min, thisOption.max]); 80039 80040 this._dataExtent = extent; 80041 }, 80042 80043 /** 80044 * @public
80045 * @param {module:echarts/data/List} list 80046 * @return {string} Concrete dimention. If return null/undefined, 80047 * no dimension used. 80048 */ 80049 getDataDimension: function (list) { 80050 var optDim = this.option.dimension; 80051 var listDimensions = list.dimensions; 80052 if (optDim == null && !listDimensions.length) { 80053 return; 80054 } 80055 80056 if (optDim != null) { 80057 return list.getDimension(optDim); 80058 } 80059 80060 var dimNames = list.dimensions; 80061 for (var i = dimNames.length - 1; i >= 0; i--) { 80062 var dimName = dimNames[i]; 80063 var dimInfo = list.getDimensionInfo(dimName); 80064 if (!dimInfo.isCalculationCoord) { 80065 return dimName; 80066 } 80067 } 80068 }, 80069 80070 /** 80071 * @public 80072 * @override 80073 */ 80074 getExtent: function () { 80075 return this._dataExtent.slice(); 80076 }, 80077 80078 /** 80079 * @protected 80080 */ 80081 completeVisualOption: function () { 80082 var ecModel = this.ecModel; 80083 var thisOption = this.option; 80084 var base = {inRange: thisOption.inRange, outOfRange: thisOption.outOfRange}; 80085 80086 var target = thisOption.target || (thisOption.target = {}); 80087 var controller = thisOption.controller || (thisOption.controller = {}); 80088 80089 merge(target, base); // Do not override 80090 merge(controller, base); // Do not override 80091 80092 var isCategory = this.isCategory(); 80093 80094 completeSingle.call(this, target); 80095 completeSingle.call(this, controller); 80096 completeInactive.call(this, target, 'inRange', 'outOfRange'); 80097 // completeInactive.call(this, target, 'outOfRange', 'inRange'); 80098 completeController.call(this, controller); 80099 80100 function completeSingle(base) { 80101 // Compatible with ec2 dataRange.color. 80102 // The mapping order of dataRange.color is: [high value, ..., low value] 80103 // whereas inRange.color and outOfRange.color is [low value, ..., high value] 80104 // Notice: ec2 has no inverse. 80105 if (isArray$3(thisOption.color) 80106 // If there has been inRange: {symbol: ...}, adding color is a mistake. 80107 // So adding color only when no inRange defined. 80108 && !base.inRange 80109 ) { 80110 base.inRange = {color: thisOption.color.slice().reverse()}; 80111 } 80112 80113 // Compatible with previous logic, always give a defautl color, otherwise 80114 // simple config with no inRange and outOfRange will not work. 80115 // Originally we use visualMap.color as the default color, but setOption at 80116 // the second time the default color will be erased. So we change to use 80117 // constant DEFAULT_COLOR. 80118 // If user do not want the defualt color, set inRange: {color: null}. 80119 base.inRange = base.inRange || {color: ecModel.get('gradientColor')}; 80120 80121 // If using shortcut like: {inRange: 'symbol'}, complete default value. 80122 each$25(this.stateList, function (state) { 80123 var visualType = base[state]; 80124 80125 if (isString(visualType)) { 80126 var defa = visualDefault.get(visualType, 'active', isCategory); 80127 if (defa) { 80128 base[state] = {}; 80129 base[state][visualType] = defa; 80130 } 80131 else { 80132 // Mark as not specified. 80133 delete base[state]; 80134 } 80135 } 80136 }, this); 80137 } 80138 80139 function completeInactive(base, stateExist, stateAbsent) { 80140 var optExist = base[stateExist]; 80141 var optAbsent = base[stateAbsent]; 80142 80143 if (optExist && !optAbsent) { 80144 optAbsent = base[stateAbsent] = {}; 80145 each$25(optExist, function (visualData, visualType) { 80146 if (!VisualMapping.isValidType(visualType)) { 80147 return; 80148 } 80149 80150 var defa = visualDefault.get(visualType, 'inactive', isCategory); 80151 80152 if (defa != null) { 80153 optAbsent[visualType] = defa; 80154 80155 // Compatibable with ec2: 80156 // Only inactive color to rgba(0,0,0,0) can not 80157 // make label transparent, so use opacity also. 80158 if (visualType === 'color' 80159 && !optAbsent.hasOwnProperty('opacity') 80160 && !optAbsent.hasOwnProperty('colorAlpha') 80161 ) { 80162 optAbsent.opacity = [0, 0]; 80163 } 80164 } 80165 }); 80166 } 80167 } 80168 80169 function completeController(controller) { 80170 var symbolExists = (controller.inRange || {}).symbol 80171 || (controller.outOfRange || {}).symbol; 80172 var symbolSizeExists = (controller.inRange || {}).symbolSize 80173 || (controller.outOfRange || {}).symbolSize; 80174 var inactiveColor = this.get('inactiveColor'); 80175 80176 each$25(this.stateList, function (state) { 80177 80178 var itemSize = this.itemSize; 80179 var visuals = controller[state]; 80180 80181 // Set inactive color for controller if no other color 80182 // attr (like colorAlpha) specified. 80183 if (!visuals) { 80184 visuals = controller[state] = { 80185 color: isCategory ? inactiveColor : [inactiveColor] 80186 }; 80187 } 80188 80189 // Consistent symbol and symbolSize if not specified. 80190 if (visuals.symbol == null) { 80191 visuals.symbol = symbolExists 80192 && clone(symbolExists) 80193 || (isCategory ? 'roundRect' : ['roundRect']); 80194 } 80195 if (visuals.symbolSize == null) { 80196 visuals.symbolSize = symbolSizeExists 80197 && clone(symbolSizeExists) 80198 || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]); 80199 } 80200 80201 // Filter square and none. 80202 visuals.symbol = mapVisual$2(visuals.symbol, function (symbol) { 80203 return (symbol === 'none' || symbol === 'square') ? 'roundRect' : symbol; 80204 }); 80205 80206 // Normalize symbolSize 80207 var symbolSize = visuals.symbolSize; 80208 80209 if (symbolSize != null) { 80210 var max = -Infinity; 80211 // symbolSize can be object when categories defined. 80212 eachVisual(symbolSize, function (value) { 80213 value > max && (max = value); 80214 }); 80215 visuals.symbolSize = mapVisual$2(symbolSize, function (value) { 80216 return linearMap$2(value, [0, max], [0, itemSize[0]], true); 80217 }); 80218 } 80219 80220 }, this); 80221 } 80222 }, 80223 80224 /** 80225 * @protected 80226 */ 80227 resetItemSize: function () { 80228 this.itemSize = [ 80229 parseFloat(this.get('itemWidth')), 80230 parseFloat(this.get('itemHeight')) 80231 ]; 80232 }, 80233 80234 /** 80235 * @public 80236 */ 80237 isCategory: function () { 80238 return !!this.option.categories; 80239 }, 80240 80241 /** 80242 * @public 80243 * @abstract 80244 */ 80245 setSelected: noop$2, 80246 80247 /** 80248 * @public 80249 * @abstract
80250 * @param {*|module:echarts/data/List} valueOrData 80251 * @param {number} dataIndex 80252 * @return {string} state See this.stateList 80253 */ 80254 getValueState: noop$2, 80255 80256 /** 80257 * FIXME 80258 * Do not publish to thirt-part-dev temporarily 80259 * util the interface is stable. (Should it return 80260 * a function but not visual meta?) 80261 * 80262 * @pubilc 80263 * @abstract 80264 * @param {Function} getColorVisual 80265 * params: value, valueState 80266 * return: color 80267 * @return {Object} visualMeta 80268 * should includes {stops, outerColors} 80269 * outerColor means [colorBeyondMinValue, colorBeyondMaxValue] 80270 */ 80271 getVisualMeta: noop$2 80272 80273 }); 80274 80275 /* 80276* Licensed to the Apache Software Foundation (ASF) under one 80277* or more contributor license agreements. See the NOTICE file 80278* distributed with this work for additional information 80279* regarding copyright ownership. The ASF licenses this file 80280* to you under the Apache License, Version 2.0 (the 80281* "License"); you may not use this file except in compliance 80282* with the License. You may obtain a copy of the License at 80283* 80284* http://www.apache.org/licenses/LICENSE-2.0 80285* 80286* Unless required by applicable law or agreed to in writing, 80287* software distributed under the License is distributed on an 80288* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 80289* KIND, either express or implied. See the License for the 80290* specific language governing permissions and limitations 80291* under the License. 80292*/ 80293 80294// Constant 80295 var DEFAULT_BAR_BOUND = [20, 140]; 80296 80297 var ContinuousModel = VisualMapModel.extend({ 80298 80299 type: 'visualMap.continuous', 80300 80301 /** 80302 * @protected 80303 */ 80304 defaultOption: { 80305 align: 'auto', // 'auto', 'left', 'right', 'top', 'bottom' 80306 calculable: false, // This prop effect default component type determine, 80307 // See echarts/component/visualMap/typeDefaulter. 80308 range: null, // selected range. In default case `range` is [min, max] 80309 // and can auto change along with modification of min max, 80310 // util use specifid a range. 80311 realtime: true, // Whether realtime update. 80312 itemHeight: null, // The length of the range control edge. 80313 itemWidth: null, // The length of the other side. 80314 hoverLink: true, // Enable hover highlight. 80315 hoverLinkDataSize: null,// The size of hovered data. 80316 hoverLinkOnHandle: null // Whether trigger hoverLink when hover handle. 80317 // If not specified, follow the value of `realtime`. 80318 }, 80319 80320 /** 80321 * @override 80322 */ 80323 optionUpdated: function (newOption, isInit) { 80324 ContinuousModel.superApply(this, 'optionUpdated', arguments); 80325 80326 this.resetExtent(); 80327 80328 this.resetVisual(function (mappingOption) { 80329 mappingOption.mappingMethod = 'linear'; 80330 mappingOption.dataExtent = this.getExtent(); 80331 }); 80332 80333 this._resetRange(); 80334 }, 80335 80336 /** 80337 * @protected 80338 * @override 80339 */ 80340 resetItemSize: function () { 80341 ContinuousModel.superApply(this, 'resetItemSize', arguments); 80342 80343 var itemSize = this.itemSize; 80344 80345 this._orient === 'horizontal' && itemSize.reverse(); 80346 80347 (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]); 80348 (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]); 80349 }, 80350 80351 /** 80352 * @private 80353 */ 80354 _resetRange: function () { 80355 var dataExtent = this.getExtent(); 80356 var range = this.option.range; 80357 80358 if (!range || range.auto) { 80359 // `range` should always be array (so we dont use other 80360 // value like 'auto') for user-friend. (consider getOption). 80361 dataExtent.auto = 1; 80362 this.option.range = dataExtent; 80363 } 80364 else if (isArray(range)) { 80365 if (range[0] > range[1]) { 80366 range.reverse(); 80367 } 80368 range[0] = Math.max(range[0], dataExtent[0]); 80369 range[1] = Math.min(range[1], dataExtent[1]); 80370 } 80371 }, 80372 80373 /** 80374 * @protected 80375 * @override 80376 */ 80377 completeVisualOption: function () { 80378 VisualMapModel.prototype.completeVisualOption.apply(this, arguments); 80379 80380 each$1(this.stateList, function (state) { 80381 var symbolSize = this.option.controller[state].symbolSize; 80382 if (symbolSize && symbolSize[0] !== symbolSize[1]) { 80383 symbolSize[0] = 0; // For good looking. 80384 } 80385 }, this); 80386 }, 80387 80388 /** 80389 * @override 80390 */ 80391 setSelected: function (selected) { 80392 this.option.range = selected.slice(); 80393 this._resetRange(); 80394 }, 80395 80396 /** 80397 * @public 80398 */ 80399 getSelected: function () { 80400 var dataExtent = this.getExtent(); 80401 80402 var dataInterval = asc( 80403 (this.get('range') || []).slice() 80404 ); 80405 80406 // Clamp 80407 dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]); 80408 dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]); 80409 dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]); 80410 dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]); 80411 80412 return dataInterval; 80413 }, 80414 80415 /** 80416 * @override 80417 */ 80418 getValueState: function (value) { 80419 var range = this.option.range; 80420 var dataExtent = this.getExtent(); 80421 80422 // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'. 80423 // range[1] is processed likewise. 80424 return ( 80425 (range[0] <= dataExtent[0] || range[0] <= value) 80426 && (range[1] >= dataExtent[1] || value <= range[1]) 80427 ) ? 'inRange' : 'outOfRange'; 80428 }, 80429 80430 /** 80431 * @params {Array.<number>} range target value: range[0] <= value && value <= range[1] 80432 * @return {Array.<Object>} [{seriesId, dataIndices: <Array.<number>>}, ...] 80433 */ 80434 findTargetDataIndices: function (range) { 80435 var result = []; 80436 80437 this.eachTargetSeries(function (seriesModel) { 80438 var dataIndices = []; 80439 var data = seriesModel.getData(); 80440 80441 data.each(this.getDataDimension(data), function (value, dataIndex) { 80442 range[0] <= value && value <= range[1] && dataIndices.push(dataIndex); 80443 }, this); 80444 80445 result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); 80446 }, this); 80447 80448 return result; 80449 }, 80450 80451 /** 80452 * @implement 80453 */
vendor: 4,856 bytes, lines 80454-80596
80454 getVisualMeta: function (getColorVisual) { 80455 var oVals = getColorStopValues(this, 'outOfRange', this.getExtent()); 80456 var iVals = getColorStopValues(this, 'inRange', this.option.range.slice()); 80457 var stops = []; 80458 80459 function setStop(value, valueState) { 80460 stops.push({ 80461 value: value, 80462 color: getColorVisual(value, valueState) 80463 }); 80464 } 80465 80466 // Format to: outOfRange -- inRange -- outOfRange. 80467 var iIdx = 0; 80468 var oIdx = 0; 80469 var iLen = iVals.length; 80470 var oLen = oVals.length; 80471 80472 for (; oIdx < oLen && (!iVals.length || oVals[oIdx] <= iVals[0]); oIdx++) { 80473 // If oVal[oIdx] === iVals[iIdx], oVal[oIdx] should be ignored. 80474 if (oVals[oIdx] < iVals[iIdx]) { 80475 setStop(oVals[oIdx], 'outOfRange'); 80476 } 80477 } 80478 for (var first = 1; iIdx < iLen; iIdx++, first = 0) { 80479 // If range is full, value beyond min, max will be clamped. 80480 // make a singularity 80481 first && stops.length && setStop(iVals[iIdx], 'outOfRange'); 80482 setStop(iVals[iIdx], 'inRange'); 80483 } 80484 for (var first = 1; oIdx < oLen; oIdx++) { 80485 if (!iVals.length || iVals[iVals.length - 1] < oVals[oIdx]) { 80486 // make a singularity 80487 if (first) { 80488 stops.length && setStop(stops[stops.length - 1].value, 'outOfRange'); 80489 first = 0; 80490 } 80491 setStop(oVals[oIdx], 'outOfRange'); 80492 } 80493 } 80494 80495 var stopsLen = stops.length; 80496 80497 return { 80498 stops: stops, 80499 outerColors: [ 80500 stopsLen ? stops[0].color : 'transparent', 80501 stopsLen ? stops[stopsLen - 1].color : 'transparent' 80502 ] 80503 }; 80504 } 80505 80506 }); 80507 80508 function getColorStopValues(visualMapModel, valueState, dataExtent) { 80509 if (dataExtent[0] === dataExtent[1]) { 80510 return dataExtent.slice(); 80511 } 80512 80513 // When using colorHue mapping, it is not linear color any more. 80514 // Moreover, canvas gradient seems not to be accurate linear. 80515 // FIXME 80516 // Should be arbitrary value 100? or based on pixel size? 80517 var count = 200; 80518 var step = (dataExtent[1] - dataExtent[0]) / count; 80519 80520 var value = dataExtent[0]; 80521 var stopValues = []; 80522 for (var i = 0; i <= count && value < dataExtent[1]; i++) { 80523 stopValues.push(value); 80524 value += step; 80525 } 80526 stopValues.push(dataExtent[1]); 80527 80528 return stopValues; 80529 } 80530 80531 /* 80532* Licensed to the Apache Software Foundation (ASF) under one 80533* or more contributor license agreements. See the NOTICE file 80534* distributed with this work for additional information 80535* regarding copyright ownership. The ASF licenses this file 80536* to you under the Apache License, Version 2.0 (the 80537* "License"); you may not use this file except in compliance 80538* with the License. You may obtain a copy of the License at 80539* 80540* http://www.apache.org/licenses/LICENSE-2.0 80541* 80542* Unless required by applicable law or agreed to in writing, 80543* software distributed under the License is distributed on an 80544* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 80545* KIND, either express or implied. See the License for the 80546* specific language governing permissions and limitations 80547* under the License. 80548*/ 80549 80550 var VisualMapView = extendComponentView({ 80551 80552 type: 'visualMap', 80553 80554 /** 80555 * @readOnly 80556 * @type {Object} 80557 */ 80558 autoPositionValues: {left: 1, right: 1, top: 1, bottom: 1}, 80559 80560 init: function (ecModel, api) { 80561 /** 80562 * @readOnly 80563 * @type {module:echarts/model/Global} 80564 */ 80565 this.ecModel = ecModel; 80566 80567 /** 80568 * @readOnly 80569 * @type {module:echarts/ExtensionAPI} 80570 */ 80571 this.api = api; 80572 80573 /** 80574 * @readOnly 80575 * @type {module:echarts/component/visualMap/visualMapModel} 80576 */ 80577 this.visualMapModel; 80578 }, 80579 80580 /** 80581 * @protected 80582 */ 80583 render: function (visualMapModel, ecModel, api, payload) { 80584 this.visualMapModel = visualMapModel; 80585 80586 if (visualMapModel.get('show') === false) { 80587 this.group.removeAll(); 80588 return; 80589 } 80590 80591 this.doRender.apply(this, arguments); 80592 }, 80593 80594 /** 80595 * @protected 80596 */
80597 renderBackground: function (group) { 80598 var visualMapModel = this.visualMapModel; 80599 var padding = normalizeCssArray$1(visualMapModel.get('padding') || 0); 80600 var rect = group.getBoundingRect(); 80601 80602 group.add(new Rect({ 80603 z2: -1, // Lay background rect on the lowest layer. 80604 silent: true, 80605 shape: { 80606 x: rect.x - padding[3], 80607 y: rect.y - padding[0], 80608 width: rect.width + padding[3] + padding[1], 80609 height: rect.height + padding[0] + padding[2] 80610 }, 80611 style: { 80612 fill: visualMapModel.get('backgroundColor'), 80613 stroke: visualMapModel.get('borderColor'), 80614 lineWidth: visualMapModel.get('borderWidth') 80615 } 80616 })); 80617 }, 80618 80619 /** 80620 * @protected 80621 * @param {number} targetValue can be Infinity or -Infinity 80622 * @param {string=} visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize' 80623 * @param {Object} [opts] 80624 * @param {string=} [opts.forceState] Specify state, instead of using getValueState method. 80625 * @param {string=} [opts.convertOpacityToAlpha=false] For color gradient in controller widget. 80626 * @return {*} Visual value. 80627 */ 80628 getControllerVisual: function (targetValue, visualCluster, opts) { 80629 opts = opts || {}; 80630 80631 var forceState = opts.forceState; 80632 var visualMapModel = this.visualMapModel; 80633 var visualObj = {}; 80634 80635 // Default values. 80636 if (visualCluster === 'symbol') { 80637 visualObj.symbol = visualMapModel.get('itemSymbol'); 80638 } 80639 if (visualCluster === 'color') { 80640 var defaultColor = visualMapModel.get('contentColor'); 80641 visualObj.color = defaultColor; 80642 } 80643 80644 function getter(key) { 80645 return visualObj[key]; 80646 } 80647 80648 function setter(key, value) { 80649 visualObj[key] = value; 80650 } 80651 80652 var mappings = visualMapModel.controllerVisuals[ 80653 forceState || visualMapModel.getValueState(targetValue) 80654 ]; 80655 var visualTypes = VisualMapping.prepareVisualTypes(mappings); 80656 80657 each$1(visualTypes, function (type) { 80658 var visualMapping = mappings[type]; 80659 if (opts.convertOpacityToAlpha && type === 'opacity') { 80660 type = 'colorAlpha'; 80661 visualMapping = mappings.__alphaForOpacity; 80662 } 80663 if (VisualMapping.dependsOn(type, visualCluster)) { 80664 visualMapping && visualMapping.applyVisual( 80665 targetValue, getter, setter 80666 ); 80667 } 80668 }); 80669 80670 return visualObj[visualCluster]; 80671 }, 80672 80673 /** 80674 * @protected 80675 */ 80676 positionGroup: function (group) { 80677 var model = this.visualMapModel; 80678 var api = this.api; 80679 80680 positionElement( 80681 group, 80682 model.getBoxLayoutParams(), 80683 {width: api.getWidth(), height: api.getHeight()} 80684 ); 80685 }, 80686 80687 /** 80688 * @protected 80689 * @abstract 80690 */ 80691 doRender: noop 80692 80693 }); 80694 80695 /* 80696* Licensed to the Apache Software Foundation (ASF) under one 80697* or more contributor license agreements. See the NOTICE file 80698* distributed with this work for additional information 80699* regarding copyright ownership. The ASF licenses this file 80700* to you under the Apache License, Version 2.0 (the 80701* "License"); you may not use this file except in compliance 80702* with the License. You may obtain a copy of the License at 80703* 80704* http://www.apache.org/licenses/LICENSE-2.0 80705* 80706* Unless required by applicable law or agreed to in writing, 80707* software distributed under the License is distributed on an 80708* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 80709* KIND, either express or implied. See the License for the 80710* specific language governing permissions and limitations 80711* under the License. 80712*/ 80713 80714 /** 80715 * @param {module:echarts/component/visualMap/VisualMapModel} visualMapModel\ 80716 * @param {module:echarts/ExtensionAPI} api 80717 * @param {Array.<number>} itemSize always [short, long] 80718 * @return {string} 'left' or 'right' or 'top' or 'bottom' 80719 */ 80720 function getItemAlign(visualMapModel, api, itemSize) { 80721 var modelOption = visualMapModel.option; 80722 var itemAlign = modelOption.align; 80723 80724 if (itemAlign != null && itemAlign !== 'auto') { 80725 return itemAlign; 80726 } 80727 80728 // Auto decision align. 80729 var ecSize = {width: api.getWidth(), height: api.getHeight()}; 80730 var realIndex = modelOption.orient === 'horizontal' ? 1 : 0; 80731 80732 var paramsSet = [ 80733 ['left', 'right', 'width'], 80734 ['top', 'bottom', 'height'] 80735 ]; 80736 var reals = paramsSet[realIndex]; 80737 var fakeValue = [0, null, 10]; 80738 80739 var layoutInput = {}; 80740 for (var i = 0; i < 3; i++) { 80741 layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i]; 80742 layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]]; 80743 } 80744 80745 var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex]; 80746 var rect = getLayoutRect(layoutInput, ecSize, modelOption.padding); 80747 80748 return reals[ 80749 (rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5 80750 < ecSize[rParam[1]] * 0.5 ? 0 : 1 80751 ]; 80752 } 80753 80754 /** 80755 * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and 80756 * dataIndexInside means filtered index. 80757 */ 80758 function convertDataIndex(batch) { 80759 each$1(batch || [], function (batchItem) { 80760 if (batch.dataIndex != null) { 80761 batch.dataIndexInside = batch.dataIndex; 80762 batch.dataIndex = null; 80763 } 80764 }); 80765 return batch; 80766 } 80767 80768 /* 80769* Licensed to the Apache Software Foundation (ASF) under one 80770* or more contributor license agreements. See the NOTICE file 80771* distributed with this work for additional information 80772* regarding copyright ownership. The ASF licenses this file 80773* to you under the Apache License, Version 2.0 (the 80774* "License");
80774 you may not use this file except in compliance 80775* with the License. You may obtain a copy of the License at 80776* 80777* http://www.apache.org/licenses/LICENSE-2.0 80778* 80779* Unless required by applicable law or agreed to in writing, 80780* software distributed under the License is distributed on an 80781* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 80782* KIND, either express or implied. See the License for the 80783* specific language governing permissions and limitations 80784* under the License. 80785*/ 80786 80787 var linearMap$3 = linearMap; 80788 var each$26 = each$1; 80789 var mathMin$7 = Math.min; 80790 var mathMax$7 = Math.max; 80791 80792// Arbitrary value 80793 var HOVER_LINK_SIZE = 12; 80794 var HOVER_LINK_OUT = 6; 80795 80796// Notice: 80797// Any "interval" should be by the order of [low, high]. 80798// "handle0" (handleIndex === 0) maps to 80799// low data value: this._dataInterval[0] and has low coord. 80800// "handle1" (handleIndex === 1) maps to 80801// high data value: this._dataInterval[1] and has high coord. 80802// The logic of transform is implemented in this._createBarGroup. 80803 80804 var ContinuousView = VisualMapView.extend({ 80805 80806 type: 'visualMap.continuous', 80807 80808 /** 80809 * @override 80810 */ 80811 init: function () { 80812 80813 ContinuousView.superApply(this, 'init', arguments); 80814 80815 /** 80816 * @private 80817 */ 80818 this._shapes = {}; 80819 80820 /** 80821 * @private 80822 */ 80823 this._dataInterval = []; 80824 80825 /** 80826 * @private 80827 */ 80828 this._handleEnds = []; 80829 80830 /** 80831 * @private 80832 */ 80833 this._orient; 80834 80835 /** 80836 * @private 80837 */ 80838 this._useHandle; 80839 80840 /** 80841 * @private 80842 */ 80843 this._hoverLinkDataIndices = []; 80844 80845 /** 80846 * @private 80847 */ 80848 this._dragging; 80849 80850 /** 80851 * @private 80852 */ 80853 this._hovering; 80854 }, 80855 80856 /** 80857 * @protected 80858 * @override 80859 */ 80860 doRender: function (visualMapModel, ecModel, api, payload) { 80861 if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) { 80862 this._buildView(); 80863 } 80864 }, 80865 80866 /** 80867 * @private 80868 */ 80869 _buildView: function () { 80870 this.group.removeAll(); 80871 80872 var visualMapModel = this.visualMapModel; 80873 var thisGroup = this.group; 80874 80875 this._orient = visualMapModel.get('orient'); 80876 this._useHandle = visualMapModel.get('calculable'); 80877 80878 this._resetInterval(); 80879 80880 this._renderBar(thisGroup); 80881 80882 var dataRangeText = visualMapModel.get('text'); 80883 this._renderEndsText(thisGroup, dataRangeText, 0); 80884 this._renderEndsText(thisGroup, dataRangeText, 1); 80885 80886 // Do this for background size calculation. 80887 this._updateView(true); 80888 80889 // After updating view, inner shapes is built completely, 80890 // and then background can be rendered. 80891 this.renderBackground(thisGroup); 80892 80893 // Real update view 80894 this._updateView(); 80895 80896 this._enableHoverLinkToSeries(); 80897 this._enableHoverLinkFromSeries(); 80898 80899 this.positionGroup(thisGroup); 80900 }, 80901 80902 /** 80903 * @private 80904 */ 80905 _renderEndsText: function (group, dataRangeText, endsIndex) { 80906 if (!dataRangeText) { 80907 return; 80908 } 80909 80910 // Compatible with ec2, text[0] map to high value, text[1] map low value. 80911 var text = dataRangeText[1 - endsIndex]; 80912 text = text != null ? text + '' : ''; 80913 80914 var visualMapModel = this.visualMapModel; 80915 var textGap = visualMapModel.get('textGap'); 80916 var itemSize = visualMapModel.itemSize; 80917 80918 var barGroup = this._shapes.barGroup; 80919 var position = this._applyTransform( 80920 [ 80921 itemSize[0] / 2, 80922 endsIndex === 0 ? -textGap : itemSize[1] + textGap 80923 ], 80924 barGroup 80925 ); 80926 var align = this._applyTransform( 80927 endsIndex === 0 ? 'bottom' : 'top', 80928 barGroup 80929 ); 80930 var orient = this._orient; 80931 var textStyleModel = this.visualMapModel.textStyleModel; 80932 80933 this.group.add(new Text({ 80934 style: { 80935 x: position[0], 80936 y: position[1], 80937 textVerticalAlign: orient === 'horizontal' ? 'middle' : align, 80938 textAlign: orient === 'horizontal' ? align : 'center', 80939 text: text, 80940 textFont: textStyleModel.getFont(), 80941 textFill: textStyleModel.getTextColor() 80942 } 80943 })); 80944 }, 80945 80946 /** 80947 * @private 80948 */ 80949 _renderBar: function (targetGroup) { 80950 var visualMapModel = this.visualMapModel; 80951 var shapes = this._shapes; 80952 var itemSize = visualMapModel.itemSize; 80953 var orient = this._orient; 80954 var useHandle = this._useHandle; 80955 var itemAlign = getItemAlign(visualMapModel, this.api, itemSize); 80956 var barGroup = shapes.barGroup = this._createBarGroup(itemAlign); 80957 80958 // Bar 80959 barGroup.add(shapes.outOfRange = createPolygon()); 80960 barGroup.add(shapes.inRange = createPolygon( 80961 null, 80962 useHandle ? getCursor$1(this._orient) : null, 80963 bind(this._dragHandle, this, 'all', false), 80964 bind(this._dragHandle, this, 'all', true) 80965 )); 80966 80967 var textRect = visualMapModel.textStyleModel.getTextRect
80967('Ã¥âºÂ½'); 80968 var textSize = mathMax$7(textRect.width, textRect.height); 80969 80970 // Handle 80971 if (useHandle) { 80972 shapes.handleThumbs = []; 80973 shapes.handleLabels = []; 80974 shapes.handleLabelPoints = []; 80975 80976 this._createHandle(barGroup, 0, itemSize, textSize, orient, itemAlign); 80977 this._createHandle(barGroup, 1, itemSize, textSize, orient, itemAlign); 80978 } 80979 80980 this._createIndicator(barGroup, itemSize, textSize, orient); 80981 80982 targetGroup.add(barGroup); 80983 }, 80984 80985 /** 80986 * @private 80987 */ 80988 _createHandle: function (barGroup, handleIndex, itemSize, textSize, orient) { 80989 var onDrift = bind(this._dragHandle, this, handleIndex, false); 80990 var onDragEnd = bind(this._dragHandle, this, handleIndex, true); 80991 var handleThumb = createPolygon( 80992 createHandlePoints(handleIndex, textSize), 80993 getCursor$1(this._orient), 80994 onDrift, 80995 onDragEnd 80996 ); 80997 handleThumb.position[0] = itemSize[0]; 80998 barGroup.add(handleThumb); 80999 81000 // Text is always horizontal layout but should not be effected by 81001 // transform (orient/inverse). So label is built separately but not 81002 // use zrender/graphic/helper/RectText, and is located based on view 81003 // group (according to handleLabelPoint) but not barGroup. 81004 var textStyleModel = this.visualMapModel.textStyleModel; 81005 var handleLabel = new Text({ 81006 draggable: true, 81007 drift: onDrift, 81008 onmousemove: function (e) { 81009 // Fot mobile devicem, prevent screen slider on the button. 81010 stop(e.event); 81011 }, 81012 ondragend: onDragEnd, 81013 style: { 81014 x: 0, y: 0, text: '', 81015 textFont: textStyleModel.getFont(), 81016 textFill: textStyleModel.getTextColor() 81017 } 81018 }); 81019 this.group.add(handleLabel); 81020 81021 var handleLabelPoint = [ 81022 orient === 'horizontal' 81023 ? textSize / 2 81024 : textSize * 1.5, 81025 orient === 'horizontal' 81026 ? (handleIndex === 0 ? -(textSize * 1.5) : (textSize * 1.5)) 81027 : (handleIndex === 0 ? -textSize / 2 : textSize / 2) 81028 ]; 81029 81030 var shapes = this._shapes; 81031 shapes.handleThumbs[handleIndex] = handleThumb; 81032 shapes.handleLabelPoints[handleIndex] = handleLabelPoint; 81033 shapes.handleLabels[handleIndex] = handleLabel; 81034 }, 81035 81036 /** 81037 * @private 81038 */ 81039 _createIndicator: function (barGroup, itemSize, textSize, orient) { 81040 var indicator = createPolygon([[0, 0]], 'move'); 81041 indicator.position[0] = itemSize[0]; 81042 indicator.attr({invisible: true, silent: true}); 81043 barGroup.add(indicator); 81044 81045 var textStyleModel = this.visualMapModel.textStyleModel; 81046 var indicatorLabel = new Text({ 81047 silent: true, 81048 invisible: true, 81049 style: { 81050 x: 0, y: 0, text: '', 81051 textFont: textStyleModel.getFont(), 81052 textFill: textStyleModel.getTextColor() 81053 } 81054 }); 81055 this.group.add(indicatorLabel); 81056 81057 var indicatorLabelPoint = [ 81058 orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT + 3, 81059 0 81060 ]; 81061 81062 var shapes = this._shapes; 81063 shapes.indicator = indicator; 81064 shapes.indicatorLabel = indicatorLabel; 81065 shapes.indicatorLabelPoint = indicatorLabelPoint; 81066 }, 81067 81068 /** 81069 * @private 81070 */ 81071 _dragHandle: function (handleIndex, isEnd, dx, dy) { 81072 if (!this._useHandle) { 81073 return; 81074 } 81075 81076 this._dragging = !isEnd;
81077 81078 if (!isEnd) { 81079 // Transform dx, dy to bar coordination. 81080 var vertex = this._applyTransform([dx, dy], this._shapes.barGroup, true); 81081 this._updateInterval(handleIndex, vertex[1]); 81082 81083 // Considering realtime, update view should be executed 81084 // before dispatch action. 81085 this._updateView(); 81086 } 81087 81088 // dragEnd do not dispatch action when realtime. 81089 if (isEnd === !this.visualMapModel.get('realtime')) { // jshint ignore:line 81090 this.api.dispatchAction({ 81091 type: 'selectDataRange', 81092 from: this.uid, 81093 visualMapId: this.visualMapModel.id, 81094 selected: this._dataInterval.slice() 81095 }); 81096 } 81097 81098 if (isEnd) { 81099 !this._hovering && this._clearHoverLinkToSeries(); 81100 } 81101 else if (useHoverLinkOnHandle(this.visualMapModel)) { 81102 this._doHoverLinkToSeries(this._handleEnds[handleIndex], false); 81103 } 81104 }, 81105 81106 /** 81107 * @private 81108 */ 81109 _resetInterval: function () { 81110 var visualMapModel = this.visualMapModel; 81111 81112 var dataInterval = this._dataInterval = visualMapModel.getSelected(); 81113 var dataExtent = visualMapModel.getExtent(); 81114 var sizeExtent = [0, visualMapModel.itemSize[1]]; 81115 81116 this._handleEnds = [ 81117 linearMap$3(dataInterval[0], dataExtent, sizeExtent, true), 81118 linearMap$3(dataInterval[1], dataExtent, sizeExtent, true) 81119 ]; 81120 }, 81121 81122 /** 81123 * @private 81124 * @param {(number|string)} handleIndex 0 or 1 or 'all' 81125 * @param {number} dx 81126 * @param {number} dy 81127 */ 81128 _updateInterval: function (handleIndex, delta) { 81129 delta = delta || 0; 81130 var visualMapModel = this.visualMapModel; 81131 var handleEnds = this._handleEnds; 81132 var sizeExtent = [0, visualMapModel.itemSize[1]]; 81133 81134 sliderMove( 81135 delta, 81136 handleEnds, 81137 sizeExtent, 81138 handleIndex, 81139 // cross is forbiden 81140 0 81141 ); 81142 81143 var dataExtent = visualMapModel.getExtent(); 81144 // Update data interval. 81145 this._dataInterval = [ 81146 linearMap$3(handleEnds[0], sizeExtent, dataExtent, true), 81147 linearMap$3(handleEnds[1], sizeExtent, dataExtent, true) 81148 ]; 81149 }, 81150 81151 /** 81152 * @private 81153 */ 81154 _updateView: function (forSketch) { 81155 var visualMapModel = this.visualMapModel; 81156 var dataExtent = visualMapModel.getExtent(); 81157 var shapes = this._shapes; 81158 81159 var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]]; 81160 var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds; 81161 81162 var visualInRange = this._createBarVisual( 81163 this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange' 81164 ); 81165 var visualOutOfRange = this._createBarVisual( 81166 dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange' 81167 ); 81168 81169 shapes.inRange 81170 .setStyle({ 81171 fill: visualInRange.barColor, 81172 opacity: visualInRange.opacity 81173 }) 81174 .setShape('points', visualInRange.barPoints); 81175 shapes.outOfRange 81176 .setStyle({ 81177 fill: visualOutOfRange.barColor, 81178 opacity: visualOutOfRange.opacity 81179 }) 81180 .setShape('points', visualOutOfRange.barPoints); 81181 81182 this._updateHandle(inRangeHandleEnds, visualInRange); 81183 }, 81184 81185 /** 81186 * @private 81187 */ 81188 _createBarVisual: function (dataInterval, dataExtent, handleEnds, forceState) { 81189 var opts = { 81190 forceState: forceState, 81191 convertOpacityToAlpha: true 81192 }; 81193 var colorStops = this._makeColorGradient(dataInterval, opts); 81194 81195 var symbolSizes = [ 81196 this.getControllerVisual(dataInterval[0], 'symbolSize', opts), 81197 this.getControllerVisual(dataInterval[1], 'symbolSize', opts) 81198 ]; 81199 var barPoints = this._createBarPoints(handleEnds, symbolSizes); 81200 81201 return { 81202 barColor: new LinearGradient(0, 0, 0, 1, colorStops), 81203 barPoints: barPoints, 81204 handlesColor: [ 81205 colorStops[0].color, 81206 colorStops[colorStops.length - 1].color 81207 ] 81208 }; 81209 }, 81210 81211 /** 81212 * @private 81213 */ 81214 _makeColorGradient: function (dataInterval, opts) { 81215 // Considering colorHue, which is not linear, so we have to sample 81216 // to calculate gradient color stops, but not only caculate head 81217 // and tail. 81218 var sampleNumber = 100; // Arbitrary value. 81219 var colorStops = []; 81220 var step = (dataInterval[1] - dataInterval[0]) / sampleNumber; 81221 81222 colorStops.push({ 81223 color: this.getControllerVisual(dataInterval[0], 'color', opts), 81224 offset: 0 81225 }); 81226 81227 for (var i = 1; i < sampleNumber; i++) { 81228 var currValue = dataInterval[0] + step * i; 81229 if (currValue > dataInterval[1]) { 81230 break; 81231 } 81232 colorStops.push({ 81233 color: this.getControllerVisual(currValue, 'color', opts), 81234 offset: i / sampleNumber 81235 }); 81236 } 81237 81238 colorStops.push({ 81239 color: this.getControllerVisual(dataInterval[1], 'color', opts), 81240 offset: 1 81241 }); 81242 81243 return colorStops; 81244 }, 81245 81246 /** 81247 * @private 81248 */ 81249 _createBarPoints: function (handleEnds, symbolSizes) { 81250 var itemSize = this.visualMapModel.itemSize; 81251 81252 return [ 81253 [itemSize[0] - symbolSizes[0], handleEnds[0]], 81254 [itemSize[0], handleEnds[0]], 81255 [itemSize[0], handleEnds[1]], 81256 [itemSize[0] - symbolSizes[1], handleEnds[1]] 81257 ]; 81258 }, 81259 81260 /** 81261 * @private 81262 */ 81263 _createBarGroup: function (itemAlign) { 81264 var orient = this._orient; 81265 var inverse = this.visualMapModel.get('inverse'); 81266 81267 return new Group( 81268 (orient === 'horizontal' && !inverse) 81269 ? {scale: itemAlign === 'bottom' ? [1, 1] : [-1, 1], rotation: Math.PI / 2} 81270 : (orient === 'horizontal' && inverse) 81271 ? {scale: itemAlign === 'bottom' ? [-1, 1] : [1, 1], rotation: -Math.PI / 2} 81272 : (orient === 'vertical' && !inverse) 81273 ? {scale: itemAlign === 'left' ? [1, -1] : [-1, -1]} 81274 : {scale: itemAlign === 'left' ? [1, 1] : [-1, 1]} 81275 ); 81276 }, 81277 81278 /** 81279 * @private 81280 */ 81281 _updateHandle: function (handleEnds, visualInRange) { 81282 if (!this._useHandle) { 81283 return; 81284 } 81285 81286 var shapes = this._shapes; 81287 var visualMapModel = this.visualMapModel; 81288 var handleThumbs = shapes.handleThumbs; 81289 var handleLabels = shapes.handleLabels;
81290 81291 each$26([0, 1], function (handleIndex) { 81292 var handleThumb = handleThumbs[handleIndex]; 81293 handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]); 81294 handleThumb.position[1] = handleEnds[handleIndex]; 81295 81296 // Update handle label position. 81297 var textPoint = applyTransform$1( 81298 shapes.handleLabelPoints[handleIndex], 81299 getTransform(handleThumb, this.group) 81300 ); 81301 handleLabels[handleIndex].setStyle({ 81302 x: textPoint[0], 81303 y: textPoint[1], 81304 text: visualMapModel.formatValueText(this._dataInterval[handleIndex]), 81305 textVerticalAlign: 'middle', 81306 textAlign: this._applyTransform( 81307 this._orient === 'horizontal' 81308 ? (handleIndex === 0 ? 'bottom' : 'top') 81309 : 'left', 81310 shapes.barGroup 81311 ) 81312 }); 81313 }, this); 81314 }, 81315 81316 /** 81317 * @private 81318 * @param {number} cursorValue 81319 * @param {number} textValue 81320 * @param {string} [rangeSymbol] 81321 * @param {number} [halfHoverLinkSize] 81322 */ 81323 _showIndicator: function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) { 81324 var visualMapModel = this.visualMapModel; 81325 var dataExtent = visualMapModel.getExtent(); 81326 var itemSize = visualMapModel.itemSize; 81327 var sizeExtent = [0, itemSize[1]]; 81328 var pos = linearMap$3(cursorValue, dataExtent, sizeExtent, true); 81329 81330 var shapes = this._shapes; 81331 var indicator = shapes.indicator; 81332 if (!indicator) { 81333 return; 81334 } 81335 81336 indicator.position[1] = pos; 81337 indicator.attr('invisible', false); 81338 indicator.setShape('points', createIndicatorPoints( 81339 !!rangeSymbol, halfHoverLinkSize, pos, itemSize[1] 81340 )); 81341 81342 var opts = {convertOpacityToAlpha: true}; 81343 var color = this.getControllerVisual(cursorValue, 'color', opts); 81344 indicator.setStyle('fill', color); 81345 81346 // Update handle label position. 81347 var textPoint = applyTransform$1( 81348 shapes.indicatorLabelPoint, 81349 getTransform(indicator, this.group) 81350 ); 81351 81352 var indicatorLabel = shapes.indicatorLabel; 81353 indicatorLabel.attr('invisible', false); 81354 var align = this._applyTransform('left', shapes.barGroup); 81355 var orient = this._orient; 81356 indicatorLabel.setStyle({ 81357 text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue), 81358 textVerticalAlign: orient === 'horizontal' ? align : 'middle', 81359 textAlign: orient === 'horizontal' ? 'center' : align, 81360 x: textPoint[0], 81361 y: textPoint[1] 81362 }); 81363 }, 81364 81365 /** 81366 * @private 81367 */ 81368 _enableHoverLinkToSeries: function () { 81369 var self = this; 81370 this._shapes.barGroup 81371 81372 .on('mousemove', function (e) { 81373 self._hovering = true; 81374 81375 if (!self._dragging) { 81376 var itemSize = self.visualMapModel.itemSize; 81377 var pos = self._applyTransform( 81378 [e.offsetX, e.offsetY], self._shapes.barGroup, true, true 81379 ); 81380 // For hover link show when hover handle, which might be 81381 // below or upper than sizeExtent. 81382 pos[1] = mathMin$7(mathMax$7(0, pos[1]), itemSize[1]); 81383 self._doHoverLinkToSeries( 81384 pos[1], 81385 0 <= pos[0] && pos[0] <= itemSize[0] 81386 ); 81387 } 81388 }) 81389 81390 .on('mouseout', function () { 81391 // When mouse is out of handle, hoverLink still need 81392 // to be displayed when realtime is set as false. 81393 self._hovering = false; 81394 !self._dragging && self._clearHoverLinkToSeries(); 81395 }); 81396 }, 81397 81398 /** 81399 * @private 81400 */ 81401 _enableHoverLinkFromSeries: function () { 81402 var zr = this.api.getZr(); 81403 81404 if (this.visualMapModel.option.hoverLink) { 81405 zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this); 81406 zr.on('mouseout', this._hideIndicator, this); 81407 } 81408 else { 81409 this._clearHoverLinkFromSeries(); 81410 } 81411 }, 81412 81413 /** 81414 * @private 81415 */ 81416 _doHoverLinkToSeries: function (cursorPos, hoverOnBar) { 81417 var visualMapModel = this.visualMapModel; 81418 var itemSize = visualMapModel.itemSize; 81419 81420 if (!visualMapModel.option.hoverLink) { 81421 return; 81422 } 81423 81424 var sizeExtent = [0, itemSize[1]]; 81425 var dataExtent = visualMapModel.getExtent(); 81426 81427 // For hover link show when hover handle, which might be below or upper than sizeExtent.
81428 cursorPos = mathMin$7(mathMax$7(sizeExtent[0], cursorPos), sizeExtent[1]); 81429 81430 var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent); 81431 var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize]; 81432 var cursorValue = linearMap$3(cursorPos, sizeExtent, dataExtent, true); 81433 var valueRange = [ 81434 linearMap$3(hoverRange[0], sizeExtent, dataExtent, true), 81435 linearMap$3(hoverRange[1], sizeExtent, dataExtent, true) 81436 ]; 81437 // Consider data range is out of visualMap range, see test/visualMap-continuous.html, 81438 // where china and india has very large population. 81439 hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity); 81440 hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity); 81441 81442 // Do not show indicator when mouse is over handle, 81443 // otherwise labels overlap, especially when dragging. 81444 if (hoverOnBar) { 81445 if (valueRange[0] === -Infinity) { 81446 this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize); 81447 } 81448 else if (valueRange[1] === Infinity) { 81449 this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize); 81450 } 81451 else { 81452 this._showIndicator(cursorValue, cursorValue, 'ââ°Ë ', halfHoverLinkSize); 81453 } 81454 } 81455 81456 // When realtime is set as false, handles, which are in barGroup, 81457 // also trigger hoverLink, which help user to realize where they 81458 // focus on when dragging. (see test/heatmap-large.html) 81459 // When realtime is set as true, highlight will not show when hover 81460 // handle, because the label on handle, which displays a exact value 81461 // but not range, might mislead users. 81462 var oldBatch = this._hoverLinkDataIndices; 81463 var newBatch = []; 81464 if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) { 81465 newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange); 81466 } 81467 81468 var resultBatches = compressBatches(oldBatch, newBatch); 81469 81470 this._dispatchHighDown('downplay', convertDataIndex(resultBatches[0])); 81471 this._dispatchHighDown('highlight', convertDataIndex(resultBatches[1])); 81472 }, 81473 81474 /** 81475 * @private 81476 */ 81477 _hoverLinkFromSeriesMouseOver: function (e) { 81478 var el = e.target; 81479 var visualMapModel = this.visualMapModel; 81480 81481 if (!el || el.dataIndex == null) { 81482 return; 81483 } 81484 81485 var dataModel = this.ecModel.getSeriesByIndex(el.seriesIndex); 81486 81487 if (!visualMapModel.isTargetSeries(dataModel)) { 81488 return; 81489 } 81490 81491 var data = dataModel.getData(el.dataType); 81492 var value = data.get(visualMapModel.getDataDimension(data), el.dataIndex, true); 81493 81494 if (!isNaN(value)) { 81495 this._showIndicator(value, value); 81496 } 81497 }, 81498 81499 /** 81500 * @private 81501 */ 81502 _hideIndicator: function () { 81503 var shapes = this._shapes; 81504 shapes.indicator && shapes.indicator.attr('invisible', true); 81505 shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true); 81506 }, 81507 81508 /** 81509 * @private 81510 */ 81511 _clearHoverLinkToSeries: function () { 81512 this._hideIndicator(); 81513 81514 var indices = this._hoverLinkDataIndices; 81515 this._dispatchHighDown('downplay', convertDataIndex(indices)); 81516 81517 indices.length = 0; 81518 }, 81519 81520 /** 81521 * @private 81522 */ 81523 _clearHoverLinkFromSeries: function () { 81524 this._hideIndicator(); 81525 81526 var zr = this.api.getZr(); 81527 zr.off('mouseover', this._hoverLinkFromSeriesMouseOver); 81528 zr.off('mouseout', this._hideIndicator); 81529 }, 81530 81531 /** 81532 * @private 81533 */ 81534 _applyTransform: function (vertex, element, inverse, global) { 81535 var transform = getTransform(element, global ? null : this.group); 81536 81537 return graphic[ 81538 isArray(vertex) ? 'applyTransform' : 'transformDirection' 81539 ](vertex, transform, inverse); 81540 }, 81541 81542 /** 81543 * @private 81544 */ 81545 _dispatchHighDown: function (type, batch) { 81546 batch && batch.length && this.api.dispatchAction({ 81547 type: type, 81548 batch: batch 81549 }); 81550 }, 81551 81552 /** 81553 * @override 81554 */ 81555 dispose: function () { 81556 this._clearHoverLinkFromSeries(); 81557 this._clearHoverLinkToSeries(); 81558 }, 81559 81560 /** 81561 * @override 81562 */ 81563 remove: function () { 81564 this._clearHoverLinkFromSeries(); 81565 this._clearHoverLinkToSeries(); 81566 } 81567 81568 });
81569 81570 function createPolygon(points, cursor, onDrift, onDragEnd) { 81571 return new Polygon({ 81572 shape: {points: points}, 81573 draggable: !!onDrift, 81574 cursor: cursor, 81575 drift: onDrift, 81576 onmousemove: function (e) { 81577 // Fot mobile devicem, prevent screen slider on the button. 81578 stop(e.event); 81579 }, 81580 ondragend: onDragEnd 81581 }); 81582 } 81583 81584 function createHandlePoints(handleIndex, textSize) { 81585 return handleIndex === 0 81586 ? [[0, 0], [textSize, 0], [textSize, -textSize]] 81587 : [[0, 0], [textSize, 0], [textSize, textSize]]; 81588 } 81589 81590 function createIndicatorPoints(isRange, halfHoverLinkSize, pos, extentMax) { 81591 return isRange 81592 ? [ // indicate range 81593 [0, -mathMin$7(halfHoverLinkSize, mathMax$7(pos, 0))], 81594 [HOVER_LINK_OUT, 0], 81595 [0, mathMin$7(halfHoverLinkSize, mathMax$7(extentMax - pos, 0))] 81596 ] 81597 : [ // indicate single value 81598 [0, 0], [5, -5], [5, 5] 81599 ]; 81600 } 81601 81602 function getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) { 81603 var halfHoverLinkSize = HOVER_LINK_SIZE / 2; 81604 var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize'); 81605 if (hoverLinkDataSize) { 81606 halfHoverLinkSize = linearMap$3(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2; 81607 } 81608 return halfHoverLinkSize; 81609 } 81610 81611 function useHoverLinkOnHandle(visualMapModel) { 81612 var hoverLinkOnHandle = visualMapModel.get('hoverLinkOnHandle'); 81613 return !!(hoverLinkOnHandle == null ? visualMapModel.get('realtime') : hoverLinkOnHandle); 81614 } 81615 81616 function getCursor$1(orient) { 81617 return orient === 'vertical' ? 'ns-resize' : 'ew-resize'; 81618 } 81619 81620 /* 81621* Licensed to the Apache Software Foundation (ASF) under one 81622* or more contributor license agreements. See the NOTICE file 81623* distributed with this work for additional information 81624* regarding copyright ownership. The ASF licenses this file 81625* to you under the Apache License, Version 2.0 (the 81626* "License"); you may not use this file except in compliance 81627* with the License. You may obtain a copy of the License at 81628* 81629* http://www.apache.org/licenses/LICENSE-2.0 81630* 81631* Unless required by applicable law or agreed to in writing, 81632* software distributed under the License is distributed on an 81633* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 81634* KIND, either express or implied. See the License for the 81635* specific language governing permissions and limitations 81636* under the License. 81637*/ 81638 81639 var actionInfo$2 = { 81640 type: 'selectDataRange', 81641 event: 'dataRangeSelected', 81642 // FIXME use updateView appears wrong 81643 update: 'update' 81644 }; 81645 81646 registerAction(actionInfo$2, function (payload, ecModel) { 81647 81648 ecModel.eachComponent({mainType: 'visualMap', query: payload}, function (model) { 81649 model.setSelected(payload.selected); 81650 }); 81651 81652 }); 81653 81654 /* 81655* Licensed to the Apache Software Foundation (ASF) under one 81656* or more contributor license agreements. See the NOTICE file 81657* distributed with this work for additional information 81658* regarding copyright ownership. The ASF licenses this file 81659* to you under the Apache License, Version 2.0 (the 81660* "License"); you may not use this file except in compliance 81661* with the License. You may obtain a copy of the License at 81662* 81663* http://www.apache.org/licenses/LICENSE-2.0 81664* 81665* Unless required by applicable law or agreed to in writing, 81666* software distributed under the License is distributed on an 81667* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 81668* KIND, either express or implied. See the License for the 81669* specific language governing permissions and limitations 81670* under the License. 81671*/ 81672 81673 /** 81674 * DataZoom component entry 81675 */ 81676 81677 registerPreprocessor(preprocessor$2); 81678 81679 /* 81680* Licensed to the Apache Software Foundation (ASF) under one 81681* or more contributor license agreements. See the NOTICE file 81682* distributed with this work for additional information 81683* regarding copyright ownership. The ASF licenses this file 81684* to you under the Apache License, Version 2.0 (the 81685* "License");
81685 you may not use this file except in compliance 81686* with the License. You may obtain a copy of the License at 81687* 81688* http://www.apache.org/licenses/LICENSE-2.0 81689* 81690* Unless required by applicable law or agreed to in writing, 81691* software distributed under the License is distributed on an 81692* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 81693* KIND, either express or implied. See the License for the 81694* specific language governing permissions and limitations 81695* under the License. 81696*/ 81697 81698 var PiecewiseModel = VisualMapModel.extend({ 81699 81700 type: 'visualMap.piecewise', 81701 81702 /** 81703 * Order Rule: 81704 * 81705 * option.categories / option.pieces / option.text / option.selected: 81706 * If !option.inverse, 81707 * Order when vertical: ['top', ..., 'bottom']. 81708 * Order when horizontal: ['left', ..., 'right']. 81709 * If option.inverse, the meaning of 81710 * the order should be reversed. 81711 * 81712 * this._pieceList: 81713 * The order is always [low, ..., high]. 81714 * 81715 * Mapping from location to low-high: 81716 * If !option.inverse 81717 * When vertical, top is high. 81718 * When horizontal, right is high. 81719 * If option.inverse, reverse. 81720 */ 81721 81722 /** 81723 * @protected 81724 */ 81725 defaultOption: { 81726 selected: null, // Object. If not specified, means selected. 81727 // When pieces and splitNumber: {'0': true, '5': true} 81728 // When categories: {'cate1': false, 'cate3': true} 81729 // When selected === false, means all unselected. 81730 81731 minOpen: false, // Whether include values that smaller than `min`. 81732 maxOpen: false, // Whether include values that bigger than `max`. 81733 81734 align: 'auto', // 'auto', 'left', 'right' 81735 itemWidth: 20, // When put the controller vertically, it is the length of 81736 // horizontal side of each item. Otherwise, vertical side. 81737 itemHeight: 14, // When put the controller vertically, it is the length of 81738 // vertical side of each item. Otherwise, horizontal side. 81739 itemSymbol: 'roundRect', 81740 pieceList: null, // Each item is Object, with some of those attrs: 81741 // {min, max, lt, gt, lte, gte, value, 81742 // color, colorSaturation, colorAlpha, opacity, 81743 // symbol, symbolSize}, which customize the range or visual 81744 // coding of the certain piece. Besides, see "Order Rule". 81745 categories: null, // category names, like: ['some1', 'some2', 'some3']. 81746 // Attr min/max are ignored when categories set. See "Order Rule" 81747 splitNumber: 5, // If set to 5, auto split five pieces equally. 81748 // If set to 0 and component type not set, component type will be 81749 // determined as "continuous". (It is less reasonable but for ec2 81750 // compatibility, see echarts/component/visualMap/typeDefaulter) 81751 selectedMode: 'multiple', // Can be 'multiple' or 'single'. 81752 itemGap: 10, // The gap between two items, in px. 81753 hoverLink: true, // Enable hover highlight. 81754 81755 showLabel: null // By default, when text is used, label will hide (the logic 81756 // is remained for compatibility reason) 81757 }, 81758 81759 /** 81760 * @override 81761 */ 81762 optionUpdated: function (newOption, isInit) { 81763 PiecewiseModel.superApply(this, 'optionUpdated', arguments); 81764 81765 /** 81766 * The order is always [low, ..., high]. 81767 * [{text: string, interval: Array.<number>}, ...] 81768 * @private 81769 * @type {Array.<Object>} 81770 */ 81771 this._pieceList = []; 81772 81773 this.resetExtent(); 81774 81775 /** 81776 * 'pieces', 'categories', 'splitNumber' 81777 * @type {string} 81778 */ 81779 var mode = this._mode = this._determineMode(); 81780 81781 resetMethods[this._mode].call(this); 81782 81783 this._resetSelected(newOption, isInit); 81784 81785 var categories = this.option.categories; 81786 81787 this.resetVisual(function (mappingOption, state) { 81788 if (mode === 'categories') { 81789 mappingOption.mappingMethod = 'category'; 81790 mappingOption.categories = clone(categories); 81791 } 81792 else { 81793 mappingOption.dataExtent = this.getExtent(); 81794 mappingOption.mappingMethod = 'piecewise'; 81795 mappingOption.pieceList = map(this._pieceList, function (piece) { 81796 var piece = clone(piece); 81797 if (state !== 'inRange') { 81798 // FIXME 81799 // outOfRange do not support special visual in pieces. 81800 piece.visual = null; 81801 } 81802 return piece; 81803 }); 81804 } 81805 }); 81806 }, 81807 81808 /** 81809 * @protected 81810 * @override 81811 */ 81812 completeVisualOption: function () { 81813 // Consider this case: 81814 // visualMap: { 81815 // pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}] 81816 // } 81817 // where no inRange/outOfRange set but only pieces. So we should make 81818 // default inRange/outOfRange for this case, otherwise visual
81818s that only 81819 // appear in `pieces` will not be taken into account in visual encoding. 81820 81821 var option = this.option; 81822 var visualTypesInPieces = {}; 81823 var visualTypes = VisualMapping.listVisualTypes(); 81824 var isCategory = this.isCategory(); 81825 81826 each$1(option.pieces, function (piece) { 81827 each$1(visualTypes, function (visualType) { 81828 if (piece.hasOwnProperty(visualType)) { 81829 visualTypesInPieces[visualType] = 1; 81830 } 81831 }); 81832 }); 81833 81834 each$1(visualTypesInPieces, function (v, visualType) { 81835 var exists = 0; 81836 each$1(this.stateList, function (state) { 81837 exists |= has(option, state, visualType) 81838 || has(option.target, state, visualType); 81839 }, this); 81840 81841 !exists && each$1(this.stateList, function (state) { 81842 (option[state] || (option[state] = {}))[visualType] = visualDefault.get( 81843 visualType, state === 'inRange' ? 'active' : 'inactive', isCategory 81844 ); 81845 }); 81846 }, this); 81847 81848 function has(obj, state, visualType) { 81849 return obj && obj[state] && ( 81850 isObject$1(obj[state]) 81851 ? obj[state].hasOwnProperty(visualType) 81852 : obj[state] === visualType // e.g., inRange: 'symbol' 81853 ); 81854 } 81855 81856 VisualMapModel.prototype.completeVisualOption.apply(this, arguments); 81857 }, 81858 81859 _resetSelected: function (newOption, isInit) { 81860 var thisOption = this.option; 81861 var pieceList = this._pieceList; 81862 81863 // Selected do not merge but all override. 81864 var selected = (isInit ? thisOption : newOption).selected || {}; 81865 thisOption.selected = selected; 81866 81867 // Consider 'not specified' means true. 81868 each$1(pieceList, function (piece, index) { 81869 var key = this.getSelectedMapKey(piece); 81870 if (!selected.hasOwnProperty(key)) { 81871 selected[key] = true; 81872 } 81873 }, this); 81874 81875 if (thisOption.selectedMode === 'single') { 81876 // Ensure there is only one selected. 81877 var hasSel = false; 81878 81879 each$1(pieceList, function (piece, index) { 81880 var key = this.getSelectedMapKey(piece); 81881 if (selected[key]) { 81882 hasSel 81883 ? (selected[key] = false) 81884 : (hasSel = true); 81885 } 81886 }, this); 81887 } 81888 // thisOption.selectedMode === 'multiple', default: all selected. 81889 }, 81890 81891 /** 81892 * @public 81893 */ 81894 getSelectedMapKey: function (piece) { 81895 return this._mode === 'categories' 81896 ? piece.value + '' : piece.index + ''; 81897 }, 81898 81899 /** 81900 * @public 81901 */ 81902 getPieceList: function () { 81903 return this._pieceList; 81904 }, 81905 81906 /** 81907 * @private 81908 * @return {string} 81909 */ 81910 _determineMode: function () { 81911 var option = this.option; 81912 81913 return option.pieces && option.pieces.length > 0 81914 ? 'pieces' 81915 : this.option.categories 81916 ? 'categories' 81917 : 'splitNumber'; 81918 }, 81919 81920 /** 81921 * @public 81922 * @override 81923 */ 81924 setSelected: function (selected) { 81925 this.option.selected = clone(selected); 81926 }, 81927 81928 /** 81929 * @public 81930 * @override 81931 */ 81932 getValueState: function (value) { 81933 var index = VisualMapping.findPieceIndex(value, this._pieceList); 81934 81935 return index != null 81936 ? (this.option.selected[this.getSelectedMapKey(this._pieceList[index])] 81937 ? 'inRange' : 'outOfRange' 81938 ) 81939 : 'outOfRange'; 81940 }, 81941 81942 /** 81943 * @public 81944 * @params {number} pieceIndex piece index in visualMapModel.getPieceList() 81945 * @return {Array.<Object>} [{seriesId, dataIndices: <Array.<number>>}, ...] 81946 */ 81947 findTargetDataIndices: function (pieceIndex) { 81948 var result = []; 81949 81950 this.eachTargetSeries(function (seriesModel) { 81951 var dataIndices = []; 81952 var data = seriesModel.getData(); 81953 81954 data.each(this.getDataDimension(data), function (value, dataIndex) { 81955 // Should always base on model pieceList, because it is order sensitive. 81956 var pIdx = VisualMapping.findPieceIndex(value, this._pieceList); 81957 pIdx === pieceIndex && dataIndices.push(dataIndex); 81958 }, this); 81959 81960 result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); 81961 }, this); 81962 81963 return result; 81964 }, 81965 81966 /** 81967 * @private 81968 * @param {Object} piece piece.value or piece.interval is required. 81969 * @return {number} Can be Infinity or -Infinity 81970 */ 81971 getRepresentValue: function (piece) { 81972 var representValue; 81973 if (this.isCategory()) { 81974 representValue = piece.value; 81975 } 81976 else { 81977 if (piece.value != null) { 81978 representValue = piece.value; 81979 } 81980 else { 81981 var pieceInterval = piece.interval || []; 81982 representValue = (pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity) 81983 ? 0 81984 : (pieceInterval[0] + pieceInterval[1]) / 2; 81985 } 81986 } 81987 return representValue; 81988 }, 81989
81990 getVisualMeta: function (getColorVisual) { 81991 // Do not support category. (category axis is ordinal, numerical) 81992 if (this.isCategory()) { 81993 return; 81994 } 81995 81996 var stops = []; 81997 var outerColors = []; 81998 var visualMapModel = this; 81999 82000 function setStop(interval, valueState) { 82001 var representValue = visualMapModel.getRepresentValue({interval: interval}); 82002 if (!valueState) { 82003 valueState = visualMapModel.getValueState(representValue); 82004 } 82005 var color = getColorVisual(representValue, valueState); 82006 if (interval[0] === -Infinity) { 82007 outerColors[0] = color; 82008 } 82009 else if (interval[1] === Infinity) { 82010 outerColors[1] = color; 82011 } 82012 else { 82013 stops.push( 82014 {value: interval[0], color: color}, 82015 {value: interval[1], color: color} 82016 ); 82017 } 82018 } 82019 82020 // Suplement 82021 var pieceList = this._pieceList.slice(); 82022 if (!pieceList.length) { 82023 pieceList.push({interval: [-Infinity, Infinity]}); 82024 } 82025 else { 82026 var edge = pieceList[0].interval[0]; 82027 edge !== -Infinity && pieceList.unshift({interval: [-Infinity, edge]}); 82028 edge = pieceList[pieceList.length - 1].interval[1]; 82029 edge !== Infinity && pieceList.push({interval: [edge, Infinity]}); 82030 } 82031 82032 var curr = -Infinity; 82033 each$1(pieceList, function (piece) { 82034 var interval = piece.interval; 82035 if (interval) { 82036 // Fulfill gap. 82037 interval[0] > curr && setStop([curr, interval[0]], 'outOfRange'); 82038 setStop(interval.slice()); 82039 curr = interval[1]; 82040 } 82041 }, this); 82042 82043 return {stops: stops, outerColors: outerColors}; 82044 } 82045 82046 }); 82047 82048 /** 82049 * Key is this._mode 82050 * @type {Object} 82051 * @this {module:echarts/component/viusalMap/PiecewiseMode} 82052 */ 82053 var resetMethods = { 82054 82055 splitNumber: function () { 82056 var thisOption = this.option; 82057 var pieceList = this._pieceList; 82058 var precision = Math.min(thisOption.precision, 20); 82059 var dataExtent = this.getExtent(); 82060 var splitNumber = thisOption.splitNumber; 82061 splitNumber = Math.max(parseInt(splitNumber, 10), 1); 82062 thisOption.splitNumber = splitNumber; 82063 82064 var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; 82065 // Precision auto-adaption 82066 while (+splitStep.toFixed(precision) !== splitStep && precision < 5) { 82067 precision++; 82068 } 82069 thisOption.precision = precision; 82070 splitStep = +splitStep.toFixed(precision); 82071 82072 var index = 0; 82073 82074 if (thisOption.minOpen) { 82075 pieceList.push({ 82076 index: index++, 82077 interval: [-Infinity, dataExtent[0]], 82078 close: [0, 0] 82079 }); 82080 } 82081 82082 for ( 82083 var curr = dataExtent[0], len = index + splitNumber; 82084 index < len; 82085 curr += splitStep 82086 ) { 82087 var max = index === splitNumber - 1 ? dataExtent[1] : (curr + splitStep); 82088 82089 pieceList.push({ 82090 index: index++, 82091 interval: [curr, max], 82092 close: [1, 1] 82093 }); 82094 } 82095 82096 if (thisOption.maxOpen) { 82097 pieceList.push({ 82098 index: index++, 82099 interval: [dataExtent[1], Infinity], 82100 close: [0, 0] 82101 }); 82102 } 82103 82104 reformIntervals(pieceList); 82105 82106 each$1(pieceList, function (piece) { 82107 piece.text = this.formatValueText(piece.interval); 82108 }, this); 82109 }, 82110 82111 categories: function () { 82112 var thisOption = this.option; 82113 each$1(thisOption.categories, function (cate) { 82114 // FIXME category模å¼Â也使çâ¨pieceListï¼Åä½â åŨvisualMappingä¸Âä¸ÂæË¯ä½¿çâ¨pieceListãâ¬â 82115 // æË¯å¦æâ¹ä¸â¬Ã¨â¡Â´Ã£â¬â 82116 this._pieceList.push({ 82117 text: this.formatValueText(cate, true), 82118 value: cate 82119 }); 82120 }, this); 82121 82122 // See "Order Rule".
vendor: 5,184 bytes, lines 82123-82254
82123 normalizeReverse(thisOption, this._pieceList); 82124 }, 82125 82126 pieces: function () { 82127 var thisOption = this.option; 82128 var pieceList = this._pieceList; 82129 82130 each$1(thisOption.pieces, function (pieceListItem, index) { 82131 82132 if (!isObject$1(pieceListItem)) { 82133 pieceListItem = {value: pieceListItem}; 82134 } 82135 82136 var item = {text: '', index: index}; 82137 82138 if (pieceListItem.label != null) { 82139 item.text = pieceListItem.label; 82140 } 82141 82142 if (pieceListItem.hasOwnProperty('value')) { 82143 var value = item.value = pieceListItem.value; 82144 item.interval = [value, value]; 82145 item.close = [1, 1]; 82146 } 82147 else { 82148 // `min` `max` is legacy option. 82149 // `lt` `gt` `lte` `gte` is recommanded. 82150 var interval = item.interval = []; 82151 var close = item.close = [0, 0]; 82152 82153 var closeList = [1, 0, 1]; 82154 var infinityList = [-Infinity, Infinity]; 82155 82156 var useMinMax = []; 82157 for (var lg = 0; lg < 2; lg++) { 82158 var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg]; 82159 for (var i = 0; i < 3 && interval[lg] == null; i++) { 82160 interval[lg] = pieceListItem[names[i]]; 82161 close[lg] = closeList[i]; 82162 useMinMax[lg] = i === 2; 82163 } 82164 interval[lg] == null && (interval[lg] = infinityList[lg]); 82165 } 82166 useMinMax[0] && interval[1] === Infinity && (close[0] = 0); 82167 useMinMax[1] && interval[0] === -Infinity && (close[1] = 0); 82168 82169 if (__DEV__) { 82170 if (interval[0] > interval[1]) { 82171 console.warn( 82172 'Piece ' + index + 'is illegal: ' + interval 82173 + ' lower bound should not greater then uppper bound.' 82174 ); 82175 } 82176 } 82177 82178 if (interval[0] === interval[1] && close[0] && close[1]) { 82179 // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}], 82180 // we use value to lift the priority when min === max 82181 item.value = interval[0]; 82182 } 82183 } 82184 82185 item.visual = VisualMapping.retrieveVisuals(pieceListItem); 82186 82187 pieceList.push(item); 82188 82189 }, this); 82190 82191 // See "Order Rule". 82192 normalizeReverse(thisOption, pieceList); 82193 // Only pieces 82194 reformIntervals(pieceList); 82195 82196 each$1(pieceList, function (piece) { 82197 var close = piece.close; 82198 var edgeSymbols = [['<', 'ââ°Â¤'][close[1]], ['>', 'ââ°Â¥'][close[0]]]; 82199 piece.text = piece.text || this.formatValueText( 82200 piece.value != null ? piece.value : piece.interval, 82201 false, 82202 edgeSymbols 82203 ); 82204 }, this); 82205 } 82206 }; 82207 82208 function normalizeReverse(thisOption, pieceList) { 82209 var inverse = thisOption.inverse; 82210 if (thisOption.orient === 'vertical' ? !inverse : inverse) { 82211 pieceList.reverse(); 82212 } 82213 } 82214 82215 /* 82216* Licensed to the Apache Software Foundation (ASF) under one 82217* or more contributor license agreements. See the NOTICE file 82218* distributed with this work for additional information 82219* regarding copyright ownership. The ASF licenses this file 82220* to you under the Apache License, Version 2.0 (the 82221* "License"); you may not use this file except in compliance 82222* with the License. You may obtain a copy of the License at 82223* 82224* http://www.apache.org/licenses/LICENSE-2.0 82225* 82226* Unless required by applicable law or agreed to in writing, 82227* software distributed under the License is distributed on an 82228* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82229* KIND, either express or implied. See the License for the 82230* specific language governing permissions and limitations 82231* under the License. 82232*/ 82233 82234 var PiecewiseVisualMapView = VisualMapView.extend({ 82235 82236 type: 'visualMap.piecewise', 82237 82238 /** 82239 * @protected 82240 * @override 82241 */ 82242 doRender: function () { 82243 var thisGroup = this.group; 82244 82245 thisGroup.removeAll(); 82246 82247 var visualMapModel = this.visualMapModel; 82248 var textGap = visualMapModel.get('textGap'); 82249 var textStyleModel = visualMapModel.textStyleModel; 82250 var textFont = textStyleModel.getFont(); 82251 var textFill = textStyleModel.getTextColor(); 82252 var itemAlign = this._getItemAlign(); 82253 var itemSize = visualMapModel.itemSize; 82254 var viewData = this._getViewData();
82255 var endsText = viewData.endsText; 82256 var showLabel = retrieve(visualMapModel.get('showLabel', true), !endsText); 82257 82258 endsText && this._renderEndsText( 82259 thisGroup, endsText[0], itemSize, showLabel, itemAlign 82260 ); 82261 82262 each$1(viewData.viewPieceList, renderItem, this); 82263 82264 endsText && this._renderEndsText( 82265 thisGroup, endsText[1], itemSize, showLabel, itemAlign 82266 ); 82267 82268 box( 82269 visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap') 82270 ); 82271 82272 this.renderBackground(thisGroup); 82273 82274 this.positionGroup(thisGroup); 82275 82276 function renderItem(item) { 82277 var piece = item.piece; 82278 82279 var itemGroup = new Group(); 82280 itemGroup.onclick = bind(this._onItemClick, this, piece); 82281 82282 this._enableHoverLink(itemGroup, item.indexInModelPieceList); 82283 82284 var representValue = visualMapModel.getRepresentValue(piece); 82285 82286 this._createItemSymbol( 82287 itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]] 82288 ); 82289 82290 if (showLabel) { 82291 var visualState = this.visualMapModel.getValueState(representValue); 82292 82293 itemGroup.add(new Text({ 82294 style: { 82295 x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap, 82296 y: itemSize[1] / 2, 82297 text: piece.text, 82298 textVerticalAlign: 'middle', 82299 textAlign: itemAlign, 82300 textFont: textFont, 82301 textFill: textFill, 82302 opacity: visualState === 'outOfRange' ? 0.5 : 1 82303 } 82304 })); 82305 } 82306 82307 thisGroup.add(itemGroup); 82308 } 82309 }, 82310 82311 /** 82312 * @private 82313 */ 82314 _enableHoverLink: function (itemGroup, pieceIndex) { 82315 itemGroup 82316 .on('mouseover', bind(onHoverLink, this, 'highlight')) 82317 .on('mouseout', bind(onHoverLink, this, 'downplay')); 82318 82319 function onHoverLink(method) { 82320 var visualMapModel = this.visualMapModel; 82321 82322 visualMapModel.option.hoverLink && this.api.dispatchAction({ 82323 type: method, 82324 batch: convertDataIndex( 82325 visualMapModel.findTargetDataIndices(pieceIndex) 82326 ) 82327 }); 82328 } 82329 }, 82330 82331 /** 82332 * @private 82333 */ 82334 _getItemAlign: function () { 82335 var visualMapModel = this.visualMapModel; 82336 var modelOption = visualMapModel.option; 82337 82338 if (modelOption.orient === 'vertical') { 82339 return getItemAlign( 82340 visualMapModel, this.api, visualMapModel.itemSize 82341 ); 82342 } 82343 else { // horizontal, most case left unless specifying right. 82344 var align = modelOption.align; 82345 if (!align || align === 'auto') { 82346 align = 'left'; 82347 } 82348 return align; 82349 } 82350 }, 82351 82352 /** 82353 * @private 82354 */ 82355 _renderEndsText: function (group, text, itemSize, showLabel, itemAlign) { 82356 if (!text) { 82357 return; 82358 } 82359 82360 var itemGroup = new Group(); 82361 var textStyleModel = this.visualMapModel.textStyleModel; 82362 82363 itemGroup.add(new Text({ 82364 style: { 82365 x: showLabel ? (itemAlign === 'right' ? itemSize[0] : 0) : itemSize[0] / 2, 82366 y: itemSize[1] / 2, 82367 textVerticalAlign: 'middle', 82368 textAlign: showLabel ? itemAlign : 'center', 82369 text: text, 82370 textFont: textStyleModel.getFont(), 82371 textFill: textStyleModel.getTextColor() 82372 } 82373 })); 82374 82375 group.add(itemGroup); 82376 }, 82377 82378 /** 82379 * @private 82380 * @return {Object} {peiceList, endsText} The order is the same as screen pixel order. 82381 */ 82382 _getViewData: function () { 82383 var visualMapModel = this.visualMapModel; 82384 82385 var viewPieceList = map(visualMapModel.getPieceList(), function (piece, index) { 82386 return {piece: piece, indexInModelPieceList: index}; 82387 }); 82388 var endsText = visualMapModel.get('text'); 82389 82390 // Consider orient and inverse. 82391 var orient = visualMapModel.get('orient'); 82392 var inverse = visualMapModel.get('inverse'); 82393 82394 // Order of model pieceList is always [low, ..., high] 82395 if (orient === 'horizontal' ? inverse : !inverse) { 82396 viewPieceList.reverse(); 82397 } 82398 // Origin order of endsText is [high, low] 82399 else if (endsText) { 82400 endsText = endsText.slice().reverse(); 82401 } 82402 82403 return {viewPieceList: viewPieceList, endsText: endsText}; 82404 }, 82405 82406 /** 82407 * @private 82408 */ 82409 _createItemSymbol: function (group, representValue, shapeParam) { 82410 group.add(createSymbol( 82411 this.getControllerVisual(representValue, 'symbol'), 82412 shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3], 82413 this.getControllerVisual(representValue, 'color') 82414 )); 82415 }, 82416 82417 /** 82418 * @private 82419 */ 82420 _onItemClick: function (piece) { 82421 var visualMapModel = this.visualMapModel; 82422 var option = visualMapModel.option; 82423 var selected = clone(option.selected); 82424 var newKey = visualMapModel.getSelectedMapKey(piece); 82425 82426 if (option.selectedMode === 'single') { 82427 selected[newKey] = true; 82428 each$1(selected, function (o, key) { 82429 selected[key] = key === newKey; 82430 }); 82431 } 82432 else { 82433 selected[newKey] = !selected[newKey]; 82434 } 82435 82436 this.api.dispatchAction({ 82437 type: 'selectDataRange', 82438 from: this.uid, 82439 visualMapId: this.visualMapModel.id, 82440 selected: selected 82441 }); 82442 } 82443 });
82444 82445 /* 82446* Licensed to the Apache Software Foundation (ASF) under one 82447* or more contributor license agreements. See the NOTICE file 82448* distributed with this work for additional information 82449* regarding copyright ownership. The ASF licenses this file 82450* to you under the Apache License, Version 2.0 (the 82451* "License"); you may not use this file except in compliance 82452* with the License. You may obtain a copy of the License at 82453* 82454* http://www.apache.org/licenses/LICENSE-2.0 82455* 82456* Unless required by applicable law or agreed to in writing, 82457* software distributed under the License is distributed on an 82458* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82459* KIND, either express or implied. See the License for the 82460* specific language governing permissions and limitations 82461* under the License. 82462*/ 82463 82464 /** 82465 * DataZoom component entry 82466 */ 82467 82468 registerPreprocessor(preprocessor$2); 82469 82470 /* 82471* Licensed to the Apache Software Foundation (ASF) under one 82472* or more contributor license agreements. See the NOTICE file 82473* distributed with this work for additional information 82474* regarding copyright ownership. The ASF licenses this file 82475* to you under the Apache License, Version 2.0 (the 82476* "License"); you may not use this file except in compliance 82477* with the License. You may obtain a copy of the License at 82478* 82479* http://www.apache.org/licenses/LICENSE-2.0 82480* 82481* Unless required by applicable law or agreed to in writing, 82482* software distributed under the License is distributed on an 82483* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82484* KIND, either express or implied. See the License for the 82485* specific language governing permissions and limitations 82486* under the License. 82487*/ 82488 82489 /** 82490 * visualMap component entry 82491 */ 82492 82493 /* 82494* Licensed to the Apache Software Foundation (ASF) under one 82495* or more contributor license agreements. See the NOTICE file 82496* distributed with this work for additional information 82497* regarding copyright ownership. The ASF licenses this file 82498* to you under the Apache License, Version 2.0 (the 82499* "License"); you may not use this file except in compliance 82500* with the License. You may obtain a copy of the License at 82501* 82502* http://www.apache.org/licenses/LICENSE-2.0 82503* 82504* Unless required by applicable law or agreed to in writing, 82505* software distributed under the License is distributed on an 82506* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82507* KIND, either express or implied. See the License for the 82508* specific language governing permissions and limitations 82509* under the License. 82510*/ 82511 82512 var addCommas$1 = addCommas; 82513 var encodeHTML$1 = encodeHTML; 82514 82515 function fillLabel(opt) { 82516 defaultEmphasis(opt, 'label', ['show']); 82517 } 82518 var MarkerModel = extendComponentModel({ 82519 82520 type: 'marker', 82521 82522 dependencies: ['series', 'grid', 'polar', 'geo'], 82523 82524 /** 82525 * @overrite 82526 */ 82527 init: function (option, parentModel, ecModel, extraOpt) { 82528 82529 if (__DEV__) { 82530 if (this.type === 'marker') { 82531 throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.'); 82532 } 82533 } 82534 this.mergeDefaultAndTheme(option, ecModel); 82535 this.mergeOption(option, ecModel, extraOpt.createdBySelf, true); 82536 }, 82537 82538 /** 82539 * @return {boolean} 82540 */ 82541 isAnimationEnabled: function () { 82542 if (env$1.node) { 82543 return false; 82544 } 82545 82546 var hostSeries = this.__hostSeries; 82547 return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled(); 82548 }, 82549 82550 mergeOption: function (newOpt, ecModel, createdBySelf, isInit) { 82551 var MarkerModel = this.constructor; 82552 var modelPropName = this.mainType + 'Model'; 82553 if (!createdBySelf) { 82554 ecModel.eachSeries(function (seriesModel) { 82555 82556 var markerOpt = seriesModel.get(this.mainType, true); 82557 82558 var markerModel = seriesModel[modelPropName]; 82559 if (!markerOpt || !markerOpt.data) { 82560 seriesModel[modelPropName] = null; 82561 return; 82562 } 82563 if (!markerModel) { 82564 if (isInit) { 82565 // Default label emphasis `position` and `show` 82566 fillLabel(markerOpt); 82567 } 82568 each$1(markerOpt.data, function (item) { 82569 // FIXME Overwrite fillLabel method ? 82570 if (item instanceof Array) { 82571 fillLabel(item[0]); 82572 fillLabel(item[1]); 82573 } 82574 else { 82575 fillLabel(item); 82576 } 82577 }); 82578 82579 markerModel = new MarkerModel( 82580 markerOpt, this, ecModel 82581 ); 82582 82583 extend(markerModel, { 82584 mainType: this.mainType, 82585 // Use the same series index and name 82586 seriesIndex: seriesModel.seriesIndex, 82587 name: seriesModel.name, 82588 createdBySelf: true 82589 }); 82590 82591 markerModel.__hostSeries = seriesModel; 82592 } 82593 else { 82594 markerModel.mergeOption(markerOpt, ecModel, true); 82595 } 82596 seriesModel[modelPropName] = markerModel; 82597 }, this); 82598 } 82599 }, 82600 82601 formatTooltip: function (dataIndex) { 82602 var data = this.getData(); 82603 var value = this.getRawValue(dataIndex); 82604 var formattedValue = isArray(value) 82605 ? map(value, addCommas$1).join(', ') : addCommas$1(value); 82606 var name = data.getName(dataIndex); 82607 var html = encodeHTML$1(this.name); 82608 if (value != null || name) { 82609 html += '<br />'; 82610 } 82611 if (name) { 82612 html += encodeHTML$1(name); 82613 if (value != null) { 82614 html += ' : '; 82615 } 82616 } 82617 if (value != null) { 82618 html += encodeHTML$1(formattedValue); 82619 } 82620 return html; 82621 }, 82622 82623 getData: function () { 82624 return this._data; 82625 }, 82626 82627 setData: function (data) { 82628 this._data = data; 82629 } 82630 });
82631 82632 mixin(MarkerModel, dataFormatMixin); 82633 82634 /* 82635* Licensed to the Apache Software Foundation (ASF) under one 82636* or more contributor license agreements. See the NOTICE file 82637* distributed with this work for additional information 82638* regarding copyright ownership. The ASF licenses this file 82639* to you under the Apache License, Version 2.0 (the 82640* "License"); you may not use this file except in compliance 82641* with the License. You may obtain a copy of the License at 82642* 82643* http://www.apache.org/licenses/LICENSE-2.0 82644* 82645* Unless required by applicable law or agreed to in writing, 82646* software distributed under the License is distributed on an 82647* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82648* KIND, either express or implied. See the License for the 82649* specific language governing permissions and limitations 82650* under the License. 82651*/ 82652 82653 MarkerModel.extend({ 82654 82655 type: 'markPoint', 82656 82657 defaultOption: { 82658 zlevel: 0, 82659 z: 5, 82660 symbol: 'pin', 82661 symbolSize: 50, 82662 //symbolRotate: 0, 82663 //symbolOffset: [0, 0] 82664 tooltip: { 82665 trigger: 'item' 82666 }, 82667 label: { 82668 show: true, 82669 position: 'inside' 82670 }, 82671 itemStyle: { 82672 borderWidth: 2 82673 }, 82674 emphasis: { 82675 label: { 82676 show: true 82677 } 82678 } 82679 } 82680 }); 82681 82682 /* 82683* Licensed to the Apache Software Foundation (ASF) under one 82684* or more contributor license agreements. See the NOTICE file 82685* distributed with this work for additional information 82686* regarding copyright ownership. The ASF licenses this file 82687* to you under the Apache License, Version 2.0 (the 82688* "License"); you may not use this file except in compliance 82689* with the License. You may obtain a copy of the License at 82690* 82691* http://www.apache.org/licenses/LICENSE-2.0 82692* 82693* Unless required by applicable law or agreed to in writing, 82694* software distributed under the License is distributed on an 82695* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82696* KIND, either express or implied. See the License for the 82697* specific language governing permissions and limitations 82698* under the License. 82699*/ 82700 82701 var indexOf$2 = indexOf; 82702 82703 function hasXOrY(item) { 82704 return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y))); 82705 } 82706 82707 function hasXAndY(item) { 82708 return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y)); 82709 } 82710 82711// Make it simple, do not visit all stacked value to count precision. 82712// function getPrecision(data, valueAxisDim, dataIndex) { 82713// var precision = -1; 82714// var stackedDim = data.mapDimension(valueAxisDim); 82715// do { 82716// precision = Math.max( 82717// numberUtil.getPrecision(data.get(stackedDim, dataIndex)), 82718// precision 82719// ); 82720// var stackedOnSeries = data.getCalculationInfo('stackedOnSeries'); 82721// if (stackedOnSeries) { 82722// var byValue = data.get(data.getCalculationInfo('stackedByDimension'), dataIndex); 82723// data = stackedOnSeries.getData(); 82724// dataIndex = data.indexOf(data.getCalculationInfo('stackedByDimension'), byValue); 82725// stackedDim = data.getCalculationInfo('stackedDimension'); 82726// } 82727// else { 82728// data = null; 82729// } 82730// } while (data); 82731 82732// return precision; 82733// } 82734 82735 function markerTypeCalculatorWithExtent( 82736 mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex 82737 ) { 82738 var coordArr = []; 82739 82740 var stacked = isDimensionStacked(data, targetDataDim /*, otherDataDim*/); 82741 var calcDataDim = stacked 82742 ? data.getCalculationInfo('stackResultDimension') 82743 : targetDataDim; 82744 82745 var value = numCalculate(data, calcDataDim, mlType); 82746 82747 var dataIndex = data.indicesOfNearest(calcDataDim, value)[0]; 82748 coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex); 82749 coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex); 82750 82751 // Make it simple, do not visit all stacked value to count precision. 82752 var precision = getPrecision(data.get(targetDataDim, dataIndex)); 82753 precision = Math.min(precision, 20); 82754 if (precision >= 0) { 82755 coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision); 82756 } 82757 82758 return coordArr; 82759 } 82760 82761 var curry$7 = curry;
82762// TODO Specified percent 82763 var markerTypeCalculator = { 82764 /** 82765 * @method 82766 * @param {module:echarts/data/List} data 82767 * @param {string} baseAxisDim 82768 * @param {string} valueAxisDim 82769 */ 82770 min: curry$7(markerTypeCalculatorWithExtent, 'min'), 82771 /** 82772 * @method 82773 * @param {module:echarts/data/List} data 82774 * @param {string} baseAxisDim 82775 * @param {string} valueAxisDim 82776 */ 82777 max: curry$7(markerTypeCalculatorWithExtent, 'max'), 82778 82779 /** 82780 * @method 82781 * @param {module:echarts/data/List} data 82782 * @param {string} baseAxisDim 82783 * @param {string} valueAxisDim 82784 */ 82785 average: curry$7(markerTypeCalculatorWithExtent, 'average') 82786 }; 82787 82788 /** 82789 * Transform markPoint data item to format used in List by do the following 82790 * 1. Calculate statistic like `max`, `min`, `average` 82791 * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array 82792 * @param {module:echarts/model/Series} seriesModel 82793 * @param {module:echarts/coord/*} [coordSys] 82794 * @param {Object} item 82795 * @return {Object} 82796 */ 82797 function dataTransform(seriesModel, item) { 82798 var data = seriesModel.getData(); 82799 var coordSys = seriesModel.coordinateSystem; 82800 82801 // 1. If not specify the position with pixel directly 82802 // 2. If `coord` is not a data array. Which uses `xAxis`, 82803 // `yAxis` to specify the coord on each dimension 82804 82805 // parseFloat first because item.x and item.y can be percent string like '20%' 82806 if (item && !hasXAndY(item) && !isArray(item.coord) && coordSys) { 82807 var dims = coordSys.dimensions; 82808 var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); 82809 82810 // Clone the option 82811 // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value 82812 item = clone(item); 82813 82814 if (item.type 82815 && markerTypeCalculator[item.type] 82816 && axisInfo.baseAxis && axisInfo.valueAxis 82817 ) { 82818 var otherCoordIndex = indexOf$2(dims, axisInfo.baseAxis.dim); 82819 var targetCoordIndex = indexOf$2(dims, axisInfo.valueAxis.dim); 82820 82821 item.coord = markerTypeCalculator[item.type]( 82822 data, axisInfo.baseDataDim, axisInfo.valueDataDim, 82823 otherCoordIndex, targetCoordIndex 82824 ); 82825 // Force to use the value of calculated value. 82826 item.value = item.coord[targetCoordIndex]; 82827 } 82828 else { 82829 // FIXME Only has one of xAxis and yAxis. 82830 var coord = [ 82831 item.xAxis != null ? item.xAxis : item.radiusAxis, 82832 item.yAxis != null ? item.yAxis : item.angleAxis 82833 ]; 82834 // Each coord support max, min, average 82835 for (var i = 0; i < 2; i++) { 82836 if (markerTypeCalculator[coord[i]]) { 82837 coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]); 82838 } 82839 } 82840 item.coord = coord; 82841 } 82842 } 82843 return item; 82844 } 82845 82846 function getAxisInfo$1(item, data, coordSys, seriesModel) { 82847 var ret = {}; 82848 82849 if (item.valueIndex != null || item.valueDim != null) { 82850 ret.valueDataDim = item.valueIndex != null 82851 ? data.getDimension(item.valueIndex) : item.valueDim; 82852 ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim)); 82853 ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis); 82854 ret.baseDataDim = data.mapDimension(ret.baseAxis.dim); 82855 } 82856 else { 82857 ret.baseAxis = seriesModel.getBaseAxis(); 82858 ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis); 82859 ret.baseDataDim = data.mapDimension(ret.baseAxis.dim); 82860 ret.valueDataDim = data.mapDimension(ret.valueAxis.dim); 82861 } 82862 82863 return ret; 82864 } 82865 82866 function dataDimToCoordDim(seriesModel, dataDim) { 82867 var data = seriesModel.getData(); 82868 var dimensions = data.dimensions; 82869 dataDim = data.getDimension(dataDim); 82870 for (var i = 0; i < dimensions.length; i++) { 82871 var dimItem = data.getDimensionInfo(dimensions[i]); 82872 if (dimItem.name === dataDim) { 82873 return dimItem.coordDim; 82874 } 82875 } 82876 } 82877 82878 /** 82879 * Filter data which is out of coordinateSystem range 82880 * [dataFilter description] 82881 * @param {module:echarts/coord/*} [coordSys] 82882 * @param {Object} item 82883 * @return {boolean} 82884 */ 82885 function dataFilter$1(coordSys, item) { 82886 // Alwalys return true if there is no coordSys 82887 return (coordSys && coordSys.containData && item.coord && !hasXOrY(item)) 82888 ? coordSys.containData(item.coord) : true; 82889 } 82890 82891 function dimValueGetter(item, dimName, dataIndex, dimIndex) { 82892 // x, y, radius, angle 82893 if (dimIndex < 2) { 82894 return item.coord && item.coord[dimIndex]; 82895 } 82896 return item.value; 82897 } 82898 82899 function numCalculate(data, valueDataDim, type) { 82900 if (type === 'average') { 82901 var sum = 0; 82902 var count = 0; 82903 data.each(valueDataDim, function (val, idx) { 82904 if (!isNaN(val)) { 82905 sum += val; 82906 count++; 82907 } 82908 }); 82909 return sum / count; 82910 } 82911 else if (type === 'median') { 82912 return data.getMedian(valueDataDim); 82913 } 82914 else { 82915 // max & min 82916 return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0]; 82917 } 82918 } 82919 82920 /* 82921* Licensed to the Apache Software Foundation (ASF) under one 82922* or more contributor license agreements. See the NOTICE file 82923* distributed with this work for additional information 82924* regarding copyright ownership. The ASF licenses this file 82925* to you under the Apache License, Version 2.0 (the 82926* "License");
82926 you may not use this file except in compliance 82927* with the License. You may obtain a copy of the License at 82928* 82929* http://www.apache.org/licenses/LICENSE-2.0 82930* 82931* Unless required by applicable law or agreed to in writing, 82932* software distributed under the License is distributed on an 82933* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82934* KIND, either express or implied. See the License for the 82935* specific language governing permissions and limitations 82936* under the License. 82937*/ 82938 82939 var MarkerView = extendComponentView({ 82940 82941 type: 'marker', 82942 82943 init: function () { 82944 /** 82945 * Markline grouped by series 82946 * @private 82947 * @type {module:zrender/core/util.HashMap} 82948 */ 82949 this.markerGroupMap = createHashMap(); 82950 }, 82951 82952 render: function (markerModel, ecModel, api) { 82953 var markerGroupMap = this.markerGroupMap; 82954 markerGroupMap.each(function (item) { 82955 item.__keep = false; 82956 }); 82957 82958 var markerModelKey = this.type + 'Model'; 82959 ecModel.eachSeries(function (seriesModel) { 82960 var markerModel = seriesModel[markerModelKey]; 82961 markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api); 82962 }, this); 82963 82964 markerGroupMap.each(function (item) { 82965 !item.__keep && this.group.remove(item.group); 82966 }, this); 82967 }, 82968 82969 renderSeries: function () {} 82970 }); 82971 82972 /* 82973* Licensed to the Apache Software Foundation (ASF) under one 82974* or more contributor license agreements. See the NOTICE file 82975* distributed with this work for additional information 82976* regarding copyright ownership. The ASF licenses this file 82977* to you under the Apache License, Version 2.0 (the 82978* "License"); you may not use this file except in compliance 82979* with the License. You may obtain a copy of the License at 82980* 82981* http://www.apache.org/licenses/LICENSE-2.0 82982* 82983* Unless required by applicable law or agreed to in writing, 82984* software distributed under the License is distributed on an 82985* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 82986* KIND, either express or implied. See the License for the 82987* specific language governing permissions and limitations 82988* under the License. 82989*/ 82990 82991 function updateMarkerLayout(mpData, seriesModel, api) { 82992 var coordSys = seriesModel.coordinateSystem; 82993 mpData.each(function (idx) { 82994 var itemModel = mpData.getItemModel(idx); 82995 var point; 82996 var xPx = parsePercent$1(itemModel.get('x'), api.getWidth()); 82997 var yPx = parsePercent$1(itemModel.get('y'), api.getHeight()); 82998 if (!isNaN(xPx) && !isNaN(yPx)) { 82999 point = [xPx, yPx]; 83000 } 83001 // Chart like bar may have there own marker positioning logic 83002 else if (seriesModel.getMarkerPosition) { 83003 // Use the getMarkerPoisition 83004 point = seriesModel.getMarkerPosition( 83005 mpData.getValues(mpData.dimensions, idx) 83006 ); 83007 } 83008 else if (coordSys) { 83009 var x = mpData.get(coordSys.dimensions[0], idx); 83010 var y = mpData.get(coordSys.dimensions[1], idx); 83011 point = coordSys.dataToPoint([x, y]); 83012 83013 } 83014 83015 // Use x, y if has any 83016 if (!isNaN(xPx)) { 83017 point[0] = xPx; 83018 } 83019 if (!isNaN(yPx)) { 83020 point[1] = yPx; 83021 } 83022 83023 mpData.setItemLayout(idx, point); 83024 }); 83025 } 83026 83027 MarkerView.extend({ 83028 83029 type: 'markPoint', 83030 83031 // updateLayout: function (markPointModel, ecModel, api) { 83032 // ecModel.eachSeries(function (seriesModel) { 83033 // var mpModel = seriesModel.markPointModel; 83034 // if (mpModel) { 83035 // updateMarkerLayout(mpModel.getData(), seriesModel, api); 83036 // this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel); 83037 // } 83038 // }, this); 83039 // }, 83040 83041 updateTransform: function (markPointModel, ecModel, api) { 83042 ecModel.eachSeries(function (seriesModel) { 83043 var mpModel = seriesModel.markPointModel; 83044 if (mpModel) { 83045 updateMarkerLayout(mpModel.getData(), seriesModel, api); 83046 this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel); 83047 } 83048 }, this); 83049 }, 83050 83051 renderSeries: function (seriesModel, mpModel, ecModel, api) { 83052 var coordSys = seriesModel.coordinateSystem; 83053 var seriesId = seriesModel.id; 83054 var seriesData = seriesModel.getData(); 83055 83056 var symbolDrawMap = this.markerGroupMap; 83057 var symbolDraw = symbolDrawMap.get(seriesId) 83058 || symbolDrawMap.set(seriesId, new SymbolDraw()); 83059 83060 var mpData = createList$1(coordSys, seriesModel, mpModel); 83061 83062 // FIXME 83063 mpModel.setData(mpData); 83064 83065 updateMarkerLayout(mpModel.getData(), seriesModel, api); 83066 83067 mpData.each(function (idx) { 83068 var itemModel = mpData.getItemModel(idx); 83069 var symbolSize = itemModel.getShallow('symbolSize'); 83070 if (typeof symbolSize === 'function') { 83071 // FIXME è¿â¢Ã©â¡Åä¸ÂÃ¥â¦Â¼Ã¥Â®Â¹ ECharts 2.xï¼Å2.x è²Åä¼¼åÂâæâ¢Â°Ã¦Ë¯æâ¢Â´Ã¤Â¸ÂªÃ¦â¢Â°Ã¦Â®ï¼Ÿ 83072 symbolSize = symbolSize( 83073 mpModel.getRawValue(idx), mpModel.getDataParams(idx) 83074 ); 83075 } 83076 mpData.setItemVisual(idx, { 83077 symbolSize: symbolSize, 83078 color: itemModel.get('itemStyle.color') 83079 || seriesData.getVisual('color'), 83080 symbol: itemModel.getShallow('symbol') 83081 }); 83082 }); 83083 83084 // TODO Text are wrong 83085 symbolDraw.updateData(mpData); 83086 this.group.add(symbolDraw.group); 83087 83088 // Set host model for tooltip 83089 // FIXME 83090 mpData.eachItemGraphicEl(function (el) { 83091 el.traverse(function (child) { 83092 child.dataModel = mpModel; 83093 }); 83094 }); 83095 83096 symbolDraw.__keep = true; 83097 83098 symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent'); 83099 } 83100 });
83101 83102 /** 83103 * @inner 83104 * @param {module:echarts/coord/*} [coordSys] 83105 * @param {module:echarts/model/Series} seriesModel 83106 * @param {module:echarts/model/Model} mpModel 83107 */ 83108 function createList$1(coordSys, seriesModel, mpModel) { 83109 var coordDimsInfos; 83110 if (coordSys) { 83111 coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { 83112 var info = seriesModel.getData().getDimensionInfo( 83113 seriesModel.getData().mapDimension(coordDim) 83114 ) || {}; 83115 // In map series data don't have lng and lat dimension. Fallback to same with coordSys 83116 return defaults({name: coordDim}, info); 83117 }); 83118 } 83119 else { 83120 coordDimsInfos =[{ 83121 name: 'value', 83122 type: 'float' 83123 }]; 83124 } 83125 83126 var mpData = new List(coordDimsInfos, mpModel); 83127 var dataOpt = map(mpModel.get('data'), curry( 83128 dataTransform, seriesModel 83129 )); 83130 if (coordSys) { 83131 dataOpt = filter( 83132 dataOpt, curry(dataFilter$1, coordSys) 83133 ); 83134 } 83135 83136 mpData.initData(dataOpt, null, 83137 coordSys ? dimValueGetter : function (item) { 83138 return item.value; 83139 } 83140 ); 83141 83142 return mpData; 83143 } 83144 83145 /* 83146* Licensed to the Apache Software Foundation (ASF) under one 83147* or more contributor license agreements. See the NOTICE file 83148* distributed with this work for additional information 83149* regarding copyright ownership. The ASF licenses this file 83150* to you under the Apache License, Version 2.0 (the 83151* "License"); you may not use this file except in compliance 83152* with the License. You may obtain a copy of the License at 83153* 83154* http://www.apache.org/licenses/LICENSE-2.0 83155* 83156* Unless required by applicable law or agreed to in writing, 83157* software distributed under the License is distributed on an 83158* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 83159* KIND, either express or implied. See the License for the 83160* specific language governing permissions and limitations 83161* under the License. 83162*/ 83163 83164// HINT Markpoint can't be used too much 83165 registerPreprocessor(function (opt) { 83166 // Make sure markPoint component is enabled 83167 opt.markPoint = opt.markPoint || {}; 83168 }); 83169 83170 /* 83171* Licensed to the Apache Software Foundation (ASF) under one 83172* or more contributor license agreements. See the NOTICE file 83173* distributed with this work for additional information 83174* regarding copyright ownership. The ASF licenses this file 83175* to you under the Apache License, Version 2.0 (the 83176* "License"); you may not use this file except in compliance 83177* with the License. You may obtain a copy of the License at 83178* 83179* http://www.apache.org/licenses/LICENSE-2.0 83180* 83181* Unless required by applicable law or agreed to in writing, 83182* software distributed under the License is distributed on an 83183* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 83184* KIND, either express or implied. See the License for the 83185* specific language governing permissions and limitations 83186* under the License. 83187*/ 83188 83189 MarkerModel.extend({ 83190 83191 type: 'markLine', 83192 83193 defaultOption: { 83194 zlevel: 0, 83195 z: 5, 83196 83197 symbol: ['circle', 'arrow'], 83198 symbolSize: [8, 16], 83199 83200 //symbolRotate: 0, 83201 83202 precision: 2, 83203 tooltip: { 83204 trigger: 'item' 83205 }, 83206 label: { 83207 show: true, 83208 position: 'end' 83209 }, 83210 lineStyle: { 83211 type: 'dashed' 83212 }, 83213 emphasis: { 83214 label: { 83215 show: true 83216 }, 83217 lineStyle: { 83218 width: 3 83219 } 83220 }, 83221 animationEasing: 'linear' 83222 } 83223 }); 83224 83225 /* 83226* Licensed to the Apache Software Foundation (ASF) under one 83227* or more contributor license agreements. See the NOTICE file 83228* distributed with this work for additional information 83229* regarding copyright ownership. The ASF licenses this file 83230* to you under the Apache License, Version 2.0 (the 83231* "License");
83231 you may not use this file except in compliance 83232* with the License. You may obtain a copy of the License at 83233* 83234* http://www.apache.org/licenses/LICENSE-2.0 83235* 83236* Unless required by applicable law or agreed to in writing, 83237* software distributed under the License is distributed on an 83238* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 83239* KIND, either express or implied. See the License for the 83240* specific language governing permissions and limitations 83241* under the License. 83242*/ 83243 83244 var markLineTransform = function (seriesModel, coordSys, mlModel, item) { 83245 var data = seriesModel.getData(); 83246 // Special type markLine like 'min', 'max', 'average', 'median' 83247 var mlType = item.type; 83248 83249 if (!isArray(item) 83250 && ( 83251 mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median' 83252 // In case 83253 // data: [{ 83254 // yAxis: 10 83255 // }] 83256 || (item.xAxis != null || item.yAxis != null) 83257 ) 83258 ) { 83259 var valueAxis; 83260 var valueDataDim; 83261 var value; 83262 83263 if (item.yAxis != null || item.xAxis != null) { 83264 valueDataDim = item.yAxis != null ? 'y' : 'x'; 83265 valueAxis = coordSys.getAxis(valueDataDim); 83266 83267 value = retrieve(item.yAxis, item.xAxis); 83268 } 83269 else { 83270 var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); 83271 valueDataDim = axisInfo.valueDataDim; 83272 valueAxis = axisInfo.valueAxis; 83273 value = numCalculate(data, valueDataDim, mlType); 83274 } 83275 var valueIndex = valueDataDim === 'x' ? 0 : 1; 83276 var baseIndex = 1 - valueIndex; 83277 83278 var mlFrom = clone(item); 83279 var mlTo = {}; 83280 83281 mlFrom.type = null; 83282 83283 mlFrom.coord = []; 83284 mlTo.coord = []; 83285 mlFrom.coord[baseIndex] = -Infinity; 83286 mlTo.coord[baseIndex] = Infinity; 83287 83288 var precision = mlModel.get('precision'); 83289 if (precision >= 0 && typeof value === 'number') { 83290 value = +value.toFixed(Math.min(precision, 20)); 83291 } 83292 83293 mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value; 83294 83295 item = [mlFrom, mlTo, { // Extra option for tooltip and label 83296 type: mlType, 83297 valueIndex: item.valueIndex, 83298 // Force to use the value of calculated value. 83299 value: value 83300 }]; 83301 } 83302 83303 item = [ 83304 dataTransform(seriesModel, item[0]), 83305 dataTransform(seriesModel, item[1]), 83306 extend({}, item[2]) 83307 ]; 83308 83309 // Avoid line data type is extended by from(to) data type 83310 item[2].type = item[2].type || ''; 83311 83312 // Merge from option and to option into line option 83313 merge(item[2], item[0]); 83314 merge(item[2], item[1]); 83315 83316 return item; 83317 }; 83318 83319 function isInifinity(val) { 83320 return !isNaN(val) && !isFinite(val); 83321 } 83322 83323// If a markLine has one dim 83324 function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { 83325 var otherDimIndex = 1 - dimIndex; 83326 var dimName = coordSys.dimensions[dimIndex]; 83327 return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex]) 83328 && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]); 83329 } 83330 83331 function markLineFilter(coordSys, item) { 83332 if (coordSys.type === 'cartesian2d') { 83333 var fromCoord = item[0].coord; 83334 var toCoord = item[1].coord; 83335 // In case 83336 // { 83337 // markLine: { 83338 // data: [{ yAxis: 2 }] 83339 // } 83340 // } 83341 if ( 83342 fromCoord && toCoord && 83343 (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) 83344 || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys)) 83345 ) { 83346 return true; 83347 } 83348 } 83349 return dataFilter$1(coordSys, item[0]) 83350 && dataFilter$1(coordSys, item[1]); 83351 } 83352 83353 function updateSingleMarkerEndLayout( 83354 data, idx, isFrom, seriesModel, api 83355 ) { 83356 var coordSys = seriesModel.coordinateSystem; 83357 var itemModel = data.getItemModel(idx); 83358 83359 var point; 83360 var xPx = parsePercent$1(itemModel.get('x'), api.getWidth()); 83361 var yPx = parsePercent$1(itemModel.get('y'), api.getHeight()); 83362 if (!isNaN(xPx) && !isNaN(yPx)) { 83363 point = [xPx, yPx]; 83364 } 83365 else { 83366 // Chart like bar may have there own marker positioning logic 83367 if (seriesModel.getMarkerPosition) { 83368 // Use the getMarkerPoisition 83369 point = seriesModel.getMarkerPosition( 83370 data.getValues(data.dimensions, idx) 83371 ); 83372 } 83373 else { 83374 var dims = coordSys.dimensions; 83375 var x = data.get(dims[0], idx); 83376 var y = data.get(dims[1], idx); 83377 point = coordSys.dataToPoint([x, y]); 83378 } 83379 // Expand line to the edge of grid if value on one axis is Inifnity 83380 // In case 83381 // markLine: { 83382 // data: [{ 83383 // yAxis: 2 83384 // // or 83385 // type: 'average' 83386 // }] 83387 // } 83388 if (coordSys.type === 'cartesian2d') { 83389 var xAxis = coordSys.getAxis('x'); 83390 var yAxis = coordSys.getAxis('y'); 83391 var dims = coordSys.dimensions; 83392 if (isInifinity(data.get(dims[0], idx))) { 83393 point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]); 83394 } 83395 else if (isInifinity(data.get(dims[1], idx))) { 83396 point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]); 83397 } 83398 } 83399 83400 // Use x, y if has any 83401 if (!isNaN(xPx)) { 83402 point[0] = xPx; 83403 } 83404 if (!isNaN(yPx)) { 83405 point[1] = yPx; 83406 } 83407 } 83408 83409 data.setItemLayout(idx, point); 83410 } 83411 83412 MarkerView.extend({ 83413 83414 type: 'markLine', 83415 83416 // updateLayout: function (markLineModel, ecModel, api) { 83417 // ecModel.eachSeries(function (seriesModel) { 83418 // var mlModel = seriesModel.markLineModel; 83419 // if (mlModel) { 83420 // var mlData = mlModel.getData(); 83421 // var fromData = mlModel.__from; 83422 // var toData = mlModel.__to; 83423 // // Update visual and layout of from symbol and to symbol 83424 // fromData.each(function (idx) { 83425 // updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api); 83426 // updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api); 83427 // }); 83428 // // Update layout of line 83429 // mlData.each(function (idx) { 83430 // mlData.setItemLayout(idx, [ 83431 // fromData.getItemLayout(idx), 83432 // toData.getItemLayout(idx) 83433 // ]); 83434 // }); 83435 83436 // this.markerGroupMap.get(seriesModel.id).updateLayout(); 83437 83438 // } 83439 // }, this); 83440 // }, 83441 83442 updateTransform: function (markLineModel, ecModel, api) { 83443 ecModel.eachSeries(function (seriesModel) { 83444 var mlModel = seriesModel.markLineModel; 83445 if (mlModel) { 83446 var mlData = mlModel.getData(); 83447 var fromData = mlModel.__from; 83448 var toData = mlModel.__to; 83449 // Update visual and layout of from symbol and to symbol 83450 fromData.each(function (idx) { 83451 updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api); 83452 updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api); 83453 }); 83454 // Update layout of line 83455 mlData.each(function (idx) { 83456 mlData.setItemLayout(idx, [ 83457 fromData.getItemLayout(idx), 83458 toData.getItemLayout(idx) 83459 ]); 83460 }); 83461 83462 this.markerGroupMap.get(seriesModel.id).updateLayout(); 83463 83464 } 83465 }, this); 83466 }, 83467 83468 renderSeries: function (seriesModel, mlModel, ecModel, api) { 83469 var coordSys = seriesModel.coordinateSystem; 83470 var seriesId = seriesModel.id; 83471 var seriesData = seriesModel.getData(); 83472 83473 var lineDrawMap = this.markerGroupMap; 83474 var lineDraw = lineDrawMap.get(seriesId) 83475 || lineDrawMap.set(seriesId, new LineDraw()); 83476 this.group.add(lineDraw.group); 83477 83478 var mlData = createList$2(coordSys, seriesModel, mlModel); 83479 83480 var fromData = mlData.from; 83481 var toData = mlData.to; 83482 var lineData = mlData.line; 83483 83484 mlModel.__from = fromData; 83485 mlModel.__to = toData; 83486 // Line data for tooltip and formatter 83487 mlModel.setData(lineData); 83488 83489 var symbolType = mlModel.get('symbol'); 83490 var symbolSize = mlModel.get('symbolSize'); 83491 if (!isArray(symbolType)) { 83492 symbolType = [symbolType, symbolType]; 83493 } 83494 if (typeof symbolSize === 'number') { 83495 symbolSize = [symbolSize, symbolSize]; 83496 } 83497 83498 // Update visual and layout of from symbol and to symbol 83499 mlData.from.each(function (idx) { 83500 updateDataVisualAndLayout(fromData, idx, true); 83501 updateDataVisualAndLayout(toData, idx, false); 83502 }); 83503 83504 // Update visual and layout of line 83505 lineData.each(function (idx) { 83506 var lineColor = lineData.getItemModel(idx).get('lineStyle.color'); 83507 lineData.setItemVisual(idx, { 83508 color: lineColor || fromData.getItemVisual(idx, 'color') 83509 }); 83510 lineData.setItemLayout(idx, [ 83511 fromData.getItemLayout(idx), 83512 toData.getItemLayout(idx) 83513 ]); 83514 83515 lineData.setItemVisual(idx, { 83516 'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'), 83517 'fromSymbol': fromData.getItemVisual(idx, 'symbol'), 83518 'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'), 83519 'toSymbol': toData.getItemVisual(idx, 'symbol') 83520 }); 83521 }); 83522 83523 lineDraw.updateData(lineData); 83524 83525 // Set host model for tooltip 83526 // FIXME 83527 mlData.line.eachItemGraphicEl(function (el, idx) { 83528 el.traverse(function (child) { 83529 child.dataModel = mlModel; 83530 }); 83531 }); 83532 83533 function updateDataVisualAndLayout(data, idx, isFrom) { 83534 var itemModel = data.getItemModel(idx); 83535 83536 updateSingleMarkerEndLayout( 83537 data, idx, isFrom, seriesModel, api 83538 ); 83539 83540 data.setItemVisual(idx, { 83541 symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1], 83542 symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1], 83543 color: itemModel.get('itemStyle.color') || seriesData.getVisual('color') 83544 }); 83545 } 83546 83547 lineDraw.__keep = true; 83548 83549 lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent'); 83550 } 83551 });
83552 83553 /** 83554 * @inner 83555 * @param {module:echarts/coord/*} coordSys 83556 * @param {module:echarts/model/Series} seriesModel 83557 * @param {module:echarts/model/Model} mpModel 83558 */ 83559 function createList$2(coordSys, seriesModel, mlModel) { 83560 83561 var coordDimsInfos; 83562 if (coordSys) { 83563 coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { 83564 var info = seriesModel.getData().getDimensionInfo( 83565 seriesModel.getData().mapDimension(coordDim) 83566 ) || {}; 83567 // In map series data don't have lng and lat dimension. Fallback to same with coordSys 83568 return defaults({name: coordDim}, info); 83569 }); 83570 } 83571 else { 83572 coordDimsInfos =[{ 83573 name: 'value', 83574 type: 'float' 83575 }]; 83576 } 83577 83578 var fromData = new List(coordDimsInfos, mlModel); 83579 var toData = new List(coordDimsInfos, mlModel); 83580 // No dimensions 83581 var lineData = new List([], mlModel); 83582 83583 var optData = map(mlModel.get('data'), curry( 83584 markLineTransform, seriesModel, coordSys, mlModel 83585 )); 83586 if (coordSys) { 83587 optData = filter( 83588 optData, curry(markLineFilter, coordSys) 83589 ); 83590 } 83591 var dimValueGetter$$1 = coordSys ? dimValueGetter : function (item) { 83592 return item.value; 83593 }; 83594 fromData.initData( 83595 map(optData, function (item) { return item[0]; }), 83596 null, dimValueGetter$$1 83597 ); 83598 toData.initData( 83599 map(optData, function (item) { return item[1]; }), 83600 null, dimValueGetter$$1 83601 ); 83602 lineData.initData( 83603 map(optData, function (item) { return item[2]; }) 83604 ); 83605 lineData.hasItemOption = true; 83606 83607 return { 83608 from: fromData, 83609 to: toData, 83610 line: lineData 83611 }; 83612 } 83613 83614 /* 83615* Licensed to the Apache Software Foundation (ASF) under one 83616* or more contributor license agreements. See the NOTICE file 83617* distributed with this work for additional information 83618* regarding copyright ownership. The ASF licenses this file 83619* to you under the Apache License, Version 2.0 (the 83620* "License"); you may not use this file except in compliance 83621* with the License. You may obtain a copy of the License at 83622* 83623* http://www.apache.org/licenses/LICENSE-2.0 83624* 83625* Unless required by applicable law or agreed to in writing, 83626* software distributed under the License is distributed on an 83627* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 83628* KIND, either express or implied. See the License for the 83629* specific language governing permissions and limitations 83630* under the License. 83631*/ 83632 83633 registerPreprocessor(function (opt) { 83634 // Make sure markLine component is enabled 83635 opt.markLine = opt.markLine || {}; 83636 }); 83637 83638 /* 83639* Licensed to the Apache Software Foundation (ASF) under one 83640* or more contributor license agreements. See the NOTICE file 83641* distributed with this work for additional information 83642* regarding copyright ownership. The ASF licenses this file 83643* to you under the Apache License, Version 2.0 (the 83644* "License"); you may not use this file except in compliance 83645* with the License. You may obtain a copy of the License at 83646* 83647* http://www.apache.org/licenses/LICENSE-2.0 83648* 83649* Unless required by applicable law or agreed to in writing, 83650* software distributed under the License is distributed on an 83651* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 83652* KIND, either express or implied. See the License for the 83653* specific language governing permissions and limitations 83654* under the License. 83655*/ 83656 83657 MarkerModel.extend({ 83658 83659 type: 'markArea', 83660 83661 defaultOption: { 83662 zlevel: 0, 83663 // PENDING 83664 z: 1, 83665 tooltip: { 83666 trigger: 'item' 83667 }, 83668 // markArea should fixed on the coordinate system 83669 animation: false, 83670 label: { 83671 show: true, 83672 position: 'top' 83673 }, 83674 itemStyle: { 83675 // color and borderColor default to use color from series 83676 // color: 'auto' 83677 // borderColor: 'auto' 83678 borderWidth: 0 83679 }, 83680 83681 emphasis: { 83682 label: { 83683 show: true, 83684 position: 'top' 83685 } 83686 } 83687 } 83688 });
83689 83690 /* 83691* Licensed to the Apache Software Foundation (ASF) under one 83692* or more contributor license agreements. See the NOTICE file 83693* distributed with this work for additional information 83694* regarding copyright ownership. The ASF licenses this file 83695* to you under the Apache License, Version 2.0 (the 83696* "License"); you may not use this file except in compliance 83697* with the License. You may obtain a copy of the License at 83698* 83699* http://www.apache.org/licenses/LICENSE-2.0 83700* 83701* Unless required by applicable law or agreed to in writing, 83702* software distributed under the License is distributed on an 83703* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 83704* KIND, either express or implied. See the License for the 83705* specific language governing permissions and limitations 83706* under the License. 83707*/ 83708 83709// TODO Better on polar 83710 83711 var markAreaTransform = function (seriesModel, coordSys, maModel, item) { 83712 var lt = dataTransform(seriesModel, item[0]); 83713 var rb = dataTransform(seriesModel, item[1]); 83714 var retrieve$$1 = retrieve; 83715 83716 // FIXME make sure lt is less than rb 83717 var ltCoord = lt.coord; 83718 var rbCoord = rb.coord; 83719 ltCoord[0] = retrieve$$1(ltCoord[0], -Infinity); 83720 ltCoord[1] = retrieve$$1(ltCoord[1], -Infinity); 83721 83722 rbCoord[0] = retrieve$$1(rbCoord[0], Infinity); 83723 rbCoord[1] = retrieve$$1(rbCoord[1], Infinity); 83724 83725 // Merge option into one 83726 var result = mergeAll([{}, lt, rb]); 83727 83728 result.coord = [ 83729 lt.coord, rb.coord 83730 ]; 83731 result.x0 = lt.x; 83732 result.y0 = lt.y; 83733 result.x1 = rb.x; 83734 result.y1 = rb.y; 83735 return result; 83736 }; 83737 83738 function isInifinity$1(val) { 83739 return !isNaN(val) && !isFinite(val); 83740 } 83741 83742// If a markArea has one dim 83743 function ifMarkLineHasOnlyDim$1(dimIndex, fromCoord, toCoord, coordSys) { 83744 var otherDimIndex = 1 - dimIndex; 83745 return isInifinity$1(fromCoord[otherDimIndex]) && isInifinity$1(toCoord[otherDimIndex]); 83746 } 83747 83748 function markAreaFilter(coordSys, item) { 83749 var fromCoord = item.coord[0]; 83750 var toCoord = item.coord[1]; 83751 if (coordSys.type === 'cartesian2d') { 83752 // In case 83753 // { 83754 // markArea: { 83755 // data: [{ yAxis: 2 }] 83756 // } 83757 // } 83758 if ( 83759 fromCoord && toCoord && 83760 (ifMarkLineHasOnlyDim$1(1, fromCoord, toCoord, coordSys) 83761 || ifMarkLineHasOnlyDim$1(0, fromCoord, toCoord, coordSys)) 83762 ) { 83763 return true; 83764 } 83765 } 83766 return dataFilter$1(coordSys, { 83767 coord: fromCoord, 83768 x: item.x0, 83769 y: item.y0 83770 }) 83771 || dataFilter$1(coordSys, { 83772 coord: toCoord, 83773 x: item.x1, 83774 y: item.y1 83775 }); 83776 } 83777 83778// dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0'] 83779 function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) { 83780 var coordSys = seriesModel.coordinateSystem; 83781 var itemModel = data.getItemModel(idx); 83782 83783 var point; 83784 var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth()); 83785 var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight()); 83786 if (!isNaN(xPx) && !isNaN(yPx)) { 83787 point = [xPx, yPx]; 83788 } 83789 else { 83790 // Chart like bar may have there own marker positioning logic 83791 if (seriesModel.getMarkerPosition) { 83792 // Use the getMarkerPoisition 83793 point = seriesModel.getMarkerPosition( 83794 data.getValues(dims, idx) 83795 ); 83796 } 83797 else { 83798 var x = data.get(dims[0], idx); 83799 var y = data.get(dims[1], idx); 83800 var pt = [x, y]; 83801 coordSys.clampData && coordSys.clampData(pt, pt); 83802 point = coordSys.dataToPoint(pt, true); 83803 } 83804 if (coordSys.type === 'cartesian2d') { 83805 var xAxis = coordSys.getAxis('x'); 83806 var yAxis = coordSys.getAxis('y'); 83807 var x = data.get(dims[0], idx); 83808 var y = data.get(dims[1], idx); 83809 if (isInifinity$1(x)) { 83810 point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]); 83811 } 83812 else if (isInifinity$1(y)) { 83813 point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]); 83814 } 83815 } 83816 83817 // Use x, y if has any 83818 if (!isNaN(xPx)) { 83819 point[0] = xPx; 83820 } 83821 if (!isNaN(yPx)) { 83822 point[1] = yPx; 83823 } 83824 } 83825 83826 return point; 83827 } 83828 83829 var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']]; 83830 83831 MarkerView.extend({ 83832 83833 type: 'markArea', 83834 83835 // updateLayout: function (markAreaModel, ecModel, api) { 83836 // ecModel.eachSeries(function (seriesModel) { 83837 // var maModel = seriesModel.markAreaModel; 83838 // if (maModel) { 83839 // var areaData = maModel.getData(); 83840 // areaData.each(function (idx) { 83841 // var points = zrUtil.map(dimPermutations, function (dim) { 83842 // return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); 83843 // }); 83844 // // Layout 83845 // areaData.setItemLayout(idx, points); 83846 // var el = areaData.getItemGraphicEl(idx); 83847 // el.setShape('points', points); 83848 // }); 83849 // } 83850 // }, this); 83851 // }, 83852 83853 updateTransform: function (markAreaModel, ecModel, api) { 83854 ecModel.eachSeries(function (seriesModel) { 83855 var maModel = seriesModel.markAreaModel; 83856 if (maModel) { 83857 var areaData = maModel.getData(); 83858 areaData.each(function (idx) { 83859 var points = map(dimPermutations, function (dim) { 83860 return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); 83861 }); 83862 // Layout 83863 areaData.setItemLayout(idx, points); 83864 var el = areaData.getItemGraphicEl(idx); 83865 el.setShape('points', points); 83866 }); 83867 } 83868 }, this); 83869 }, 83870 83871 renderSeries: function (seriesModel, maModel, ecModel, api) { 83872 var coordSys = seriesModel.coordinateSystem; 83873 var seriesId = seriesModel.id; 83874 var seriesData = seriesModel.getData(); 83875 83876 var areaGroupMap = this.markerGroupMap; 83877 var polygonGroup = areaGroupMap.get(seriesId) 83878 || areaGroupMap.set(seriesId, {group: new Group()}); 83879 83880 this.group.add(polygonGroup.group); 83881 polygonGroup.__keep = true; 83882 83883 var areaData = createList$3(coordSys, seriesModel, maModel); 83884 83885 // Line data for tooltip and formatter 83886 maModel.setData(areaData); 83887 83888 // Update visual and layout of line 83889 areaData.each(function (idx) { 83890 // Layout 83891 areaData.setItemLayout(idx, map(dimPermutations, function (dim) { 83892 return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); 83893 })); 83894 83895 // Visual 83896 areaData.setItemVisual(idx, { 83897 color: seriesData.getVisual('color') 83898 }); 83899 }); 83900 83901 83902 areaData.diff(polygonGroup.__data) 83903 .add(function (idx) { 83904 var polygon = new Polygon({ 83905 shape: { 83906 points: areaData.getItemLayout(idx) 83907 } 83908 }); 83909 areaData.setItemGraphicEl(idx, polygon); 83910 polygonGroup.group.add(polygon); 83911 }) 83912 .update(function (newIdx, oldIdx) { 83913 var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx); 83914 updateProps(polygon, { 83915 shape: { 83916 points: areaData.getItemLayout(newIdx) 83917 } 83918 }, maModel, newIdx); 83919 polygonGroup.group.add(polygon); 83920 areaData.setItemGraphicEl(newIdx, polygon); 83921 }) 83922 .remove(function (idx) { 83923 var polygon = polygonGroup.__data.getItemGraphicEl(idx); 83924 polygonGroup.group.remove(polygon); 83925 }) 83926 .execute(); 83927 83928 areaData.eachItemGraphicEl(function (polygon, idx) { 83929 var itemModel = areaData.getItemModel(idx); 83930 var labelModel = itemModel.getModel('label'); 83931 var labelHoverModel = itemModel.getModel('emphasis.label'); 83932 var color = areaData.getItemVisual(idx, 'color'); 83933 polygon.useStyle( 83934 defaults( 83935 itemModel.getModel('itemStyle').getItemStyle(), 83936 { 83937 fill: modifyAlpha(color, 0.4), 83938 stroke: color 83939 } 83940 ) 83941 ); 83942 83943 polygon.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle(); 83944 83945 setLabelStyle( 83946 polygon.style, polygon.hoverStyle, labelModel, labelHoverModel, 83947 { 83948 labelFetcher: maModel,
83949 labelDataIndex: idx, 83950 defaultText: areaData.getName(idx) || '', 83951 isRectText: true, 83952 autoColor: color 83953 } 83954 ); 83955 83956 setHoverStyle(polygon, {}); 83957 83958 polygon.dataModel = maModel; 83959 }); 83960 83961 polygonGroup.__data = areaData; 83962 83963 polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent'); 83964 } 83965 }); 83966 83967 /** 83968 * @inner 83969 * @param {module:echarts/coord/*} coordSys 83970 * @param {module:echarts/model/Series} seriesModel 83971 * @param {module:echarts/model/Model} mpModel 83972 */ 83973 function createList$3(coordSys, seriesModel, maModel) { 83974 83975 var coordDimsInfos; 83976 var areaData; 83977 var dims = ['x0', 'y0', 'x1', 'y1']; 83978 if (coordSys) { 83979 coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { 83980 var data = seriesModel.getData(); 83981 var info = data.getDimensionInfo( 83982 data.mapDimension(coordDim) 83983 ) || {}; 83984 // In map series data don't have lng and lat dimension. Fallback to same with coordSys 83985 return defaults({name: coordDim}, info); 83986 }); 83987 areaData = new List(map(dims, function (dim, idx) { 83988 return { 83989 name: dim, 83990 type: coordDimsInfos[idx % 2].type 83991 }; 83992 }), maModel); 83993 } 83994 else { 83995 coordDimsInfos =[{ 83996 name: 'value', 83997 type: 'float' 83998 }]; 83999 areaData = new List(coordDimsInfos, maModel); 84000 } 84001 84002 var optData = map(maModel.get('data'), curry( 84003 markAreaTransform, seriesModel, coordSys, maModel 84004 )); 84005 if (coordSys) { 84006 optData = filter( 84007 optData, curry(markAreaFilter, coordSys) 84008 ); 84009 } 84010 84011 var dimValueGetter$$1 = coordSys ? function (item, dimName, dataIndex, dimIndex) { 84012 return item.coord[Math.floor(dimIndex / 2)][dimIndex % 2]; 84013 } : function (item) { 84014 return item.value; 84015 }; 84016 areaData.initData(optData, null, dimValueGetter$$1); 84017 areaData.hasItemOption = true; 84018 return areaData; 84019 } 84020 84021 /* 84022* Licensed to the Apache Software Foundation (ASF) under one 84023* or more contributor license agreements. See the NOTICE file 84024* distributed with this work for additional information 84025* regarding copyright ownership. The ASF licenses this file 84026* to you under the Apache License, Version 2.0 (the 84027* "License"); you may not use this file except in compliance 84028* with the License. You may obtain a copy of the License at 84029* 84030* http://www.apache.org/licenses/LICENSE-2.0 84031* 84032* Unless required by applicable law or agreed to in writing, 84033* software distributed under the License is distributed on an 84034* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84035* KIND, either express or implied. See the License for the 84036* specific language governing permissions and limitations 84037* under the License. 84038*/ 84039 84040 registerPreprocessor(function (opt) { 84041 // Make sure markArea component is enabled 84042 opt.markArea = opt.markArea || {}; 84043 }); 84044 84045 /* 84046* Licensed to the Apache Software Foundation (ASF) under one 84047* or more contributor license agreements. See the NOTICE file 84048* distributed with this work for additional information 84049* regarding copyright ownership. The ASF licenses this file 84050* to you under the Apache License, Version 2.0 (the 84051* "License"); you may not use this file except in compliance 84052* with the License. You may obtain a copy of the License at 84053* 84054* http://www.apache.org/licenses/LICENSE-2.0 84055* 84056* Unless required by applicable law or agreed to in writing, 84057* software distributed under the License is distributed on an 84058* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84059* KIND, either express or implied. See the License for the 84060* specific language governing permissions and limitations 84061* under the License. 84062*/ 84063 84064 var preprocessor$3 = function (option) { 84065 var timelineOpt = option && option.timeline; 84066 84067 if (!isArray(timelineOpt)) { 84068 timelineOpt = timelineOpt ? [timelineOpt] : []; 84069 } 84070 84071 each$1(timelineOpt, function (opt) { 84072 if (!opt) { 84073 return; 84074 } 84075 84076 compatibleEC2(opt); 84077 }); 84078 }; 84079 84080 function compatibleEC2(opt) { 84081 var type = opt.type; 84082 84083 var ec2Types = {'number': 'value', 'time': 'time'}; 84084 84085 // Compatible with ec2 84086 if (ec2Types[type]) { 84087 opt.axisType = ec2Types[type]; 84088 delete opt.type; 84089 } 84090 84091 transferItem(opt); 84092 84093 if (has$2(opt, 'controlPosition')) { 84094 var controlStyle = opt.controlStyle || (opt.controlStyle = {}); 84095 if (!has$2(controlStyle, 'position')) { 84096 controlStyle.position = opt.controlPosition; 84097 } 84098 if (controlStyle.position === 'none' && !has$2(controlStyle, 'show')) { 84099 controlStyle.show = false; 84100 delete controlStyle.position; 84101 } 84102 delete opt.controlPosition; 84103 } 84104 84105 each$1(opt.data || [], function (dataItem) { 84106 if (isObject$1(dataItem) && !isArray(dataItem)) { 84107 if (!has$2(dataItem, 'value') && has$2(dataItem, 'name')) { 84108 // In ec2, using name as value. 84109 dataItem.value = dataItem.name; 84110 } 84111 transferItem(dataItem); 84112 } 84113 }); 84114 } 84115 84116 function transferItem(opt) { 84117 var itemStyle = opt.itemStyle || (opt.itemStyle = {}); 84118
84119 var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {}); 84120 84121 // Transfer label out 84122 var label = opt.label || (opt.label || {}); 84123 var labelNormal = label.normal || (label.normal = {}); 84124 var excludeLabelAttr = {normal: 1, emphasis: 1}; 84125 84126 each$1(label, function (value, name) { 84127 if (!excludeLabelAttr[name] && !has$2(labelNormal, name)) { 84128 labelNormal[name] = value; 84129 } 84130 }); 84131 84132 if (itemStyleEmphasis.label && !has$2(label, 'emphasis')) { 84133 label.emphasis = itemStyleEmphasis.label; 84134 delete itemStyleEmphasis.label; 84135 } 84136 } 84137 84138 function has$2(obj, attr) { 84139 return obj.hasOwnProperty(attr); 84140 } 84141 84142 /* 84143* Licensed to the Apache Software Foundation (ASF) under one 84144* or more contributor license agreements. See the NOTICE file 84145* distributed with this work for additional information 84146* regarding copyright ownership. The ASF licenses this file 84147* to you under the Apache License, Version 2.0 (the 84148* "License"); you may not use this file except in compliance 84149* with the License. You may obtain a copy of the License at 84150* 84151* http://www.apache.org/licenses/LICENSE-2.0 84152* 84153* Unless required by applicable law or agreed to in writing, 84154* software distributed under the License is distributed on an 84155* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84156* KIND, either express or implied. See the License for the 84157* specific language governing permissions and limitations 84158* under the License. 84159*/ 84160 84161 ComponentModel.registerSubTypeDefaulter('timeline', function () { 84162 // Only slider now. 84163 return 'slider'; 84164 }); 84165 84166 /* 84167* Licensed to the Apache Software Foundation (ASF) under one 84168* or more contributor license agreements. See the NOTICE file 84169* distributed with this work for additional information 84170* regarding copyright ownership. The ASF licenses this file 84171* to you under the Apache License, Version 2.0 (the 84172* "License"); you may not use this file except in compliance 84173* with the License. You may obtain a copy of the License at 84174* 84175* http://www.apache.org/licenses/LICENSE-2.0 84176* 84177* Unless required by applicable law or agreed to in writing, 84178* software distributed under the License is distributed on an 84179* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84180* KIND, either express or implied. See the License for the 84181* specific language governing permissions and limitations 84182* under the License. 84183*/ 84184 84185 registerAction( 84186 84187 {type: 'timelineChange', event: 'timelineChanged', update: 'prepareAndUpdate'}, 84188 84189 function (payload, ecModel) { 84190 84191 var timelineModel = ecModel.getComponent('timeline'); 84192 if (timelineModel && payload.currentIndex != null) { 84193 timelineModel.setCurrentIndex(payload.currentIndex); 84194 84195 if (!timelineModel.get('loop', true) && timelineModel.isIndexMax()) { 84196 timelineModel.setPlayState(false); 84197 } 84198 } 84199 84200 // Set normalized currentIndex to payload. 84201 ecModel.resetOption('timeline'); 84202 84203 return defaults({ 84204 currentIndex: timelineModel.option.currentIndex 84205 }, payload); 84206 } 84207 ); 84208 84209 registerAction( 84210 84211 {type: 'timelinePlayChange', event: 'timelinePlayChanged', update: 'update'}, 84212 84213 function (payload, ecModel) { 84214 var timelineModel = ecModel.getComponent('timeline'); 84215 if (timelineModel && payload.playState != null) { 84216 timelineModel.setPlayState(payload.playState); 84217 } 84218 } 84219 ); 84220 84221 /* 84222* Licensed to the Apache Software Foundation (ASF) under one 84223* or more contributor license agreements. See the NOTICE file 84224* distributed with this work for additional information 84225* regarding copyright ownership. The ASF licenses this file 84226* to you under the Apache License, Version 2.0 (the 84227* "License"); you may not use this file except in compliance 84228* with the License. You may obtain a copy of the License at 84229* 84230* http://www.apache.org/licenses/LICENSE-2.0 84231* 84232* Unless required by applicable law or agreed to in writing, 84233* software distributed under the License is distributed on an 84234* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84235* KIND, either express or implied. See the License for the 84236* specific language governing permissions and limitations 84237* under the License. 84238*/ 84239 84240 var TimelineModel = ComponentModel.extend({ 84241 84242 type: 'timeline', 84243 84244 layoutMode: 'box', 84245 84246 /** 84247 * @protected 84248 */ 84249 defaultOption: { 84250 84251 zlevel: 0, // ä¸â¬Ã§ÂºÂ§Ã¥Â±âå 84252 z: 4, // äºÅ级å±âå 84253 show: true, 84254 84255 axisType: 'time', // 模å¼ÂæË¯æâ¶éâ´ç±»åžâ¹Ã¯Â¼Åæâ¯æÅ value, category 84256 84257 realtime: true, 84258 84259 left: '20%', 84260 top: null, 84261 right: '20%', 84262 bottom: 0, 84263 width: null, 84264 height: 40, 84265 padding: 5, 84266 84267 controlPosition: 'left', // 'left' 'right' 'top' 'bottom' 'none'
84268 autoPlay: false, 84269 rewind: false, // Ã¥ÂÂÃ¥ÂâæâÂæâ¾ 84270 loop: true, 84271 playInterval: 2000, // æâÂæâ¾æâ¶éâ´éâ´éšâï¼ÅÃ¥Ââ¢Ã¤Â½Âms 84272 84273 currentIndex: 0, 84274 84275 itemStyle: {}, 84276 label: { 84277 color: '#000' 84278 }, 84279 84280 data: [] 84281 }, 84282 84283 /** 84284 * @override 84285 */ 84286 init: function (option, parentModel, ecModel) { 84287 84288 /** 84289 * @private 84290 * @type {module:echarts/data/List} 84291 */ 84292 this._data; 84293 84294 /** 84295 * @private 84296 * @type {Array.<string>} 84297 */ 84298 this._names; 84299 84300 this.mergeDefaultAndTheme(option, ecModel); 84301 this._initData(); 84302 }, 84303 84304 /** 84305 * @override 84306 */ 84307 mergeOption: function (option) { 84308 TimelineModel.superApply(this, 'mergeOption', arguments); 84309 this._initData(); 84310 }, 84311 84312 /** 84313 * @param {number} [currentIndex] 84314 */ 84315 setCurrentIndex: function (currentIndex) { 84316 if (currentIndex == null) { 84317 currentIndex = this.option.currentIndex; 84318 } 84319 var count = this._data.count(); 84320 84321 if (this.option.loop) { 84322 currentIndex = (currentIndex % count + count) % count; 84323 } 84324 else { 84325 currentIndex >= count && (currentIndex = count - 1); 84326 currentIndex < 0 && (currentIndex = 0); 84327 } 84328 84329 this.option.currentIndex = currentIndex; 84330 }, 84331 84332 /** 84333 * @return {number} currentIndex 84334 */ 84335 getCurrentIndex: function () { 84336 return this.option.currentIndex; 84337 }, 84338 84339 /** 84340 * @return {boolean} 84341 */ 84342 isIndexMax: function () { 84343 return this.getCurrentIndex() >= this._data.count() - 1; 84344 }, 84345 84346 /** 84347 * @param {boolean} state true: play, false: stop 84348 */ 84349 setPlayState: function (state) { 84350 this.option.autoPlay = !!state; 84351 }, 84352 84353 /** 84354 * @return {boolean} true: play, false: stop 84355 */ 84356 getPlayState: function () { 84357 return !!this.option.autoPlay; 84358 }, 84359 84360 /** 84361 * @private 84362 */ 84363 _initData: function () { 84364 var thisOption = this.option; 84365 var dataArr = thisOption.data || []; 84366 var axisType = thisOption.axisType; 84367 var names = this._names = []; 84368 84369 if (axisType === 'category') { 84370 var idxArr = []; 84371 each$1(dataArr, function (item, index) { 84372 var value = getDataItemValue(item); 84373 var newItem; 84374 84375 if (isObject$1(item)) { 84376 newItem = clone(item); 84377 newItem.value = index; 84378 } 84379 else { 84380 newItem = index; 84381 } 84382
84383 idxArr.push(newItem); 84384 84385 if (!isString(value) && (value == null || isNaN(value))) { 84386 value = ''; 84387 } 84388 84389 names.push(value + ''); 84390 }); 84391 dataArr = idxArr; 84392 } 84393 84394 var dimType = ({category: 'ordinal', time: 'time'})[axisType] || 'number'; 84395 84396 var data = this._data = new List([{name: 'value', type: dimType}], this); 84397 84398 data.initData(dataArr, names); 84399 }, 84400 84401 getData: function () { 84402 return this._data; 84403 }, 84404 84405 /** 84406 * @public 84407 * @return {Array.<string>} categoreis 84408 */ 84409 getCategories: function () { 84410 if (this.get('axisType') === 'category') { 84411 return this._names.slice(); 84412 } 84413 } 84414 84415 }); 84416 84417 /* 84418* Licensed to the Apache Software Foundation (ASF) under one 84419* or more contributor license agreements. See the NOTICE file 84420* distributed with this work for additional information 84421* regarding copyright ownership. The ASF licenses this file 84422* to you under the Apache License, Version 2.0 (the 84423* "License"); you may not use this file except in compliance 84424* with the License. You may obtain a copy of the License at 84425* 84426* http://www.apache.org/licenses/LICENSE-2.0 84427* 84428* Unless required by applicable law or agreed to in writing, 84429* software distributed under the License is distributed on an 84430* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84431* KIND, either express or implied. See the License for the 84432* specific language governing permissions and limitations 84433* under the License. 84434*/ 84435 84436 var SliderTimelineModel = TimelineModel.extend({ 84437 84438 type: 'timeline.slider', 84439 84440 /** 84441 * @protected 84442 */ 84443 defaultOption: { 84444 84445 backgroundColor: 'rgba(0,0,0,0)', // æâ¶éâ´è½´èÆÅæâ¢Â¯Ã©Â¢Åèâ°Â² 84446 borderColor: '#ccc', // æâ¶éâ´è½´è¾¹æ¡â é¢Åèâ°Â² 84447 borderWidth: 0, // æâ¶éâ´è½´è¾¹æ¡â 线宽ï¼ÅÃ¥Ââ¢Ã¤Â½Âpxï¼Åé»Ë认为0ï¼Ëæâ è¾¹æ¡â ï¼Ⱐ84448 84449 orient: 'horizontal', // 'vertical' 84450 inverse: false, 84451 84452 tooltip: { // boolean or Object 84453 trigger: 'item' // data item may also have tootip attr. 84454 }, 84455 84456 symbol: 'emptyCircle', 84457 symbolSize: 10, 84458 84459 lineStyle: { 84460 show: true, 84461 width: 2, 84462 color: '#304654' 84463 }, 84464 label: { // æââ¡Ã¦Å¾ â¡Ã§Â¾ 84465 position: 'auto', // auto left right top bottom 84466 // When using number, label position is not 84467 // restricted by viewRect. 84468 // positive: right/bottom, negative: left/top 84469 show: true, 84470 interval: 'auto', 84471 rotate: 0, 84472 // formatter: null, 84473 // Ã¥â¦Â¶Ã¤Â½â¢Ã¥Â±Å¾Ã¦â¬Â§Ã©Â»Ë认使çâ¨åâ¦Â¨Ã¥Â±â¬Ã¦ââ¡Ã¦Å¾ ·å¼Âï¼Å详è§ÂTEXTSTYLE 84474 color: '#304654' 84475 }, 84476 itemStyle: { 84477 color: '#304654', 84478 borderWidth: 1 84479 }, 84480 84481 checkpointStyle: { 84482 symbol: 'circle', 84483 symbolSize: 13, 84484 color: '#c23531', 84485 borderWidth: 5, 84486 borderColor: 'rgba(194,53,49, 0.5)', 84487 animation: true, 84488 animationDuration: 300, 84489 animationEasing: 'quinticInOut' 84490 }, 84491 84492 controlStyle: { 84493 show: true, 84494 showPlayBtn: true, 84495 showPrevBtn: true, 84496 showNextBtn: true, 84497 itemSize: 22, 84498 itemGap: 12, 84499 position: 'left', // 'left' 'right' 'top' 'bottom' 84500 playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S1
845007.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z', // jshint ignore:line 84501 stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z', // jshint ignore:line 84502 nextIcon: 'path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z', // jshint ignore:line 84503 prevIcon: 'path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z', // jshint ignore:line 84504 84505 color: '#304654', 84506 borderColor: '#304654', 84507 borderWidth: 1 84508 }, 84509 84510 emphasis: { 84511 label: { 84512 show: true, 84513 // Ã¥â¦Â¶Ã¤Â½â¢Ã¥Â±Å¾Ã¦â¬Â§Ã©Â»Ë认使çâ¨åâ¦Â¨Ã¥Â±â¬Ã¦ââ¡Ã¦Å¾ ·å¼Âï¼Å详è§ÂTEXTSTYLE 84514 color: '#c23531' 84515 }, 84516 84517 itemStyle: { 84518 color: '#c23531' 84519 }, 84520 84521 controlStyle: { 84522 color: '#c23531', 84523 borderColor: '#c23531', 84524 borderWidth: 2 84525 } 84526 }, 84527 data: [] 84528 } 84529 84530 }); 84531 84532 mixin(SliderTimelineModel, dataFormatMixin); 84533 84534 /* 84535* Licensed to the Apache Software Foundation (ASF) under one 84536* or more contributor license agreements. See the NOTICE file 84537* distributed with this work for additional information 84538* regarding copyright ownership. The ASF licenses this file 84539* to you under the Apache License, Version 2.0 (the 84540* "License"); you may not use this file except in compliance 84541* with the License. You may obtain a copy of the License at 84542* 84543* http://www.apache.org/licenses/LICENSE-2.0 84544* 84545* Unless required by applicable law or agreed to in writing, 84546* software distributed under the License is distributed on an 84547* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84548* KIND, either express or implied. See the License for the 84549* specific language governing permissions and limitations 84550* under the License. 84551*/ 84552 84553 var TimelineView = Component.extend({ 84554 type: 'timeline' 84555 }); 84556 84557 /* 84558* Licensed to the Apache Software Foundation (ASF) under one 84559* or more contributor license agreements. See the NOTICE file 84560* distributed with this work for additional information 84561* regarding copyright ownership. The ASF licenses this file 84562* to you under the Apache License, Version 2.0 (the 84563* "License"); you may not use this file except in compliance 84564* with the License. You may obtain a copy of the License at 84565* 84566* http://www.apache.org/licenses/LICENSE-2.0 84567* 84568* Unless required by applicable law or agreed to in writing, 84569* software distributed under the License is distributed on an 84570* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84571* KIND, either express or implied. See the License for the 84572* specific language governing permissions and limitations 84573* under the License. 84574*/ 84575 84576 /** 84577 * Extend axis 2d 84578 * @constructor module:echarts/coord/cartesian/Axis2D 84579 * @extends {module:echarts/coord/cartesian/Axis} 84580 * @param {string} dim 84581 * @param {*} scale 84582 * @param {Array.<number>} coordExtent 84583 * @param {string} axisType 84584 * @param {string} position 84585 */ 84586 var TimelineAxis = function (dim, scale, coordExtent, axisType) { 84587 84588 Axis.call(this, dim, scale, coordExtent); 84589 84590 /** 84591 * Axis type 84592 * - 'category' 84593 * - 'value' 84594 * - 'time' 84595 * - 'log' 84596 * @type {string} 84597 */ 84598 this.type = axisType || 'value'; 84599 84600 /** 84601 * Axis model 84602 * @param {module:echarts/component/TimelineModel} 84603 */ 84604 this.model = null; 84605 }; 84606 84607 TimelineAxis.prototype = { 84608 84609 constructor: TimelineAxis, 84610 84611 /** 84612 * @override 84613 */ 84614 getLabelModel: function () { 84615 return this.model.getModel('label'); 84616 }, 84617 84618 /** 84619 * @override 84620 */ 84621 isHorizontal: function () { 84622 return this.model.get('orient') === 'horizontal'; 84623 } 84624 84625 }; 84626 84627 inherits(TimelineAxis, Axis); 84628 84629 /* 84630* Licensed to the Apache Software Foundation (ASF) under one 84631* or more contributor license agreements. See the NOTICE file 84632* distributed with this work for additional information 84633* regarding copyright ownership. The ASF licenses this file 84634* to you under the Apache License, Version 2.0 (the 84635* "License");
84635 you may not use this file except in compliance 84636* with the License. You may obtain a copy of the License at 84637* 84638* http://www.apache.org/licenses/LICENSE-2.0 84639* 84640* Unless required by applicable law or agreed to in writing, 84641* software distributed under the License is distributed on an 84642* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 84643* KIND, either express or implied. See the License for the 84644* specific language governing permissions and limitations 84645* under the License. 84646*/ 84647 84648 var bind$6 = bind; 84649 var each$27 = each$1; 84650 84651 var PI$4 = Math.PI; 84652 84653 TimelineView.extend({ 84654 84655 type: 'timeline.slider', 84656 84657 init: function (ecModel, api) { 84658 84659 this.api = api; 84660 84661 /** 84662 * @private 84663 * @type {module:echarts/component/timeline/TimelineAxis} 84664 */ 84665 this._axis; 84666 84667 /** 84668 * @private 84669 * @type {module:zrender/core/BoundingRect} 84670 */ 84671 this._viewRect; 84672 84673 /** 84674 * @type {number} 84675 */ 84676 this._timer; 84677 84678 /** 84679 * @type {module:zrender/Element} 84680 */ 84681 this._currentPointer; 84682 84683 /** 84684 * @type {module:zrender/container/Group} 84685 */ 84686 this._mainGroup; 84687 84688 /** 84689 * @type {module:zrender/container/Group} 84690 */ 84691 this._labelGroup; 84692 }, 84693 84694 /** 84695 * @override 84696 */ 84697 render: function (timelineModel, ecModel, api, payload) { 84698 this.model = timelineModel; 84699 this.api = api; 84700 this.ecModel = ecModel; 84701 84702 this.group.removeAll(); 84703 84704 if (timelineModel.get('show', true)) { 84705 84706 var layoutInfo = this._layout(timelineModel, api); 84707 var mainGroup = this._createGroup('mainGroup'); 84708 var labelGroup = this._createGroup('labelGroup'); 84709 84710 /** 84711 * @private 84712 * @type {module:echarts/component/timeline/TimelineAxis} 84713 */ 84714 var axis = this._axis = this._createAxis(layoutInfo, timelineModel); 84715 84716 timelineModel.formatTooltip = function (dataIndex) { 84717 return encodeHTML(axis.scale.getLabel(dataIndex)); 84718 }; 84719 84720 each$27( 84721 ['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], 84722 function (name) { 84723 this['_render' + name](layoutInfo, mainGroup, axis, timelineModel); 84724 }, 84725 this 84726 ); 84727 84728 this._renderAxisLabel(layoutInfo, labelGroup, axis, timelineModel); 84729 this._position(layoutInfo, timelineModel); 84730 } 84731 84732 this._doPlayStop(); 84733 }, 84734 84735 /** 84736 * @override 84737 */ 84738 remove: function () { 84739 this._clearTimer(); 84740 this.group.removeAll(); 84741 }, 84742 84743 /** 84744 * @override 84745 */ 84746 dispose: function () { 84747 this._clearTimer(); 84748 }, 84749 84750 _layout: function (timelineModel, api) { 84751 var labelPosOpt = timelineModel.get('label.position'); 84752 var orient = timelineModel.get('orient'); 84753 var viewRect = getViewRect$4(timelineModel, api); 84754 // Auto label offset. 84755 if (labelPosOpt == null || labelPosOpt === 'auto') { 84756 labelPosOpt = orient === 'horizontal' 84757 ? ((viewRect.y + viewRect.height / 2) < api.getHeight() / 2 ? '-' : '+') 84758 : ((viewRect.x + viewRect.width / 2) < api.getWidth() / 2 ? '+' : '-'); 84759 } 84760 else if (isNaN(labelPosOpt)) { 84761 labelPosOpt = ({ 84762 horizontal: {top: '-', bottom: '+'}, 84763 vertical: {left: '-', right: '+'} 84764 })[orient][labelPosOpt]; 84765 } 84766 84767 var labelAlignMap = { 84768 horizontal: 'center', 84769 vertical: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'left' : 'right' 84770 }; 84771 84772 var labelBaselineMap = { 84773 horizontal: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'top' : 'bottom', 84774 vertical: 'middle' 84775 }; 84776 var rotationMap = { 84777 horizontal: 0, 84778 vertical: PI$4 / 2 84779 }; 84780 84781 // Position 84782 var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width; 84783 84784 var controlModel = timelineModel.getModel('controlStyle'); 84785 var showControl = controlModel.get('show', true); 84786 var controlSize = showControl ? controlModel.get('itemSize') : 0;
84787 var controlGap = showControl ? controlModel.get('itemGap') : 0; 84788 var sizePlusGap = controlSize + controlGap; 84789 84790 // Special label rotate. 84791 var labelRotation = timelineModel.get('label.rotate') || 0; 84792 labelRotation = labelRotation * PI$4 / 180; // To radian. 84793 84794 var playPosition; 84795 var prevBtnPosition; 84796 var nextBtnPosition; 84797 var axisExtent; 84798 var controlPosition = controlModel.get('position', true); 84799 var showPlayBtn = showControl && controlModel.get('showPlayBtn', true); 84800 var showPrevBtn = showControl && controlModel.get('showPrevBtn', true); 84801 var showNextBtn = showControl && controlModel.get('showNextBtn', true); 84802 var xLeft = 0; 84803 var xRight = mainLength; 84804 84805 // position[0] means left, position[1] means middle. 84806 if (controlPosition === 'left' || controlPosition === 'bottom') { 84807 showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap); 84808 showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap); 84809 showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); 84810 } 84811 else { // 'top' 'right' 84812 showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); 84813 showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap); 84814 showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); 84815 } 84816 axisExtent = [xLeft, xRight]; 84817 84818 if (timelineModel.get('inverse')) { 84819 axisExtent.reverse(); 84820 } 84821 84822 return { 84823 viewRect: viewRect, 84824 mainLength: mainLength, 84825 orient: orient, 84826 84827 rotation: rotationMap[orient], 84828 labelRotation: labelRotation, 84829 labelPosOpt: labelPosOpt, 84830 labelAlign: timelineModel.get('label.align') || labelAlignMap[orient], 84831 labelBaseline: timelineModel.get('label.verticalAlign') 84832 || timelineModel.get('label.baseline') 84833 || labelBaselineMap[orient], 84834 84835 // Based on mainGroup. 84836 playPosition: playPosition, 84837 prevBtnPosition: prevBtnPosition, 84838 nextBtnPosition: nextBtnPosition, 84839 axisExtent: axisExtent, 84840 84841 controlSize: controlSize, 84842 controlGap: controlGap 84843 }; 84844 }, 84845 84846 _position: function (layoutInfo, timelineModel) { 84847 // Position is be called finally, because bounding rect is needed for 84848 // adapt content to fill viewRect (auto adapt offset). 84849 84850 // Timeline may be not all in the viewRect when 'offset' is specified 84851 // as a number, because it is more appropriate that label aligns at 84852 // 'offset' but not the other edge defined by viewRect. 84853 84854 var mainGroup = this._mainGroup; 84855 var labelGroup = this._labelGroup; 84856 84857 var viewRect = layoutInfo.viewRect; 84858 if (layoutInfo.orient === 'vertical') { 84859 // transform to horizontal, inverse rotate by left-top point. 84860 var m = create$1(); 84861 var rotateOriginX = viewRect.x; 84862 var rotateOriginY = viewRect.y + viewRect.height; 84863 translate(m, m, [-rotateOriginX, -rotateOriginY]); 84864 rotate(m, m, -PI$4 / 2); 84865 translate(m, m, [rotateOriginX, rotateOriginY]); 84866 viewRect = viewRect.clone(); 84867 viewRect.applyTransform(m); 84868 } 84869 84870 var viewBound = getBound(viewRect); 84871 var mainBound = getBound(mainGroup.getBoundingRect()); 84872 var labelBound = getBound(labelGroup.getBoundingRect()); 84873 84874 var mainPosition = mainGroup.position; 84875 var labelsPosition = labelGroup.position; 84876
84877 labelsPosition[0] = mainPosition[0] = viewBound[0][0]; 84878 84879 var labelPosOpt = layoutInfo.labelPosOpt; 84880 84881 if (isNaN(labelPosOpt)) { // '+' or '-' 84882 var mainBoundIdx = labelPosOpt === '+' ? 0 : 1; 84883 toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); 84884 toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx); 84885 } 84886 else { 84887 var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1; 84888 toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); 84889 labelsPosition[1] = mainPosition[1] + labelPosOpt; 84890 } 84891 84892 mainGroup.attr('position', mainPosition); 84893 labelGroup.attr('position', labelsPosition); 84894 mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation; 84895 84896 setOrigin(mainGroup); 84897 setOrigin(labelGroup); 84898 84899 function setOrigin(targetGroup) { 84900 var pos = targetGroup.position; 84901 targetGroup.origin = [
84902 viewBound[0][0] - pos[0], 84903 viewBound[1][0] - pos[1] 84904 ]; 84905 } 84906 84907 function getBound(rect) { 84908 // [[xmin, xmax], [ymin, ymax]] 84909 return [ 84910 [rect.x, rect.x + rect.width], 84911 [rect.y, rect.y + rect.height] 84912 ]; 84913 } 84914 84915 function toBound(fromPos, from, to, dimIdx, boundIdx) { 84916 fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx]; 84917 } 84918 }, 84919 84920 _createAxis: function (layoutInfo, timelineModel) { 84921 var data = timelineModel.getData(); 84922 var axisType = timelineModel.get('axisType'); 84923 84924 var scale = createScaleByModel(timelineModel, axisType); 84925 84926 // Customize scale. The `tickValue` is `dataIndex`. 84927 scale.getTicks = function () { 84928 return data.mapArray(['value'], function (value) { 84929 return value; 84930 }); 84931 }; 84932 84933 var dataExtent = data.getDataExtent('value'); 84934 scale.setExtent(dataExtent[0], dataExtent[1]); 84935 scale.niceTicks(); 84936 84937 var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType); 84938 axis.model = timelineModel; 84939 84940 return axis; 84941 }, 84942 84943 _createGroup: function (name) { 84944 var newGroup = this['_' + name] = new Group(); 84945 this.group.add(newGroup); 84946 return newGroup; 84947 }, 84948 84949 _renderAxisLine: function (layoutInfo, group, axis, timelineModel) { 84950 var axisExtent = axis.getExtent(); 84951 84952 if (!timelineModel.get('lineStyle.show')) { 84953 return; 84954 } 84955 84956 group.add(new Line({ 84957 shape: { 84958 x1: axisExtent[0], y1: 0, 84959 x2: axisExtent[1], y2: 0 84960 }, 84961 style: extend( 84962 {lineCap: 'round'}, 84963 timelineModel.getModel('lineStyle').getLineStyle() 84964 ), 84965 silent: true, 84966 z2: 1 84967 })); 84968 }, 84969 84970 /** 84971 * @private 84972 */ 84973 _renderAxisTick: function (layoutInfo, group, axis, timelineModel) { 84974 var data = timelineModel.getData(); 84975 // Show all ticks, despite ignoring strategy. 84976 var ticks = axis.scale.getTicks(); 84977 84978 // The value is dataIndex, see the costomized scale. 84979 each$27(ticks, function (value) { 84980 var tickCoord = axis.dataToCoord(value); 84981 var itemModel = data.getItemModel(value); 84982 var itemStyleModel = itemModel.getModel('itemStyle'); 84983 var hoverStyleModel = itemModel.getModel('emphasis.itemStyle'); 84984 var symbolOpt = { 84985 position: [tickCoord, 0], 84986 onclick: bind$6(this._changeTimeline, this, value) 84987 }; 84988 var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt); 84989 setHoverStyle(el, hoverStyleModel.getItemStyle()); 84990 84991 if (itemModel.get('tooltip')) { 84992 el.dataIndex = value; 84993 el.dataModel = timelineModel; 84994 } 84995 else { 84996 el.dataIndex = el.dataModel = null; 84997 } 84998 84999 }, this); 85000 }, 85001 85002 /** 85003 * @private 85004 */ 85005 _renderAxisLabel: function (layoutInfo, group, axis, timelineModel) { 85006 var labelModel = axis.getLabelModel(); 85007 85008 if (!labelModel.get('show')) { 85009 return; 85010 } 85011 85012 var data = timelineModel.getData(); 85013 var labels = axis.getViewLabels(); 85014 85015 each$27(labels, function (labelItem) { 85016 // The tickValue is dataIndex, see the costomized scale. 85017 var dataIndex = labelItem.tickValue; 85018 85019 var itemModel = data.getItemModel(dataIndex); 85020 var normalLabelModel = itemModel.getModel('label'); 85021 var hoverLabelModel = itemModel.getModel('emphasis.label'); 85022 var tickCoord = axis.dataToCoord(labelItem.tickValue); 85023 var textEl = new Text({ 85024 position: [tickCoord, 0], 85025 rotation: layoutInfo.labelRotation - layoutInfo.rotation,
85026 onclick: bind$6(this._changeTimeline, this, dataIndex), 85027 silent: false 85028 }); 85029 setTextStyle(textEl.style, normalLabelModel, { 85030 text: labelItem.formattedLabel, 85031 textAlign: layoutInfo.labelAlign, 85032 textVerticalAlign: layoutInfo.labelBaseline 85033 }); 85034 85035 group.add(textEl); 85036 setHoverStyle( 85037 textEl, setTextStyle({}, hoverLabelModel) 85038 ); 85039 85040 }, this); 85041 }, 85042 85043 /** 85044 * @private 85045 */ 85046 _renderControl: function (layoutInfo, group, axis, timelineModel) { 85047 var controlSize = layoutInfo.controlSize; 85048 var rotation = layoutInfo.rotation; 85049 85050 var itemStyle = timelineModel.getModel('controlStyle').getItemStyle(); 85051 var hoverStyle = timelineModel.getModel('emphasis.controlStyle').getItemStyle(); 85052 var rect = [0, -controlSize / 2, controlSize, controlSize]; 85053 var playState = timelineModel.getPlayState(); 85054 var inverse = timelineModel.get('inverse', true); 85055 85056 makeBtn( 85057 layoutInfo.nextBtnPosition, 85058 'controlStyle.nextIcon', 85059 bind$6(this._changeTimeline, this, inverse ? '-' : '+') 85060 ); 85061 makeBtn( 85062 layoutInfo.prevBtnPosition, 85063 'controlStyle.prevIcon', 85064 bind$6(this._changeTimeline, this, inverse ? '+' : '-') 85065 ); 85066 makeBtn( 85067 layoutInfo.playPosition, 85068 'controlStyle.' + (playState ? 'stopIcon' : 'playIcon'), 85069 bind$6(this._handlePlayClick, this, !playState), 85070 true 85071 ); 85072 85073 function makeBtn(position, iconPath, onclick, willRotate) { 85074 if (!position) { 85075 return; 85076 } 85077 var opt = { 85078 position: position, 85079 origin: [controlSize / 2, 0], 85080 rotation: willRotate ? -rotation : 0, 85081 rectHover: true, 85082 style: itemStyle, 85083 onclick: onclick 85084 }; 85085 var btn = makeIcon(timelineModel, iconPath, rect, opt); 85086 group.add(btn); 85087 setHoverStyle(btn, hoverStyle); 85088 } 85089 }, 85090 85091 _renderCurrentPointer: function (layoutInfo, group, axis, timelineModel) { 85092 var data = timelineModel.getData(); 85093 var currentIndex = timelineModel.getCurrentIndex(); 85094 var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle'); 85095 var me = this; 85096 85097 var callback = { 85098 onCreate: function (pointer) { 85099 pointer.draggable = true; 85100 pointer.drift = bind$6(me._handlePointerDrag, me); 85101 pointer.ondragend = bind$6(me._handlePointerDragend, me); 85102 pointerMoveTo(pointer, currentIndex, axis, timelineModel, true); 85103 }, 85104 onUpdate: function (pointer) { 85105 pointerMoveTo(pointer, currentIndex, axis, timelineModel); 85106 } 85107 }; 85108 85109 // Reuse when exists, for animation and drag. 85110 this._currentPointer = giveSymbol( 85111 pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback 85112 ); 85113 }, 85114 85115 _handlePlayClick: function (nextState) { 85116 this._clearTimer(); 85117 this.api.dispatchAction({ 85118 type: 'timelinePlayChange', 85119 playState: nextState, 85120 from: this.uid 85121 }); 85122 }, 85123 85124 _handlePointerDrag: function (dx, dy, e) { 85125 this._clearTimer(); 85126 this._pointerChangeTimeline([e.offsetX, e.offsetY]); 85127 }, 85128 85129 _handlePointerDragend: function (e) { 85130 this._pointerChangeTimeline([e.offsetX, e.offsetY], true); 85131 }, 85132 85133 _pointerChangeTimeline: function (mousePos, trigger) { 85134 var toCoord = this._toAxisCoord(mousePos)[0]; 85135 85136 var axis = this._axis; 85137 var axisExtent = asc(axis.getExtent().slice()); 85138 85139 toCoord > axisExtent[1] && (toCoord = axisExtent[1]); 85140 toCoord < axisExtent[0] && (toCoord = axisExtent[0]); 85141 85142 this._currentPointer.position[0] = toCoord; 85143 this._currentPointer.dirty(); 85144 85145 var targetDataIndex = this._findNearestTick(toCoord); 85146 var timelineModel = this.model; 85147 85148 if (trigger || ( 85149 targetDataIndex !== timelineModel.getCurrentIndex() 85150 && timelineModel.get('realtime') 85151 )) { 85152 this._changeTimeline(targetDataIndex); 85153 } 85154 }, 85155 85156 _doPlayStop: function () { 85157 this._clearTimer(); 85158 85159 if (this.model.getPlayState()) { 85160 this._timer = setTimeout( 85161 bind$6(handleFrame, this), 85162 this.model.get('playInterval') 85163 ); 85164 } 85165 85166 function handleFrame() {
85167 // Do not cache 85168 var timelineModel = this.model; 85169 this._changeTimeline( 85170 timelineModel.getCurrentIndex() 85171 + (timelineModel.get('rewind', true) ? -1 : 1) 85172 ); 85173 } 85174 }, 85175 85176 _toAxisCoord: function (vertex) { 85177 var trans = this._mainGroup.getLocalTransform(); 85178 return applyTransform$1(vertex, trans, true); 85179 }, 85180 85181 _findNearestTick: function (axisCoord) { 85182 var data = this.model.getData(); 85183 var dist = Infinity; 85184 var targetDataIndex; 85185 var axis = this._axis; 85186 85187 data.each(['value'], function (value, dataIndex) { 85188 var coord = axis.dataToCoord(value); 85189 var d = Math.abs(coord - axisCoord); 85190 if (d < dist) { 85191 dist = d; 85192 targetDataIndex = dataIndex; 85193 } 85194 }); 85195 85196 return targetDataIndex; 85197 }, 85198 85199 _clearTimer: function () { 85200 if (this._timer) { 85201 clearTimeout(this._timer); 85202 this._timer = null; 85203 } 85204 }, 85205 85206 _changeTimeline: function (nextIndex) { 85207 var currentIndex = this.model.getCurrentIndex(); 85208 85209 if (nextIndex === '+') { 85210 nextIndex = currentIndex + 1; 85211 } 85212 else if (nextIndex === '-') { 85213 nextIndex = currentIndex - 1; 85214 } 85215 85216 this.api.dispatchAction({ 85217 type: 'timelineChange', 85218 currentIndex: nextIndex, 85219 from: this.uid 85220 }); 85221 } 85222 85223 }); 85224 85225 function getViewRect$4(model, api) { 85226 return getLayoutRect( 85227 model.getBoxLayoutParams(), 85228 { 85229 width: api.getWidth(), 85230 height: api.getHeight() 85231 }, 85232 model.get('padding') 85233 ); 85234 } 85235 85236 function makeIcon(timelineModel, objPath, rect, opts) { 85237 var icon = makePath( 85238 timelineModel.get(objPath).replace(/^path:\/\//, ''), 85239 clone(opts || {}), 85240 new BoundingRect(rect[0], rect[1], rect[2], rect[3]), 85241 'center' 85242 ); 85243 85244 return icon; 85245 } 85246 85247 /** 85248 * Create symbol or update symbol 85249 * opt: basic position and event handlers 85250 */ 85251 function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) { 85252 var color = itemStyleModel.get('color'); 85253 85254 if (!symbol) { 85255 var symbolType = hostModel.get('symbol'); 85256 symbol = createSymbol(symbolType, -1, -1, 2, 2, color); 85257 symbol.setStyle('strokeNoScale', true); 85258 group.add(symbol); 85259 callback && callback.onCreate(symbol); 85260 } 85261 else { 85262 symbol.setColor(color); 85263 group.add(symbol); // Group may be new, also need to add. 85264 callback && callback.onUpdate(symbol); 85265 } 85266 85267 // Style 85268 var itemStyle = itemStyleModel.getItemStyle(['color', 'symbol', 'symbolSize']); 85269 symbol.setStyle(itemStyle); 85270 85271 // Transform and events. 85272 opt = merge({ 85273 rectHover: true, 85274 z2: 100 85275 }, opt, true); 85276 85277 var symbolSize = hostModel.get('symbolSize'); 85278 symbolSize = symbolSize instanceof Array 85279 ? symbolSize.slice() 85280 : [+symbolSize, +symbolSize]; 85281 symbolSize[0] /= 2; 85282 symbolSize[1] /= 2; 85283 opt.scale = symbolSize; 85284 85285 var symbolOffset = hostModel.get('symbolOffset'); 85286 if (symbolOffset) { 85287 var pos = opt.position = opt.position || [0, 0]; 85288 pos[0] += parsePercent$1(symbolOffset[0], symbolSize[0]); 85289 pos[1] += parsePercent$1(symbolOffset[1], symbolSize[1]); 85290 } 85291 85292 var symbolRotate = hostModel.get('symbolRotate'); 85293 opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0; 85294 85295 symbol.attr(opt); 85296 85297 // FIXME 85298 // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed, 85299 // getBoundingRect will return wrong result.
85300 // (This is supposed to be resolved in zrender, but it is a little difficult to 85301 // leverage performance and auto updateTransform) 85302 // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol. 85303 symbol.updateTransform(); 85304 85305 return symbol; 85306 } 85307 85308 function pointerMoveTo(pointer, dataIndex, axis, timelineModel, noAnimation) { 85309 if (pointer.dragging) { 85310 return; 85311 } 85312 85313 var pointerModel = timelineModel.getModel('checkpointStyle'); 85314 var toCoord = axis.dataToCoord(timelineModel.getData().get(['value'], dataIndex)); 85315 85316 if (noAnimation || !pointerModel.get('animation', true)) { 85317 pointer.attr({position: [toCoord, 0]}); 85318 } 85319 else { 85320 pointer.stopAnimation(true); 85321 pointer.animateTo( 85322 {position: [toCoord, 0]}, 85323 pointerModel.get('animationDuration', true), 85324 pointerModel.get('animationEasing', true) 85325 ); 85326 } 85327 } 85328 85329 /* 85330* Licensed to the Apache Software Foundation (ASF) under one 85331* or more contributor license agreements. See the NOTICE file 85332* distributed with this work for additional information 85333* regarding copyright ownership. The ASF licenses this file 85334* to you under the Apache License, Version 2.0 (the 85335* "License"); you may not use this file except in compliance 85336* with the License. You may obtain a copy of the License at 85337* 85338* http://www.apache.org/licenses/LICENSE-2.0 85339* 85340* Unless required by applicable law or agreed to in writing, 85341* software distributed under the License is distributed on an 85342* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 85343* KIND, either express or implied. See the License for the 85344* specific language governing permissions and limitations 85345* under the License. 85346*/ 85347 85348 /** 85349 * DataZoom component entry 85350 */ 85351 85352 registerPreprocessor(preprocessor$3); 85353 85354 /* 85355* Licensed to the Apache Software Foundation (ASF) under one 85356* or more contributor license agreements. See the NOTICE file 85357* distributed with this work for additional information 85358* regarding copyright ownership. The ASF licenses this file 85359* to you under the Apache License, Version 2.0 (the 85360* "License"); you may not use this file except in compliance 85361* with the License. You may obtain a copy of the License at 85362* 85363* http://www.apache.org/licenses/LICENSE-2.0 85364* 85365* Unless required by applicable law or agreed to in writing, 85366* software distributed under the License is distributed on an 85367* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 85368* KIND, either express or implied. See the License for the 85369* specific language governing permissions and limitations 85370* under the License. 85371*/ 85372 85373 var ToolboxModel = extendComponentModel({ 85374 85375 type: 'toolbox', 85376 85377 layoutMode: { 85378 type: 'box', 85379 ignoreSize: true 85380 }, 85381 85382 optionUpdated: function () { 85383 ToolboxModel.superApply(this, 'optionUpdated', arguments); 85384 85385 each$1(this.option.feature, function (featureOpt, featureName) { 85386 var Feature = get$1(featureName); 85387 Feature && merge(featureOpt, Feature.defaultOption); 85388 }); 85389 }, 85390 85391 defaultOption: { 85392 85393 show: true, 85394 85395 z: 6, 85396 85397 zlevel: 0, 85398 85399 orient: 'horizontal', 85400 85401 left: 'right', 85402 85403 top: 'top', 85404 85405 // right 85406 // bottom 85407 85408 backgroundColor: 'transparent', 85409 85410 borderColor: '#ccc', 85411 85412 borderRadius: 0, 85413 85414 borderWidth: 0, 85415 85416 padding: 5, 85417 85418 itemSize: 15, 85419 85420 itemGap: 8, 85421 85422 showTitle: true, 85423 85424 iconStyle: { 85425 borderColor: '#666', 85426 color: 'none' 85427 }, 85428 emphasis: { 85429 iconStyle: { 85430 borderColor: '#3E98C5' 85431 } 85432 } 85433 // textStyle: {}, 85434 85435 // feature 85436 } 85437 });
85438 85439 /* 85440* Licensed to the Apache Software Foundation (ASF) under one 85441* or more contributor license agreements. See the NOTICE file 85442* distributed with this work for additional information 85443* regarding copyright ownership. The ASF licenses this file 85444* to you under the Apache License, Version 2.0 (the 85445* "License"); you may not use this file except in compliance 85446* with the License. You may obtain a copy of the License at 85447* 85448* http://www.apache.org/licenses/LICENSE-2.0 85449* 85450* Unless required by applicable law or agreed to in writing, 85451* software distributed under the License is distributed on an 85452* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 85453* KIND, either express or implied. See the License for the 85454* specific language governing permissions and limitations 85455* under the License. 85456*/ 85457 85458 extendComponentView({ 85459 85460 type: 'toolbox', 85461 85462 render: function (toolboxModel, ecModel, api, payload) { 85463 var group = this.group; 85464 group.removeAll(); 85465 85466 if (!toolboxModel.get('show')) { 85467 return; 85468 } 85469 85470 var itemSize = +toolboxModel.get('itemSize'); 85471 var featureOpts = toolboxModel.get('feature') || {}; 85472 var features = this._features || (this._features = {}); 85473 85474 var featureNames = []; 85475 each$1(featureOpts, function (opt, name) { 85476 featureNames.push(name); 85477 }); 85478 85479 (new DataDiffer(this._featureNames || [], featureNames)) 85480 .add(processFeature) 85481 .update(processFeature) 85482 .remove(curry(processFeature, null)) 85483 .execute(); 85484 85485 // Keep for diff. 85486 this._featureNames = featureNames; 85487 85488 function processFeature(newIndex, oldIndex) { 85489 var featureName = featureNames[newIndex]; 85490 var oldName = featureNames[oldIndex]; 85491 var featureOpt = featureOpts[featureName]; 85492 var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel); 85493 var feature; 85494 85495 if (featureName && !oldName) { // Create 85496 if (isUserFeatureName(featureName)) { 85497 feature = { 85498 model: featureModel, 85499 onclick: featureModel.option.onclick, 85500 featureName: featureName 85501 }; 85502 } 85503 else { 85504 var Feature = get$1(featureName); 85505 if (!Feature) { 85506 return; 85507 } 85508 feature = new Feature(featureModel, ecModel, api); 85509 } 85510 features[featureName] = feature; 85511 } 85512 else { 85513 feature = features[oldName]; 85514 // If feature does not exsit. 85515 if (!feature) { 85516 return; 85517 } 85518 feature.model = featureModel; 85519 feature.ecModel = ecModel; 85520 feature.api = api; 85521 } 85522 85523 if (!featureName && oldName) { 85524 feature.dispose && feature.dispose(ecModel, api); 85525 return; 85526 } 85527 85528 if (!featureModel.get('show') || feature.unusable) { 85529 feature.remove && feature.remove(ecModel, api); 85530 return; 85531 } 85532 85533 createIconPaths(featureModel, feature, featureName); 85534 85535 featureModel.setIconStatus = function (iconName, status) { 85536 var option = this.option; 85537 var iconPaths = this.iconPaths; 85538 option.iconStatus = option.iconStatus || {}; 85539 option.iconStatus[iconName] = status; 85540 // FIXME 85541 iconPaths[iconName] && iconPaths[iconName].trigger(status); 85542 }; 85543 85544 if (feature.render) { 85545 feature.render(featureModel, ecModel, api, payload); 85546 } 85547 } 85548 85549 function createIconPaths(featureModel, feature, featureName) { 85550 var iconStyleModel = featureModel.getModel('iconStyle'); 85551 var iconStyleEmphasisModel = featureModel.getModel('emphasis.iconStyle'); 85552 85553 // If one feature has mutiple icon. they are orginaized as 85554 // { 85555 // icon: { 85556 // foo: '', 85557 // bar: '' 85558 // }, 85559 // title: { 85560 // foo: '', 85561 // bar: '' 85562 // } 85563 // } 85564 var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon'); 85565 var titles = featureModel.get('title') || {}; 85566 if (typeof icons === 'string') { 85567 var icon = icons; 85568 var title = titles;
85569 icons = {}; 85570 titles = {}; 85571 icons[featureName] = icon; 85572 titles[featureName] = title; 85573 } 85574 var iconPaths = featureModel.iconPaths = {}; 85575 each$1(icons, function (iconStr, iconName) { 85576 var path = createIcon( 85577 iconStr, 85578 {}, 85579 { 85580 x: -itemSize / 2, 85581 y: -itemSize / 2, 85582 width: itemSize, 85583 height: itemSize 85584 } 85585 ); 85586 path.setStyle(iconStyleModel.getItemStyle()); 85587 path.hoverStyle = iconStyleEmphasisModel.getItemStyle(); 85588 85589 setHoverStyle(path); 85590 85591 if (toolboxModel.get('showTitle')) { 85592 path.__title = titles[iconName]; 85593 path.on('mouseover', function () { 85594 // Should not reuse above hoverStyle, which might be modified. 85595 var hoverStyle = iconStyleEmphasisModel.getItemStyle(); 85596 path.setStyle({ 85597 text: titles[iconName], 85598 textPosition: hoverStyle.textPosition || 'bottom', 85599 textFill: hoverStyle.fill || hoverStyle.stroke || '#000', 85600 textAlign: hoverStyle.textAlign || 'center' 85601 }); 85602 }) 85603 .on('mouseout', function () { 85604 path.setStyle({ 85605 textFill: null 85606 }); 85607 }); 85608 } 85609 path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal'); 85610 85611 group.add(path); 85612 path.on('click', bind( 85613 feature.onclick, feature, ecModel, api, iconName 85614 )); 85615 85616 iconPaths[iconName] = path; 85617 }); 85618 } 85619 85620 layout$3(group, toolboxModel, api); 85621 // Render background after group is layout 85622 // FIXME 85623 group.add(makeBackground(group.getBoundingRect(), toolboxModel)); 85624 85625 // Adjust icon title positions to avoid them out of screen 85626 group.eachChild(function (icon) { 85627 var titleText = icon.__title; 85628 var hoverStyle = icon.hoverStyle; 85629 // May be background element 85630 if (hoverStyle && titleText) { 85631 var rect = getBoundingRect( 85632 titleText, makeFont(hoverStyle) 85633 ); 85634 var offsetX = icon.position[0] + group.position[0]; 85635 var offsetY = icon.position[1] + group.position[1] + itemSize; 85636 85637 var needPutOnTop = false; 85638 if (offsetY + rect.height > api.getHeight()) { 85639 hoverStyle.textPosition = 'top'; 85640 needPutOnTop = true; 85641 } 85642 var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8); 85643 if (offsetX + rect.width / 2 > api.getWidth()) { 85644 hoverStyle.textPosition = ['100%', topOffset]; 85645 hoverStyle.textAlign = 'right'; 85646 } 85647 else if (offsetX - rect.width / 2 < 0) { 85648 hoverStyle.textPosition = [0, topOffset]; 85649 hoverStyle.textAlign = 'left'; 85650 } 85651 } 85652 }); 85653 }, 85654 85655 updateView: function (toolboxModel, ecModel, api, payload) { 85656 each$1(this._features, function (feature) { 85657 feature.updateView && feature.updateView(feature.model, ecModel, api, payload); 85658 }); 85659 }, 85660 85661 // updateLayout: function (toolboxModel, ecModel, api, payload) { 85662 // zrUtil.each(this._features, function (feature) { 85663 // feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload); 85664 // }); 85665 // }, 85666 85667 remove: function (ecModel, api) { 85668 each$1(this._features, function (feature) { 85669 feature.remove && feature.remove(ecModel, api); 85670 }); 85671 this.group.removeAll(); 85672 }, 85673 85674 dispose: function (ecModel, api) { 85675 each$1(this._features, function (feature) { 85676 feature.dispose && feature.dispose(ecModel, api); 85677 }); 85678 } 85679 });
85680 85681 function isUserFeatureName(featureName) { 85682 return featureName.indexOf('my') === 0; 85683 } 85684 85685 /* 85686* Licensed to the Apache Software Foundation (ASF) under one 85687* or more contributor license agreements. See the NOTICE file 85688* distributed with this work for additional information 85689* regarding copyright ownership. The ASF licenses this file 85690* to you under the Apache License, Version 2.0 (the 85691* "License"); you may not use this file except in compliance 85692* with the License. You may obtain a copy of the License at 85693* 85694* http://www.apache.org/licenses/LICENSE-2.0 85695* 85696* Unless required by applicable law or agreed to in writing, 85697* software distributed under the License is distributed on an 85698* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 85699* KIND, either express or implied. See the License for the 85700* specific language governing permissions and limitations 85701* under the License. 85702*/ 85703 85704 var saveAsImageLang = lang.toolbox.saveAsImage; 85705 85706 function SaveAsImage(model) { 85707 this.model = model; 85708 } 85709 85710 SaveAsImage.defaultOption = { 85711 show: true, 85712 icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0', 85713 title: saveAsImageLang.title, 85714 type: 'png', 85715 // Default use option.backgroundColor 85716 // backgroundColor: '#fff', 85717 name: '', 85718 excludeComponents: ['toolbox'], 85719 pixelRatio: 1, 85720 lang: saveAsImageLang.lang.slice() 85721 }; 85722 85723 SaveAsImage.prototype.unusable = !env$1.canvasSupported; 85724 85725 var proto$4 = SaveAsImage.prototype; 85726
85727 proto$4.onclick = function (ecModel, api) { 85728 var model = this.model; 85729 var title = model.get('name') || ecModel.get('title.0.text') || 'echarts'; 85730 var $a = document.createElement('a'); 85731 var type = model.get('type', true) || 'png'; 85732 $a.download = title + '.' + type; 85733 $a.target = '_blank'; 85734 var url = api.getConnectedDataURL({ 85735 type: type, 85736 backgroundColor: model.get('backgroundColor', true) 85737 || ecModel.get('backgroundColor') || '#fff', 85738 excludeComponents: model.get('excludeComponents'), 85739 pixelRatio: model.get('pixelRatio') 85740 }); 85741 $a.href = url; 85742 // Chrome and Firefox 85743 if (typeof MouseEvent === 'function' && !env$1.browser.ie && !env$1.browser.edge) { 85744 var evt = new MouseEvent('click', { 85745 view: window, 85746 bubbles: true, 85747 cancelable: false 85748 }); 85749 $a.dispatchEvent(evt); 85750 } 85751 // IE 85752 else { 85753 if (window.navigator.msSaveOrOpenBlob) { 85754 var bstr = atob(url.split(',')[1]); 85755 var n = bstr.length; 85756 var u8arr = new Uint8Array(n); 85757 while(n--) { 85758 u8arr[n] = bstr.charCodeAt(n); 85759 } 85760 var blob = new Blob([u8arr]); 85761 window.navigator.msSaveOrOpenBlob(blob, title + '.' + type); 85762 } 85763 else { 85764 var lang$$1 = model.get('lang'); 85765 var html = '' + 85766 '<body style="margin:0;">' + 85767 '<img src="' + url + '" style="max-width:100%;" title="' + ((lang$$1 && lang$$1[0]) || '') + '" />' + 85768 '</body>'; 85769 var tab = window.open(); 85770 tab.document.write(html); 85771 } 85772 } 85773 }; 85774 85775 register$1( 85776 'saveAsImage', SaveAsImage 85777 ); 85778 85779 /* 85780* Licensed to the Apache Software Foundation (ASF) under one 85781* or more contributor license agreements. See the NOTICE file 85782* distributed with this work for additional information 85783* regarding copyright ownership. The ASF licenses this file 85784* to you under the Apache License, Version 2.0 (the 85785* "License"); you may not use this file except in compliance 85786* with the License. You may obtain a copy of the License at 85787* 85788* http://www.apache.org/licenses/LICENSE-2.0 85789* 85790* Unless required by applicable law or agreed to in writing, 85791* software distributed under the License is distributed on an 85792* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 85793* KIND, either express or implied. See the License for the 85794* specific language governing permissions and limitations 85795* under the License. 85796*/ 85797 85798 var magicTypeLang = lang.toolbox.magicType; 85799 85800 function MagicType(model) { 85801 this.model = model; 85802 } 85803 85804 MagicType.defaultOption = { 85805 show: true, 85806 type: [], 85807 // Icon group 85808 icon: { 85809 line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4', 85810 bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7', 85811 stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z', // jshint ignore:line 85812 tiled: 'M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z' 85813 }, 85814 // `line`, `bar`, `stack`, `tiled` 85815 title: clone(magicTypeLang.title), 85816 option: {}, 85817 seriesIndex: {} 85818 }; 85819 85820 var proto$5 = MagicType.prototype; 85821 85822 proto$5.getIcons = function () { 85823 var model = this.model; 85824 var availableIcons = model.get('icon'); 85825 var icons = {}; 85826 each$1(model.get('type'), function (type) { 85827 if (availableIcons[type]) { 85828 icons[type] = availableIcons[type]; 85829 } 85830 }); 85831 return icons; 85832 }; 85833 85834 var seriesOptGenreator = { 85835 'line': function (seriesType, seriesId, seriesModel, model) { 85836 if (seriesType === 'bar') { 85837 return merge({ 85838 id: seriesId, 85839 type: 'line', 85840 // Preserve data related option 85841 data: seriesModel.get('data'), 85842 stack: seriesModel.get('stack'), 85843 markPoint: seriesModel.get('markPoint'), 85844 markLine: seriesModel.get('markLine') 85845 }, model.get('option.line') || {}, true); 85846 } 85847 }, 85848 'bar': function (seriesType, seriesId, seriesModel, model) { 85849 if (seriesType === 'line') { 85850 return merge({ 85851 id: seriesId, 85852 type: 'bar', 85853 // Preserve data related option 85854 data: seriesModel.get('data'), 85855 stack: seriesModel.get('stack'), 85856 markPoint: seriesModel.get('markPoint'), 85857 markLine: seriesModel.get('markLine') 85858 }, model.get('option.bar') || {}, true); 85859 } 85860 }, 85861 'stack': function (seriesType, seriesId, seriesModel, model) { 85862 if (seriesType === 'line' || seriesType === 'bar') { 85863 return merge({ 85864 id: seriesId, 85865 stack: '__ec_magicType_stack__' 85866 }, model.get('option.stack') || {}, true); 85867 } 85868 }, 85869 'tiled': function (seriesType, seriesId, seriesModel, model) { 85870 if (seriesType === 'line' || seriesType === 'bar') { 85871 return merge({ 85872 id: seriesId, 85873 stack: '' 85874 }, model.get('option.tiled') || {}, true); 85875 } 85876 } 85877 }; 85878 85879 var radioTypes = [ 85880 ['line', 'bar'], 85881 ['stack', 'tiled'] 85882 ]; 85883 85884 proto$5.onclick = function (ecModel, api, type) { 85885 var model = this.model; 85886 var seriesIndex = model.get('seriesIndex.' + type); 85887 // Not supported magicType 85888 if (!seriesOptGenreator[type]) { 85889 return; 85890 } 85891 var newOption = { 85892 series: [] 85893 }; 85894 var generateNewSeriesTypes = function (seriesModel) { 85895 var seriesType = seriesModel.subType; 85896 var seriesId = seriesModel.id; 85897 var newSeriesOpt = seriesOptGenreator[type]( 85898 seriesType, seriesId, seriesModel, model 85899 ); 85900 if (newSeriesOpt) { 85901 // PENDING If merge original option? 85902 defaults(newSeriesOpt, seriesModel.option); 85903 newOption.series.push(newSeriesOpt); 85904 } 85905 // Modify boundaryGap 85906 var coordSys = seriesModel.coordinateSystem; 85907 if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) { 85908 var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; 85909 if (categoryAxis) { 85910 var axisDim = categoryAxis.dim; 85911 var axisType = axisDim + 'Axis'; 85912 var axisModel = ecModel.queryComponents({ 85913 mainType: axisType, 85914 index: seriesModel.get(name + 'Index'), 85915 id: seriesModel.get(name + 'Id') 85916 })[0]; 85917 var axisIndex = axisModel.componentIndex; 85918 85919 newOption[axisType] = newOption[axisType] || []; 85920 for (var i = 0; i <= axisIndex; i++) { 85921 newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {}; 85922 } 85923 newOption[axisType][axisIndex].boundaryGap = type === 'bar' ? true : false; 85924 } 85925 } 85926 }; 85927 85928 each$1(radioTypes, function (radio) { 85929 if (indexOf(radio, type) >= 0) { 85930 each$1(radio, function (item) { 85931 model.setIconStatus(item, 'normal'); 85932 }); 85933 } 85934 }); 85935 85936 model.setIconStatus(type, 'emphasis'); 85937 85938 ecModel.eachComponent( 85939 {
85940 mainType: 'series', 85941 query: seriesIndex == null ? null : { 85942 seriesIndex: seriesIndex 85943 } 85944 }, generateNewSeriesTypes 85945 ); 85946 api.dispatchAction({ 85947 type: 'changeMagicType', 85948 currentType: type, 85949 newOption: newOption 85950 }); 85951 }; 85952 85953 registerAction({ 85954 type: 'changeMagicType', 85955 event: 'magicTypeChanged', 85956 update: 'prepareAndUpdate' 85957 }, function (payload, ecModel) { 85958 ecModel.mergeOption(payload.newOption); 85959 }); 85960 85961 register$1('magicType', MagicType); 85962 85963 /* 85964* Licensed to the Apache Software Foundation (ASF) under one 85965* or more contributor license agreements. See the NOTICE file 85966* distributed with this work for additional information 85967* regarding copyright ownership. The ASF licenses this file 85968* to you under the Apache License, Version 2.0 (the 85969* "License"); you may not use this file except in compliance 85970* with the License. You may obtain a copy of the License at 85971* 85972* http://www.apache.org/licenses/LICENSE-2.0 85973* 85974* Unless required by applicable law or agreed to in writing, 85975* software distributed under the License is distributed on an 85976* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 85977* KIND, either express or implied. See the License for the 85978* specific language governing permissions and limitations 85979* under the License. 85980*/ 85981 85982 var dataViewLang = lang.toolbox.dataView; 85983 85984 var BLOCK_SPLITER = new Array(60).join('-'); 85985 var ITEM_SPLITER = '\t'; 85986 /** 85987 * Group series into two types 85988 * 1. on category axis, like line, bar 85989 * 2. others, like scatter, pie 85990 * @param {module:echarts/model/Global} ecModel 85991 * @return {Object} 85992 * @inner 85993 */ 85994 function groupSeries(ecModel) { 85995 var seriesGroupByCategoryAxis = {}; 85996 var otherSeries = []; 85997 var meta = []; 85998 ecModel.eachRawSeries(function (seriesModel) { 85999 var coordSys = seriesModel.coordinateSystem; 86000 86001 if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) { 86002 var baseAxis = coordSys.getBaseAxis(); 86003 if (baseAxis.type === 'category') { 86004 var key = baseAxis.dim + '_' + baseAxis.index; 86005 if (!seriesGroupByCategoryAxis[key]) { 86006 seriesGroupByCategoryAxis[key] = { 86007 categoryAxis: baseAxis, 86008 valueAxis: coordSys.getOtherAxis(baseAxis), 86009 series: [] 86010 }; 86011 meta.push({ 86012 axisDim: baseAxis.dim, 86013 axisIndex: baseAxis.index 86014 }); 86015 } 86016 seriesGroupByCategoryAxis[key].series.push(seriesM
86016odel); 86017 } 86018 else { 86019 otherSeries.push(seriesModel); 86020 } 86021 } 86022 else { 86023 otherSeries.push(seriesModel); 86024 } 86025 }); 86026 86027 return { 86028 seriesGroupByCategoryAxis: seriesGroupByCategoryAxis, 86029 other: otherSeries, 86030 meta: meta 86031 }; 86032 } 86033 86034 /** 86035 * Assemble content of series on cateogory axis 86036 * @param {Array.<module:echarts/model/Series>} series 86037 * @return {string} 86038 * @inner 86039 */ 86040 function assembleSeriesWithCategoryAxis(series) { 86041 var tables = []; 86042 each$1(series, function (group, key) { 86043 var categoryAxis = group.categoryAxis; 86044 var valueAxis = group.valueAxis; 86045 var valueAxisDim = valueAxis.dim; 86046 86047 var headers = [' '].concat(map(group.series, function (series) { 86048 return series.name; 86049 })); 86050 var columns = [categoryAxis.model.getCategories()]; 86051 each$1(group.series, function (series) { 86052 columns.push(series.getRawData().mapArray(valueAxisDim, function (val) { 86053 return val; 86054 })); 86055 }); 86056 // Assemble table content 86057 var lines = [headers.join(ITEM_SPLITER)]; 86058 for (var i = 0; i < columns[0].length; i++) { 86059 var items = []; 86060 for (var j = 0; j < columns.length; j++) { 86061 items.push(columns[j][i]); 86062 } 86063 lines.push(items.join(ITEM_SPLITER)); 86064 } 86065 tables.push(lines.join('\n')); 86066 }); 86067 return tables.join('\n\n' + BLOCK_SPLITER + '\n\n'); 86068 } 86069 86070 /** 86071 * Assemble content of other series 86072 * @param {Array.<module:echarts/model/Series>} series 86073 * @return {string} 86074 * @inner 86075 */ 86076 function assembleOtherSeries(series) { 86077 return map(series, function (series) { 86078 var data = series.getRawData(); 86079 var lines = [series.name]; 86080 var vals = []; 86081 data.each(data.dimensions, function () { 86082 var argLen = arguments.length; 86083 var dataIndex = arguments[argLen - 1]; 86084 var name = data.getName(dataIndex); 86085 for (var i = 0; i < argLen - 1; i++) { 86086 vals[i] = arguments[i]; 86087 } 86088 lines.push((name ? (name + ITEM_SPLITER) : '') + vals.join(ITEM_SPLITER)); 86089 }); 86090 return lines.join('\n'); 86091 }).join('\n\n' + BLOCK_SPLITER + '\n\n'); 86092 } 86093 86094 /** 86095 * @param {module:echarts/model/Global} 86096 * @return {Object} 86097 * @inner 86098 */ 86099 function getContentFromModel(ecModel) { 86100 86101 var result = groupSeries(ecModel); 86102 86103 return { 86104 value: filter([ 86105 assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), 86106 assembleOtherSeries(result.other) 86107 ], function (str) { 86108 return str.replace(/[\n\t\s]/g, ''); 86109 }).join('\n\n' + BLOCK_SPLITER + '\n\n'), 86110 86111 meta: result.meta 86112 }; 86113 } 86114 86115 86116 function trim$1(str) { 86117 return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 86118 } 86119 /** 86120 * If a block is tsv format 86121 */ 86122 function isTSVFormat(block) { 86123 // Simple method to find out if a block is tsv format 86124 var firstLine = block.slice(0, block.indexOf('\n')); 86125 if (firstLine.indexOf(ITEM_SPLITER) >= 0) { 86126 return true; 86127 } 86128 } 86129 86130 var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g'); 86131 /** 86132 * @param {string} tsv 86133 * @return {Object} 86134 */ 86135 function parseTSVContents(tsv) { 86136 var tsvLines = tsv.split(/\n+/g); 86137 var headers = trim$1(tsvLines.shift()).split(itemSplitRegex); 86138 86139 var categories = []; 86140 var series = map(headers, function (header) { 86141 return { 86142 name: header, 86143 data: [] 86144 }; 86145 }); 86146 for (var i = 0; i < tsvLines.length; i++) { 86147 var items = trim$1(tsvLines[i]).split(itemSplitRegex); 86148 categories.push(items.shift()); 86149 for (var j = 0; j < items.length; j++) { 86150 series[j] && (series[j].data[i] = items[j]); 86151 } 86152 } 86153 return { 86154 series: series, 86155 categories: categories 86156 }; 86157 } 86158 86159 /** 86160 * @param {string} str 86161 * @return {Array.<Object>} 86162 * @inner 86163 */ 86164 function parseListContents(str) { 86165 var lines = str.split(/\n+/g); 86166 var seriesName = trim$1(lines.shift()); 86167 86168 var data = []; 86169 for (var i = 0; i < lines.length; i++) { 86170 var items = trim$1(lines[i]).split(itemSplitRegex); 86171 var name = ''; 86172 var value; 86173 var hasName = false; 86174 if (isNaN(items[0])) { // First item is name 86175 hasName = true; 86176 name = items[0]; 86177 items = items.slice(1); 86178 data[i] = { 86179 name: name, 86180 value: [] 86181 }; 86182 value = data[i].value; 86183 } 86184 else { 86185 value = data[i] = []; 86186 } 86187 for (var j = 0; j < items.length; j++) { 86188 value.push(+items[j]); 86189 } 86190 if (value.length === 1) { 86191 hasName ? (data[i].value = value[0]) : (data[i] = value[0]); 86192 } 86193 } 86194 86195 return { 86196 name: seriesName, 86197 data: data 86198 }; 86199 } 86200 86201 /** 86202 * @param {string} str 86203 * @param {Array.<Object>} blockMetaList 86204 * @return {Object} 86205 * @inner 86206 */ 86207 function parseContents(str, blockMetaList) { 86208 var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g')); 86209 var newOption = { 86210 series: [] 86211 }; 86212 each$1(blocks, function (block, idx) { 86213 if (isTSVFormat(block)) { 86214 var result = parseTSVContents(block); 86215 var blockMeta = blockMetaList[idx]; 86216 var axisKey = blockMeta.axisDim + 'Axis'; 86217 86218 if (blockMeta) { 86219 newOption[axisKey] = newOption[axisKey] || []; 86220 newOption[axisKey][blockMeta.axisIndex] = { 86221 data: result.categories 86222 }; 86223 newOption.series = newOption.series.concat(result.series); 86224 } 86225 } 86226 else { 86227 var result = parseListContents(block); 86228 newOption.series.push(result); 86229 } 86230 }); 86231 return newOption; 86232 } 86233 86234 /** 86235 * @alias {module:echarts/component/toolbox/feature/DataView} 86236 * @constructor 86237 * @param {module:echarts/model/Model} model 86238 */ 86239 function DataView(model) { 86240 86241 this._dom = null; 86242 86243 this.model = model; 86244 } 86245 86246 DataView.defaultOption = { 86247 show: true, 86248 readOnly: false, 86249 optionToContent: null, 86250 contentToOption: null, 86251 86252 icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28', 86253 title: clone(dataViewLang.title), 86254 lang: clone(dataViewLang.lang), 86255 backgroundColor: '#fff', 86256 textColor: '#000', 86257 textareaColor: '#fff', 86258 textareaBorderColor: '#333',
86259 buttonColor: '#c23531', 86260 buttonTextColor: '#fff' 86261 }; 86262 86263 DataView.prototype.onclick = function (ecModel, api) { 86264 var container = api.getDom(); 86265 var model = this.model; 86266 if (this._dom) { 86267 container.removeChild(this._dom); 86268 } 86269 var root = document.createElement('div'); 86270 root.style.cssText = 'position:absolute;left:5px;top:5px;bottom:5px;right:5px;'; 86271 root.style.backgroundColor = model.get('backgroundColor') || '#fff'; 86272 86273 // Create elements 86274 var header = document.createElement('h4'); 86275 var lang$$1 = model.get('lang') || []; 86276 header.innerHTML = lang$$1[0] || model.get('title'); 86277 header.style.cssText = 'margin: 10px 20px;'; 86278 header.style.color = model.get('textColor'); 86279 86280 var viewMain = document.createElement('div'); 86281 var textarea = document.createElement('textarea'); 86282 viewMain.style.cssText = 'display:block;width:100%;overflow:auto;'; 86283 86284 var optionToContent = model.get('optionToContent'); 86285 var contentToOption = model.get('contentToOption'); 86286 var result = getContentFromModel(ecModel); 86287 if (typeof optionToContent === 'function') { 86288 var htmlOrDom = optionToContent(api.getOption()); 86289 if (typeof htmlOrDom === 'string') { 86290 viewMain.innerHTML = htmlOrDom; 86291 } 86292 else if (isDom(htmlOrDom)) { 86293 viewMain.appendChild(htmlOrDom); 86294 } 86295 } 86296 else { 86297 // Use default textarea 86298 viewMain.appendChild(textarea); 86299 textarea.readOnly = model.get('readOnly'); 86300 textarea.style.cssText = 'width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;'; 86301 textarea.style.color = model.get('textColor'); 86302 textarea.style.borderColor = model.get('textareaBorderColor'); 86303 textarea.style.backgroundColor = model.get('textareaColor'); 86304 textarea.value = result.value; 86305 } 86306 86307 var blockMetaList = result.meta; 86308 86309 var buttonContainer = document.createElement('div'); 86310 buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;'; 86311 86312 var buttonStyle = 'float:right;margin-right:20px;border:none;' 86313 + 'cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px'; 86314 var closeButton = document.createElement('div'); 86315 var refreshButton = document.createElement('div'); 86316 86317 buttonStyle += ';background-color:' + model.get('buttonColor'); 86318 buttonStyle += ';color:' + model.get('buttonTextColor'); 86319 86320 var self = this; 86321 86322 function close() { 86323 container.removeChild(root); 86324 self._dom = null; 86325 } 86326 addEventListener(closeButton, 'click', close); 86327 86328 addEventListener(refreshButton, 'click', function () { 86329 var newOption; 86330 try { 86331 if (typeof contentToOption === 'function') { 86332 newOption = contentToOption(viewMain, api.getOption()); 86333 } 86334 else { 86335 newOption = parseContents(textarea.value, blockMetaList); 86336 } 86337 } 86338 catch (e) { 86339 close(); 86340 throw new Error('Data view format error ' + e); 86341 } 86342 if (newOption) { 86343 api.dispatchAction({ 86344 type: 'changeDataView', 86345 newOption: newOption 86346 }); 86347 } 86348 86349 close(); 86350 }); 86351 86352 closeButton.innerHTML = lang$$1[1]; 86353 refreshButton.innerHTML = lang$$1[2]; 86354 refreshButton.style.cssText = buttonStyle; 86355 closeButton.style.cssText = buttonStyle; 86356 86357 !model.get('readOnly') && buttonContainer.appendChild(refreshButton); 86358 buttonContainer.appendChild(closeButton); 86359 86360 // http://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea 86361 addEventListener(textarea, 'keydown', function (e) { 86362 if ((e.keyCode || e.which) === 9) { 86363 // get caret position/selection 86364 var val = this.value; 86365 var start = this.selectionStart; 86366 var end = this.selectionEnd; 86367 86368 // set textarea value to: text before caret + tab + text after caret 86369 this.value = val.substring(0, start) + ITEM_SPLITER + val.substring(end); 86370 86371 // put caret at right position again 86372 this.selectionStart = this.selectionEnd = start + 1; 86373 86374 // prevent the focus lose 86375 stop(e); 86376 } 86377 }); 86378 86379 root.appendChild(header); 86380 root.appendChild(viewMain); 86381 root.appendChild(buttonContainer); 86382 86383 viewMain.style.height = (container.clientHeight - 80) + 'px'; 86384 86385 container.appendChild(root); 86386 this._dom = root; 86387 }; 86388 86389 DataView.prototype.remove = function (ecModel, api) { 86390 this._dom && api.getDom().removeChild(this._dom); 86391 }; 86392 86393 DataView.prototype.dispose = function (ecModel, api) { 86394 this.remove(ecModel, api); 86395 }; 86396 86397 /** 86398 * @inner 86399 */ 86400 function tryMergeDataOption(newData, originalData) { 86401 return map(newData, function (newVal, idx) { 86402 var original = originalData && originalData[idx]; 86403 if (isObject$1(original) && !isArray(original)) { 86404 if (isObject$1(newVal) && !isArray(newVal)) { 86405 newVal = newVal.value; 86406 } 86407 // Original data has option 86408 return defaults({ 86409 value: newVal 86410 }, original); 86411 } 86412 else { 86413 return newVal; 86414 } 86415 }); 86416 } 86417 86418 register$1('dataView', DataView); 86419 86420 registerAction({ 86421 type: 'changeDataView', 86422 event: 'dataViewChanged', 86423 update: 'prepareAndUpdate' 86424 }, function (payload, ecModel) { 86425 var newSeriesOptList = []; 86426 each$1(payload.newOption.series, function (seriesOpt) { 86427 var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];
vendor: 4,168 bytes, lines 86428-86553
86428 if (!seriesModel) { 86429 // New created series 86430 // Geuss the series type 86431 newSeriesOptList.push(extend({ 86432 // Default is scatter 86433 type: 'scatter' 86434 }, seriesOpt)); 86435 } 86436 else { 86437 var originalData = seriesModel.get('data'); 86438 newSeriesOptList.push({ 86439 name: seriesOpt.name, 86440 data: tryMergeDataOption(seriesOpt.data, originalData) 86441 }); 86442 } 86443 }); 86444 86445 ecModel.mergeOption(defaults({ 86446 series: newSeriesOptList 86447 }, payload.newOption)); 86448 }); 86449 86450 /* 86451* Licensed to the Apache Software Foundation (ASF) under one 86452* or more contributor license agreements. See the NOTICE file 86453* distributed with this work for additional information 86454* regarding copyright ownership. The ASF licenses this file 86455* to you under the Apache License, Version 2.0 (the 86456* "License"); you may not use this file except in compliance 86457* with the License. You may obtain a copy of the License at 86458* 86459* http://www.apache.org/licenses/LICENSE-2.0 86460* 86461* Unless required by applicable law or agreed to in writing, 86462* software distributed under the License is distributed on an 86463* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 86464* KIND, either express or implied. See the License for the 86465* specific language governing permissions and limitations 86466* under the License. 86467*/ 86468 86469 var each$29 = each$1; 86470 86471 var ATTR$2 = '\0_ec_hist_store'; 86472 86473 /** 86474 * @param {module:echarts/model/Global} ecModel 86475 * @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]} 86476 */ 86477 function push(ecModel, newSnapshot) { 86478 var store = giveStore$1(ecModel); 86479 86480 // If previous dataZoom can not be found, 86481 // complete an range with current range. 86482 each$29(newSnapshot, function (batchItem, dataZoomId) { 86483 var i = store.length - 1; 86484 for (; i >= 0; i--) { 86485 var snapshot = store[i]; 86486 if (snapshot[dataZoomId]) { 86487 break; 86488 } 86489 } 86490 if (i < 0) { 86491 // No origin range set, create one by current range. 86492 var dataZoomModel = ecModel.queryComponents( 86493 {mainType: 'dataZoom', subType: 'select', id: dataZoomId} 86494 )[0]; 86495 if (dataZoomModel) { 86496 var percentRange = dataZoomModel.getPercentRange(); 86497 store[0][dataZoomId] = { 86498 dataZoomId: dataZoomId, 86499 start: percentRange[0], 86500 end: percentRange[1] 86501 }; 86502 } 86503 } 86504 }); 86505 86506 store.push(newSnapshot); 86507 } 86508 86509 /** 86510 * @param {module:echarts/model/Global} ecModel 86511 * @return {Object} snapshot 86512 */ 86513 function pop(ecModel) { 86514 var store = giveStore$1(ecModel); 86515 var head = store[store.length - 1]; 86516 store.length > 1 && store.pop(); 86517 86518 // Find top for all dataZoom. 86519 var snapshot = {}; 86520 each$29(head, function (batchItem, dataZoomId) { 86521 for (var i = store.length - 1; i >= 0; i--) { 86522 var batchItem = store[i][dataZoomId]; 86523 if (batchItem) { 86524 snapshot[dataZoomId] = batchItem; 86525 break; 86526 } 86527 } 86528 }); 86529 86530 return snapshot; 86531 } 86532 86533 /** 86534 * @param {module:echarts/model/Global} ecModel 86535 */ 86536 function clear$1(ecModel) { 86537 ecModel[ATTR$2] = null; 86538 } 86539 86540 /** 86541 * @param {module:echarts/model/Global} ecModel 86542 * @return {number} records. always >= 1. 86543 */ 86544 function count(ecModel) { 86545 return giveStore$1(ecModel).length; 86546 } 86547 86548 /** 86549 * [{key: dataZoomId, value: {dataZoomId, range}}, ...] 86550 * History length of each dataZoom may be different. 86551 * this._history[0] is used to store origin range. 86552 * @type {Array.<Object>} 86553 */
86554 function giveStore$1(ecModel) { 86555 var store = ecModel[ATTR$2]; 86556 if (!store) { 86557 store = ecModel[ATTR$2] = [{}]; 86558 } 86559 return store; 86560 } 86561 86562 /* 86563* Licensed to the Apache Software Foundation (ASF) under one 86564* or more contributor license agreements. See the NOTICE file 86565* distributed with this work for additional information 86566* regarding copyright ownership. The ASF licenses this file 86567* to you under the Apache License, Version 2.0 (the 86568* "License"); you may not use this file except in compliance 86569* with the License. You may obtain a copy of the License at 86570* 86571* http://www.apache.org/licenses/LICENSE-2.0 86572* 86573* Unless required by applicable law or agreed to in writing, 86574* software distributed under the License is distributed on an 86575* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 86576* KIND, either express or implied. See the License for the 86577* specific language governing permissions and limitations 86578* under the License. 86579*/ 86580 86581 DataZoomModel.extend({ 86582 type: 'dataZoom.select' 86583 }); 86584 86585 /* 86586* Licensed to the Apache Software Foundation (ASF) under one 86587* or more contributor license agreements. See the NOTICE file 86588* distributed with this work for additional information 86589* regarding copyright ownership. The ASF licenses this file 86590* to you under the Apache License, Version 2.0 (the 86591* "License"); you may not use this file except in compliance 86592* with the License. You may obtain a copy of the License at 86593* 86594* http://www.apache.org/licenses/LICENSE-2.0 86595* 86596* Unless required by applicable law or agreed to in writing, 86597* software distributed under the License is distributed on an 86598* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 86599* KIND, either express or implied. See the License for the 86600* specific language governing permissions and limitations 86601* under the License. 86602*/ 86603 86604 DataZoomView.extend({ 86605 type: 'dataZoom.select' 86606 }); 86607 86608 /* 86609* Licensed to the Apache Software Foundation (ASF) under one 86610* or more contributor license agreements. See the NOTICE file 86611* distributed with this work for additional information 86612* regarding copyright ownership. The ASF licenses this file 86613* to you under the Apache License, Version 2.0 (the 86614* "License"); you may not use this file except in compliance 86615* with the License. You may obtain a copy of the License at 86616* 86617* http://www.apache.org/licenses/LICENSE-2.0 86618* 86619* Unless required by applicable law or agreed to in writing, 86620* software distributed under the License is distributed on an 86621* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 86622* KIND, either express or implied. See the License for the 86623* specific language governing permissions and limitations 86624* under the License. 86625*/ 86626 86627 /** 86628 * DataZoom component entry 86629 */ 86630 86631 /* 86632* Licensed to the Apache Software Foundation (ASF) under one 86633* or more contributor license agreements. See the NOTICE file 86634* distributed with this work for additional information 86635* regarding copyright ownership. The ASF licenses this file 86636* to you under the Apache License, Version 2.0 (the 86637* "License"); you may not use this file except in compliance 86638* with the License. You may obtain a copy of the License at 86639* 86640* http://www.apache.org/licenses/LICENSE-2.0 86641* 86642* Unless required by applicable law or agreed to in writing, 86643* software distributed under the License is distributed on an 86644* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 86645* KIND, either express or implied. See the License for the 86646* specific language governing permissions and limitations 86647* under the License. 86648*/ 86649 86650// Use dataZoomSelect 86651 var dataZoomLang = lang.toolbox.dataZoom; 86652 var each$28 = each$1; 86653 86654// Spectial component id start with \0ec\0, see echarts/model/Global.js~hasInnerId 86655 var DATA_ZOOM_ID_BASE = '\0_ec_\0toolbox-dataZoom_'; 86656 86657 function DataZoom(model, ecModel, api) { 86658 86659 /** 86660 * @private 86661 * @type {module:echarts/component/helper/BrushController} 86662 */ 86663 (this._brushController = new BrushController(api.getZr())) 86664 .on('brush', bind(this._onBrush, this)) 86665 .mount(); 86666 86667 /** 86668 * @private 86669 * @type {boolean} 86670 */
86671 this._isZoomActive; 86672 } 86673 86674 DataZoom.defaultOption = { 86675 show: true, 86676 // Icon group 86677 icon: { 86678 zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1', 86679 back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26' 86680 }, 86681 // `zoom`, `back` 86682 title: clone(dataZoomLang.title) 86683 }; 86684 86685 var proto$6 = DataZoom.prototype; 86686 86687 proto$6.render = function (featureModel, ecModel, api, payload) { 86688 this.model = featureModel; 86689 this.ecModel = ecModel; 86690 this.api = api; 86691 86692 updateZoomBtnStatus(featureModel, ecModel, this, payload, api); 86693 updateBackBtnStatus(featureModel, ecModel); 86694 }; 86695 86696 proto$6.onclick = function (ecModel, api, type) { 86697 handlers$1[type].call(this); 86698 }; 86699 86700 proto$6.remove = function (ecModel, api) { 86701 this._brushController.unmount(); 86702 }; 86703 86704 proto$6.dispose = function (ecModel, api) { 86705 this._brushController.dispose(); 86706 }; 86707 86708 /** 86709 * @private 86710 */ 86711 var handlers$1 = { 86712 86713 zoom: function () {
86714 var nextActive = !this._isZoomActive; 86715 86716 this.api.dispatchAction({ 86717 type: 'takeGlobalCursor', 86718 key: 'dataZoomSelect', 86719 dataZoomSelectActive: nextActive 86720 }); 86721 }, 86722 86723 back: function () { 86724 this._dispatchZoomAction(pop(this.ecModel)); 86725 } 86726 }; 86727 86728 /** 86729 * @private 86730 */ 86731 proto$6._onBrush = function (areas, opt) { 86732 if (!opt.isEnd || !areas.length) { 86733 return; 86734 } 86735 var snapshot = {}; 86736 var ecModel = this.ecModel; 86737 86738 this._brushController.updateCovers([]); // remove cover 86739 86740 var brushTargetManager = new BrushTargetManager( 86741 retrieveAxisSetting(this.model.option), ecModel, {include: ['grid']} 86742 ); 86743 brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) { 86744 if (coordSys.type !== 'cartesian2d') { 86745 return; 86746 } 86747 86748 var brushType = area.brushType; 86749 if (brushType === 'rect') { 86750 setBatch('x', coordSys, coordRange[0]); 86751 setBatch('y', coordSys, coordRange[1]); 86752 } 86753 else { 86754 setBatch(({lineX: 'x', lineY: 'y'})[brushType], coordSys, coordRange); 86755 } 86756 }); 86757 86758 push(ecModel, snapshot); 86759 86760 this._dispatchZoomAction(snapshot); 86761 86762 function setBatch(dimName, coordSys, minMax) { 86763 var axis = coordSys.getAxis(dimName); 86764 var axisModel = axis.model; 86765 var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); 86766 86767 // Restrict range. 86768 var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); 86769 if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { 86770 minMax = sliderMove( 86771 0, minMax.slice(), axis.scale.getExtent(), 0, 86772 minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan 86773 ); 86774 } 86775 86776 dataZoomModel && (snapshot[dataZoomModel.id] = { 86777 dataZoomId: dataZoomModel.id, 86778 startValue: minMax[0], 86779 endValue: minMax[1] 86780 }); 86781 } 86782 86783 function findDataZoom(dimName, axisModel, ecModel) { 86784 var found; 86785 ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel) { 86786 var has = dzModel.getAxisModel(dimName, axisModel.componentIndex); 86787 has && (found = dzModel); 86788 }); 86789 return found; 86790 } 86791 }; 86792 86793 /** 86794 * @private 86795 */ 86796 proto$6._dispatchZoomAction = function (snapshot) { 86797 var batch = []; 86798 86799 // Convert from hash map to array. 86800 each$28(snapshot, function (batchItem, dataZoomId) { 86801 batch.push(clone(batchItem)); 86802 }); 86803 86804 batch.length && this.api.dispatchAction({ 86805 type: 'dataZoom', 86806 from: this.uid, 86807 batch: batch 86808 }); 86809 }; 86810 86811 function retrieveAxisSetting(option) { 86812 var setting = {}; 86813 // Compatible with previous setting: null => all axis, false => no axis. 86814 each$1(['xAxisIndex', 'yAxisIndex'], function (name) { 86815 setting[name] = option[name]; 86816 setting[name] == null && (setting[name] = 'all'); 86817 (setting[name] === false || setting[name] === 'none') && (setting[name] = []); 86818 }); 86819 return setting; 86820 } 86821 86822 function updateBackBtnStatus(featureModel, ecModel) { 86823 featureModel.setIconStatus( 86824 'back', 86825 count(ecModel) > 1 ? 'emphasis' : 'normal' 86826 ); 86827 } 86828 86829 function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) { 86830 var zoomActive = view._isZoomActive; 86831 86832 if (payload && payload.type === 'takeGlobalCursor') { 86833 zoomActive = payload.key === 'dataZoomSelect' 86834 ? payload.dataZoomSelectActive : false; 86835 } 86836 86837 view._isZoomActive = zoomActive; 86838 86839 featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal'); 86840 86841 var brushTargetManager = new BrushTargetManager( 86842 retrieveAxisSetting(featureModel.option), ecModel, {include: ['grid']} 86843 ); 86844 86845 view._brushController 86846 .setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo) { 86847 return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared) 86848 ? 'lineX' 86849 : (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared) 86850 ? 'lineY' 86851 : 'rect'; 86852 })) 86853 .enableBrush( 86854 zoomActive 86855 ? { 86856 brushType: 'auto', 86857 brushStyle: { 86858 // FIXME user customized? 86859 lineWidth: 0, 86860 fill: 'rgba(0,0,0,0.2)' 86861 } 86862 } 86863 : false 86864 ); 86865 } 86866 86867 86868 register$1('dataZoom', DataZoom); 86869 86870 86871// Create special dataZoom option for select 86872// FIXME consider the case of merge option, where axes options are not exists. 86873 registerPreprocessor(function (option) { 86874 if (!option) { 86875 return; 86876 } 86877 86878 var dataZoomOpts = option.dataZoom || (option.dataZoom = []); 86879 if (!isArray(dataZoomOpts)) { 86880 option.dataZoom = dataZoomOpts = [dataZoomOpts]; 86881 } 86882 86883 var toolboxOpt = option.toolbox; 86884 if (toolboxOpt) { 86885 // Assume there is only one toolbox 86886 if (isArray(toolboxOpt)) { 86887 toolboxOpt = toolboxOpt[0]; 86888 } 86889 86890 if (toolboxOpt && toolboxOpt.feature) { 86891 var dataZoomOpt = toolboxOpt.feature.dataZoom; 86892 // FIXME: If add dataZoom when setOption in merge mode, 86893 // no axis info to be added. See `test/dataZoom-extreme.html` 86894 addForAxis('xAxis', dataZoomOpt); 86895 addForAxis('yAxis', dataZoomOpt); 86896 } 86897 } 86898 86899 function addForAxis(axisName, dataZoomOpt) { 86900 if (!dataZoomOpt) { 86901 return; 86902 } 86903 86904 // Try not to modify model, because it is not merged yet. 86905 var axisIndicesName = axisName + 'Index'; 86906 var givenAxisIndices = dataZoomOpt[axisIndicesName]; 86907 if (givenAxisIndices != null 86908 && givenAxisIndices != 'all' 86909 && !isArray(givenAxisIndices) 86910 ) { 86911 givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices]; 86912 } 86913
86914 forEachComponent(axisName, function (axisOpt, axisIndex) { 86915 if (givenAxisIndices != null 86916 && givenAxisIndices != 'all' 86917 && indexOf(givenAxisIndices, axisIndex) === -1 86918 ) { 86919 return; 86920 } 86921 var newOpt = { 86922 type: 'select', 86923 $fromToolbox: true, 86924 // Id for merge mapping. 86925 id: DATA_ZOOM_ID_BASE + axisName + axisIndex 86926 }; 86927 // FIXME 86928 // Only support one axis now. 86929 newOpt[axisIndicesName] = axisIndex; 86930 dataZoomOpts.push(newOpt); 86931 }); 86932 } 86933 86934 function forEachComponent(mainType, cb) { 86935 var opts = option[mainType]; 86936 if (!isArray(opts)) { 86937 opts = opts ? [opts] : []; 86938 } 86939 each$28(opts, cb); 86940 } 86941 }); 86942 86943 /* 86944* Licensed to the Apache Software Foundation (ASF) under one 86945* or more contributor license agreements. See the NOTICE file 86946* distributed with this work for additional information 86947* regarding copyright ownership. The ASF licenses this file 86948* to you under the Apache License, Version 2.0 (the 86949* "License"); you may not use this file except in compliance 86950* with the License. You may obtain a copy of the License at 86951* 86952* http://www.apache.org/licenses/LICENSE-2.0 86953* 86954* Unless required by applicable law or agreed to in writing, 86955* software distributed under the License is distributed on an 86956* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 86957* KIND, either express or implied. See the License for the 86958* specific language governing permissions and limitations 86959* under the License. 86960*/ 86961 86962 var restoreLang = lang.toolbox.restore; 86963 86964 function Restore(model) { 86965 this.model = model; 86966 } 86967 86968 Restore.defaultOption = { 86969 show: true, 86970 icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5', 86971 title: restoreLang.title 86972 }; 86973 86974 var proto$7 = Restore.prototype; 86975 86976 proto$7.onclick = function (ecModel, api, type) { 86977 clear$1(ecModel); 86978 86979 api.dispatchAction({ 86980 type: 'restore', 86981 from: this.uid 86982 }); 86983 }; 86984 86985 register$1('restore', Restore); 86986 86987 registerAction( 86988 {type: 'restore', event: 'restore', update: 'prepareAndUpdate'}, 86989 function (payload, ecModel) { 86990 ecModel.resetOption('recreate'); 86991 } 86992 ); 86993 86994 /* 86995* Licensed to the Apache Software Foundation (ASF) under one 86996* or more contributor license agreements. See the NOTICE file 86997* distributed with this work for additional information 86998* regarding copyright ownership. The ASF licenses this file 86999* to you under the Apache License, Version 2.0 (the 87000* "License"); you may not use this file except in compliance 87001* with the License. You may obtain a copy of the License at 87002* 87003* http://www.apache.org/licenses/LICENSE-2.0 87004* 87005* Unless required by applicable law or agreed to in writing, 87006* software distributed under the License is distributed on an 87007* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 87008* KIND, either express or implied. See the License for the 87009* specific language governing permissions and limitations 87010* under the License. 87011*/ 87012 87013 var urn = 'urn:schemas-microsoft-com:vml'; 87014 var win = typeof window === 'undefined' ? null : window; 87015 87016 var vmlInited = false; 87017 87018 var doc = win && win.document; 87019 87020 function createNode(tagName) { 87021 return doCreateNode(tagName); 87022 } 87023 87024// Avoid assign to an exported variable, for transforming to cjs. 87025 var doCreateNode; 87026 87027 if (doc && !env$1.canvasSupported) { 87028 try { 87029 !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn); 87030 doCreateNode = function (tagName) { 87031 return doc.createElement('<zrvml:' + tagName + ' class="zrvml">'); 87032 }; 87033 } 87034 catch (e) { 87035 doCreateNode = function (tagName) { 87036 return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">'); 87037 }; 87038 } 87039 } 87040 87041// From raphael 87042 function initVML() { 87043 if (vmlInited || !doc) { 87044 return; 87045 } 87046 vmlInited = true; 87047 87048 var styleSheets = doc.styleSheets; 87049 if (styleSheets.length < 31) { 87050 doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)'); 87051 } 87052 else {
87053 // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx 87054 styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)'); 87055 } 87056 } 87057 87058// http://www.w3.org/TR/NOTE-VML 87059// TODO Use proxy like svg instead of overwrite brush methods 87060 87061 var CMD$3 = PathProxy.CMD; 87062 var round$3 = Math.round; 87063 var sqrt = Math.sqrt; 87064 var abs$1 = Math.abs; 87065 var cos = Math.cos; 87066 var sin = Math.sin; 87067 var mathMax$8 = Math.max; 87068 87069 if (!env$1.canvasSupported) { 87070 87071 var comma = ','; 87072 var imageTransformPrefix = 'progid:DXImageTransform.Microsoft'; 87073 87074 var Z = 21600; 87075 var Z2 = Z / 2; 87076 87077 var ZLEVEL_BASE = 100000; 87078 var Z_BASE$1 = 1000; 87079 87080 var initRootElStyle = function (el) { 87081 el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;'; 87082 el.coordsize = Z + ',' + Z; 87083 el.coordorigin = '0,0'; 87084 }; 87085 87086 var encodeHtmlAttribute = function (s) { 87087 return String(s).replace(/&/g, '&').replace(/"/g, '"'); 87088 }; 87089 87090 var rgb2Str = function (r, g, b) { 87091 return 'rgb(' + [r, g, b].join(',') + ')'; 87092 }; 87093 87094 var append = function (parent, child) { 87095 if (child && parent && child.parentNode !== parent) { 87096 parent.appendChild(child); 87097 } 87098 }; 87099 87100 var remove = function (parent, child) { 87101 if (child && parent && child.parentNode === parent) { 87102 parent.removeChild(child); 87103 } 87104 }; 87105 87106 var getZIndex = function (zlevel, z, z2) { 87107 // z çšâÃ¥ÂâÃ¥â¬Â¼Ã¨ÅÆÃ¥âºÂ´Ã¤Â¸Âº [0, 1000] 87108 return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE$1 + z2; 87109 }; 87110 87111 var parsePercent$3 = function (value, maxValue) { 87112 if (typeof value === 'string') { 87113 if (value.lastIndexOf('%') >= 0) { 87114 return parseFloat(value) / 100 * maxValue; 87115 } 87116 return parseFloat(value); 87117 } 87118 return value; 87119 }; 87120 87121 /*************************************************** 87122 * PATH 87123 **************************************************/ 87124 87125 var setColorAndOpacity = function (el, color, opacity) { 87126 var colorArr = parse(color); 87127 opacity = +opacity; 87128 if (isNaN(opacity)) { 87129 opacity = 1; 87130 } 87131 if (colorArr) { 87132 el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]); 87133 el.opacity = opacity * colorArr[3]; 87134 } 87135 }; 87136 87137 var getColorAndAlpha = function (color) { 87138 var colorArr = parse(color); 87139 return [ 87140 rgb2Str(colorArr[0], colorArr[1], colorArr[2]), 87141 colorArr[3] 87142 ]; 87143 }; 87144 87145 var updateFillNode = function (el, style, zrEl) { 87146 // TODO pattern 87147 var fill = style.fill; 87148 if (fill != null) { 87149 // Modified from excanvas 87150 if (fill instanceof Gradient) { 87151 var gradientType; 87152 var angle = 0; 87153 var focus = [0, 0]; 87154 // additional offset 87155 var shift = 0; 87156 // scale factor for offset 87157 var expansion = 1; 87158 var rect = zrEl.getBoundingRect(); 87159 var rectWidth = rect.width; 87160 var rectHeight = rect.height; 87161 if (fill.type === 'linear') { 87162 gradientType = 'gradient'; 87163 var transform = zrEl.transform; 87164 var p0 = [fill.x * rectWidth, fill.y * rectHeight]; 87165 var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight]; 87166 if (transform) { 87167 applyTransform(p0, p0, transform); 87168 applyTransform(p1, p1, transform); 87169 } 87170 var dx = p1[0] - p0[0]; 87171 var dy = p1[1] - p0[1]; 87172 angle = Math.atan2(dx, dy) * 180 / Math.PI; 87173 // The angle should be a non-negative number. 87174 if (angle < 0) {
87175 angle += 360; 87176 } 87177 87178 // Very small angles produce an unexpected result because they are 87179 // converted to a scientific notation string. 87180 if (angle < 1e-6) { 87181 angle = 0; 87182 } 87183 } 87184 else { 87185 gradientType = 'gradientradial'; 87186 var p0 = [fill.x * rectWidth, fill.y * rectHeight]; 87187 var transform = zrEl.transform; 87188 var scale$$1 = zrEl.scale; 87189 var width = rectWidth; 87190 var height = rectHeight; 87191 focus = [ 87192 // Percent in bounding rect 87193 (p0[0] - rect.x) / width, 87194 (p0[1] - rect.y) / height 87195 ]; 87196 if (transform) { 87197 applyTransform(p0, p0, transform); 87198 } 87199 87200 width /= scale$$1[0] * Z; 87201 height /= scale$$1[1] * Z; 87202 var dimension = mathMax$8(width, height); 87203 shift = 2 * 0 / dimension; 87204 expansion = 2 * fill.r / dimension - shift; 87205 } 87206 87207 // We need to sort the color stops in ascending order by offset,
87208 // otherwise IE won't interpret it correctly. 87209 var stops = fill.colorStops.slice(); 87210 stops.sort(function(cs1, cs2) { 87211 return cs1.offset - cs2.offset; 87212 }); 87213 87214 var length$$1 = stops.length; 87215 // Color and alpha list of first and last stop 87216 var colorAndAlphaList = []; 87217 var colors = []; 87218 for (var i = 0; i < length$$1; i++) { 87219 var stop = stops[i]; 87220 var colorAndAlpha = getColorAndAlpha(stop.color); 87221 colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]); 87222 if (i === 0 || i === length$$1 - 1) { 87223 colorAndAlphaList.push(colorAndAlpha); 87224 } 87225 } 87226 87227 if (length$$1 >= 2) { 87228 var color1 = colorAndAlphaList[0][0]; 87229 var color2 = colorAndAlphaList[1][0]; 87230 var opacity1 = colorAndAlphaList[0][1] * style.opacity; 87231 var opacity2 = colorAndAlphaList[1][1] * style.opacity; 87232 87233 el.type = gradientType; 87234 el.method = 'none'; 87235 el.focus = '100%'; 87236 el.angle = angle; 87237 el.color = color1; 87238 el.color2 = color2; 87239 el.colors = colors.join(','); 87240 // When colors attribute is used, the meanings of opacity and o:opacity2 87241 // are reversed. 87242 el.opacity = opacity2; 87243 // FIXME g_o_:opacity ? 87244 el.opacity2 = opacity1; 87245 } 87246 if (gradientType === 'radial') { 87247 el.focusposition = focus.join(','); 87248 } 87249 } 87250 else { 87251 // FIXME Change from Gradient fill to color fill 87252 setColorAndOpacity(el, fill, style.opacity); 87253 } 87254 } 87255 }; 87256 87257 var updateStrokeNode = function (el, style) { 87258 // if (style.lineJoin != null) { 87259 // el.joinstyle = style.lineJoin; 87260 // } 87261 // if (style.miterLimit != null) { 87262 // el.miterlimit = style.miterLimit * Z; 87263 // } 87264 // if (style.lineCap != null) { 87265 // el.endcap = style.lineCap; 87266 // } 87267 if (style.lineDash != null) { 87268 el.dashstyle = style.lineDash.join(' '); 87269 } 87270 if (style.stroke != null && !(style.stroke instanceof Gradient)) { 87271 setColorAndOpacity(el, style.stroke, style.opacity); 87272 } 87273 }; 87274 87275 var updateFillAndStroke = function (vmlEl, type, style, zrEl) { 87276 var isFill = type == 'fill'; 87277 var el = vmlEl.getElementsByTagName(type)[0]; 87278 // Stroke must have lineWidth 87279 if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) { 87280 vmlEl[isFill ? 'filled' : 'stroked'] = 'true'; 87281 // FIXME Remove before updating, or set `colors` will throw error 87282 if (style[type] instanceof Gradient) { 87283 remove(vmlEl, el); 87284 } 87285 if (!el) { 87286 el = createNode(type); 87287 } 87288 87289 isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style); 87290 append(vmlEl, el); 87291 } 87292 else { 87293 vmlEl[isFill ? 'filled' : 'stroked'] = 'false'; 87294 remove(vmlEl, el); 87295 } 87296 }; 87297 87298 var points$3 = [[], [], []]; 87299 var pathDataToString = function (path, m) { 87300 var M = CMD$3.M; 87301 var C = CMD$3.C; 87302 var L = CMD$3.L; 87303 var A = CMD$3.A; 87304 var Q = CMD$3.Q; 87305 87306 var str = []; 87307 var nPoint; 87308 var cmdStr; 87309 var cmd; 87310 var i; 87311 var xi; 87312 var yi; 87313 var data = path.data; 87314 var dataLength = path.len(); 87315 for (i = 0; i < dataLength;) { 87316 cmd = data[i++]; 87317 cmdStr = ''; 87318 nPoint = 0; 87319 switch (cmd) { 87320 case M: 87321 cmdStr = ' m '; 87322 nPoint = 1; 87323 xi = data[i++]; 87324 yi = data[i++]; 87325 points$3[0][0] = xi; 87326 points$3[0][1] = yi; 87327 break; 87328 case L: 87329 cmdStr = ' l '; 87330 nPoint = 1; 87331 xi = data[i++]; 87332 yi = data[i++]; 87333 points$3[0][0] = xi; 87334 points$3[0][1] = yi; 87335 break; 87336 case Q: 87337 case C: 87338 cmdStr = ' c '; 87339 nPoint = 3; 87340 var x1 = data[i++]; 87341 var y1 = data[i++]; 87342 var x2 = data[i++]; 87343 var y2 = data[i++]; 87344 var x3; 87345 var y3; 87346 if (cmd === Q) { 87347 // Convert quadratic to cubic using degree elevation 87348 x3 = x2; 87349 y3 = y2; 87350 x2 = (x2 + 2 * x1) / 3; 87351 y2 = (y2 + 2 * y1) / 3; 87352 x1 = (xi + 2 * x1) / 3; 87353 y1 = (yi + 2 * y1) / 3; 87354 } 87355 else { 87356 x3 = data[i++]; 87357 y3 = data[i++]; 87358 } 87359 points$3[0][0] = x1; 87360 points$3[0][1] = y1; 87361 points$3[1][0] = x2; 87362 points$3[1][1] = y2; 87363 points$3[2][0] = x3; 87364 points$3[2][1] = y3; 87365 87366 xi = x3; 87367 yi = y3; 87368 break; 87369 case A: 87370 var x = 0; 87371 var y = 0; 87372 var sx = 1; 87373 var sy = 1; 87374 var angle = 0; 87375 if (m) { 87376 // Extract SRT from matrix 87377 x = m[4]; 87378 y = m[5]; 87379 sx = sqrt(m[0] * m[0] + m[1] * m[1]); 87380 sy = sqrt(m[2] * m[2] + m[3] * m[3]); 87381 angle = Math.atan2(-m[1] / sy, m[0] / sx); 87382 } 87383 87384 var cx = data[i++]; 87385 var cy = data[i++]; 87386 var rx = data[i++]; 87387 var ry = data[i++]; 87388 var startAngle = data[i++] + angle; 87389 var endAngle = data[i++] + startAngle + angle; 87390 // FIXME 87391 // var psi = data[i++]; 87392 i++; 87393 var clockwise = data[i++]; 87394 87395 var x0 = cx + cos(startAngle) * rx; 87396 var y0 = cy + sin(startAngle) * ry; 87397 87398 var x1 = cx + cos(endAngle) * rx; 87399 var y1 = cy + sin(endAngle) * ry; 87400 87401 var type = clockwise ? ' wa ' : ' at '; 87402 if (Math.abs(x0 - x1) < 1e-4) { 87403 // IE won't render arches drawn counter clockwise if x0 == x1. 87404 if (Math.abs(endAngle - startAngle) > 1e-2) { 87405 // Offset x0 by 1/80 of a pixel. Use something 87406 // that can be represented in binary 87407 if (clockwise) { 87408 x0 += 270 / Z; 87409 } 87410 } 87411 else { 87412 // Avoid case draw full circle 87413 if (Math.abs(y0 - cy) < 1e-4) { 87414 if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) { 87415 y1 -= 270 / Z; 87416 } 87417 else { 87418 y1 += 270 / Z; 87419 } 87420 } 87421 else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) { 87422 x1 += 270 / Z; 87423 } 87424 else { 87425 x1 -= 270 / Z; 87426 } 87427 } 87428 } 87429 str.push( 87430 type, 87431 round$3(((cx - rx) * sx + x) * Z - Z2), comma, 87432 round$3(((cy - ry) * sy + y) * Z - Z2), comma, 87433 round$3(((cx + rx) * sx + x) * Z - Z2), comma, 87434 round$3(((cy + ry) * sy + y) * Z - Z2), comma, 87435 round$3((x0 * sx + x) * Z - Z2), comma, 87436 round$3((y0 * sy + y) * Z - Z2), comma, 87437 round$3((x1 * sx + x) * Z - Z2), comma, 87438 round$3((y1 * sy + y) * Z - Z2) 87439 ); 87440 87441 xi = x1; 87442 yi = y1; 87443 break; 87444 case CMD$3.R: 87445 var p0 = points$3[0]; 87446 var p1 = points$3[1]; 87447 // x0, y0 87448 p0[0] = data[i++]; 87449 p0[1] = data[i++]; 87450 // x1, y1 87451 p1[0] = p0[0] + data[i++]; 87452 p1[1] = p0[1] + data[i++]; 87453 87454 if (m) { 87455 applyTransform(p0, p0, m); 87456 applyTransform(p1, p1, m); 87457 } 87458 87459 p0[0] = round$3(p0[0] * Z - Z2); 87460 p1[0] = round$3(p1[0] * Z - Z2); 87461 p0[1] = round$3(p0[1] * Z - Z2); 87462 p1[1] = round$3(p1[1] * Z - Z2); 87463 str.push( 87464 // x0, y0 87465 ' m ', p0[0], comma, p0[1], 87466 // x1, y0 87467 ' l ', p1[0], comma, p0[1], 87468 // x1, y1 87469 ' l ', p1[0], comma, p1[1], 87470 // x0, y1 87471 ' l ', p0[0], comma, p1[1] 87472 ); 87473 break; 87474 case CMD$3.Z: 87475 // FIXME Update xi, yi 87476 str.push(' x '); 87477 } 87478 87479 if (nPoint > 0) { 87480 str.push(cmdStr); 87481 for (var k = 0; k < nPoint; k++) { 87482 var p = points$3[k]; 87483 87484 m && applyTransform(p, p, m); 87485 // 丠round 会éž常æâ¦Â¢ 87486 str.push( 87487 round$3(p[0] * Z - Z2), comma, round$3(p[1] * Z - Z2), 87488 k < nPoint - 1 ? comma : '' 87489 ); 87490 } 87491 } 87492 } 87493 87494 return str.join(''); 87495 }; 87496 87497 // Rewrite the original path method 87498 Path.prototype.brushVML = function (vmlRoot) { 87499 var style = this.style; 87500 87501 var vmlEl = this._vmlEl; 87502 if (!vmlEl) { 87503 vmlEl = createNode('shape'); 87504 initRootElStyle(vmlEl); 87505 87506 this._vmlEl = vmlEl; 87507 } 87508 87509 updateFillAndStroke(vmlEl, 'fill', style, this); 87510 updateFillAndStroke(vmlEl, 'stroke', style, this); 87511 87512 var m = this.transform; 87513 var needTransform = m != null; 87514 var strokeEl = vmlEl.getElementsByTagName('stroke')[0]; 87515 if (strokeEl) { 87516 var lineWidth = style.lineWidth; 87517 // Get the line scale. 87518 // Determinant of this.m_ means how much the area is enlarged by the 87519 // transformation. So its square root can be used as a scale factor 87520 // for width. 87521 if (needTransform && !style.strokeNoScale) { 87522 var det = m[0] * m[3] - m[1] * m[2]; 87523 lineWidth *= sqrt(abs$1(det)); 87524 } 87525 strokeEl.weight = lineWidth + 'px'; 87526 } 87527 87528 var path = this.path || (this.path = new PathProxy()); 87529 if (this.__dirtyPath) { 87530 path.beginPath(); 87531 this.buildPath(path, this.shape); 87532 path.toStatic(); 87533 this.__dirtyPath = false; 87534 } 87535 87536 vmlEl.path = pathDataToString(path, this.transform); 87537 87538 vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); 87539 87540 // Append to root 87541 append(vmlRoot, vmlEl); 87542 87543 // Text 87544 if (style.text != null) {
87545 this.drawRectText(vmlRoot, this.getBoundingRect()); 87546 } 87547 else { 87548 this.removeRectText(vmlRoot); 87549 } 87550 }; 87551 87552 Path.prototype.onRemove = function (vmlRoot) { 87553 remove(vmlRoot, this._vmlEl); 87554 this.removeRectText(vmlRoot); 87555 }; 87556 87557 Path.prototype.onAdd = function (vmlRoot) { 87558 append(vmlRoot, this._vmlEl); 87559 this.appendRectText(vmlRoot); 87560 }; 87561 87562 /*************************************************** 87563 * IMAGE 87564 **************************************************/ 87565 var isImage = function (img) { 87566 // FIXME img instanceof Image å¦â枊img æË¯ä¸â¬Ã¤Â¸ÂªÃ¥Ââ符串çšâæâ¶åâ¬â¢Ã¯Â¼ÅIE8 ä¸â¹Ã¤Â¼Å¡Ã¦Å ¥éâ⢠87567 return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG'; 87568 // return img instanceof Image; 87569 }; 87570 87571 // Rewrite the original path method 87572 ZImage.prototype.brushVML = function (vmlRoot) { 87573 var style = this.style; 87574 var image = style.image; 87575 87576 // Image original width, height 87577 var ow; 87578 var oh; 87579 87580 if (isImage(image)) { 87581 var src = image.src; 87582 if (src === this._imageSrc) { 87583 ow = this._imageWidth; 87584 oh = this._imageHeight; 87585 } 87586 else { 87587 var imageRuntimeStyle = image.runtimeStyle; 87588 var oldRuntimeWidth = imageRuntimeStyle.width; 87589 var oldRuntimeHeight = imageRuntimeStyle.height; 87590 imageRuntimeStyle.width = 'auto'; 87591 imageRuntimeStyle.height = 'auto'; 87592 87593 // get the original size 87594 ow = image.width; 87595 oh = image.height; 87596 87597 // and remove overides 87598 imageRuntimeStyle.width = oldRuntimeWidth; 87599 imageRuntimeStyle.height = oldRuntimeHeight; 87600 87601 // Caching image original width, height and src 87602 this._imageSrc = src; 87603 this._imageWidth = ow; 87604 this._imageHeight = oh; 87605 } 87606 image = src; 87607 } 87608 else { 87609 if (image === this._imageSrc) { 87610 ow = this._imageWidth; 87611 oh = this._imageHeight; 87612 } 87613 } 87614 if (!image) { 87615 return; 87616 } 87617 87618 var x = style.x || 0; 87619 var y = style.y || 0; 87620 87621 var dw = style.width; 87622 var dh = style.height; 87623 87624 var sw = style.sWidth; 87625 var sh = style.sHeight; 87626 var sx = style.sx || 0; 87627 var sy = style.sy || 0; 87628 87629 var hasCrop = sw && sh; 87630 87631 var vmlEl = this._vmlEl; 87632 if (!vmlEl) { 87633 // FIXME 使ç⨠group åŨ left, top éÆÂ½Ã¤Â¸ÂæË¯ 0 çšâæâ¶åâ¬â¢Ã¥Â°Â±Ã¦â æ³â¢Ã¦Ë¾ç¤ºäºâ ãâ¬â 87634 // vmlEl = vmlCore.createNode('group'); 87635 vmlEl = doc.createElement('div'); 87636 initRootElStyle(vmlEl); 87637 87638 this._vmlEl = vmlEl; 87639 } 87640 87641 var vmlElStyle = vmlEl.style; 87642 var hasRotation = false; 87643 var m; 87644 var scaleX = 1; 87645 var scaleY = 1; 87646 if (this.transform) { 87647 m = this.transform; 87648 scaleX = sqrt(m[0] * m[0] + m[1] * m[1]); 87649 scaleY = sqrt(m[2] * m[2] + m[3] * m[3]); 87650 87651 hasRotation = m[1] || m[2]; 87652 } 87653 if (hasRotation) { 87654 // If filters are necessary (rotation exists), create them 87655 // filters are bog-slow, so only create them if abbsolutely necessary 87656 // The following check doesn't account for skews (which don't exist 87657 // in the canvas spec (yet) anyway. 87658 // From excanvas 87659 var p0 = [x, y]; 87660 var p1 = [x + dw, y]; 87661 var p2 = [x, y + dh]; 87662 var p3 = [x + dw, y + dh]; 87663 applyTransform(p0, p0, m); 87664 applyTransform(p1, p1, m); 87665 applyTransform(p2, p2, m); 87666 applyTransform(p3, p3, m); 87667 87668 var maxX = mathMax$8(p0[0], p1[0], p2[0], p3[0]); 87669 var maxY = mathMax$8(p0[1], p1[1], p2[1], p3[1]); 87670 87671 var transformFilter = []; 87672 transformFilter.push('M11=', m[0] / scaleX, comma, 87673 'M12=', m[2] / scaleY, comma, 87674 'M21=', m[1] / scaleX, comma, 87675 'M22=', m[3] / scaleY, comma, 87676 'Dx=', round$3(x * scaleX + m[4]), comma, 87677 'Dy=', round$3(y * scaleY + m[5])); 87678 87679 vmlElStyle.padding = '0 ' + round$3(maxX) + 'px ' + round$3(maxY) + 'px 0'; 87680 // FIXME DXImageTransform åŨ IE11 çšâÃ¥â¦Â¼Ã¥Â®Â¹Ã¦Â¨Â¡Ã¥Â¼Âä¸â¹Ã¤Â¸Âèµ·ä½Åç⨠87681 vmlElStyle.filter = imageTransformPrefix + '.Matrix(' 87682 + transformFilter.join('') + ', SizingMethod=clip)'; 87683 87684 } 87685 else { 87686 if (m) { 87687 x = x * scaleX + m[4]; 87688 y = y * scaleY + m[5]; 87689 } 87690 vmlElStyle.filter = ''; 87691 vmlElStyle.left = round$3(x) + 'px'; 87692 vmlElStyle.top = round$3(y) + 'px'; 87693 } 87694 87695 var imageEl = this._imageEl; 87696 var cropEl = this._cropEl; 87697 87698 if (!imageEl) { 87699 imageEl = doc.createElement('div'); 87700 this._imageEl = imageEl; 87701 } 87702 var imageELStyle = imageEl.style; 87703 if (hasCrop) { 87704 // Needs know image original width and height 87705 if (! (ow && oh)) { 87706 var tmpImage = new Image(); 87707 var self = this; 87708 tmpImage.onload = function () { 87709 tmpImage.onload = null; 87710 ow = tmpImage.width; 87711 oh = tmpImage.height; 87712 // Adjust image width and height to fit the ratio destinationSize / sourceSize 87713 imageELStyle.width = round$3(scaleX * ow * dw / sw) + 'px'; 87714 imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px'; 87715 87716 // Caching image original width, height and src 87717 self._imageWidth = ow; 87718 self._imageHeight = oh; 87719 self._imageSrc = image; 87720 }; 87721 tmpImage.src = image; 87722 } 87723 else { 87724 imageELStyle.width = round$3(scaleX * ow * dw / sw) + 'px'; 87725 imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px'; 87726 } 87727 87728 if (! cropEl) { 87729 cropEl = doc.createElement('div'); 87730 cropEl.style.overflow = 'hidden'; 87731 this._cropEl = cropEl; 87732 } 87733 var cropElStyle = cropEl.style; 87734 cropElStyle.width = round$3((dw + sx * dw / sw) *
87734scaleX); 87735 cropElStyle.height = round$3((dh + sy * dh / sh) * scaleY); 87736 cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx=' 87737 + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')'; 87738 87739 if (! cropEl.parentNode) { 87740 vmlEl.appendChild(cropEl); 87741 } 87742 if (imageEl.parentNode != cropEl) { 87743 cropEl.appendChild(imageEl); 87744 } 87745 } 87746 else { 87747 imageELStyle.width = round$3(scaleX * dw) + 'px'; 87748 imageELStyle.height = round$3(scaleY * dh) + 'px'; 87749 87750 vmlEl.appendChild(imageEl); 87751 87752 if (cropEl && cropEl.parentNode) { 87753 vmlEl.removeChild(cropEl); 87754 this._cropEl = null; 87755 } 87756 } 87757 87758 var filterStr = ''; 87759 var alpha = style.opacity; 87760 if (alpha < 1) { 87761 filterStr += '.Alpha(opacity=' + round$3(alpha * 100) + ') '; 87762 } 87763 filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)'; 87764 87765 imageELStyle.filter = filterStr; 87766 87767 vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); 87768 87769 // Append to root 87770 append(vmlRoot, vmlEl); 87771 87772 // Text 87773 if (style.text != null) { 87774 this.drawRectText(vmlRoot, this.getBoundingRect()); 87775 } 87776 }; 87777 87778 ZImage.prototype.onRemove = function (vmlRoot) { 87779 remove(vmlRoot, this._vmlEl); 87780 87781 this._vmlEl = null; 87782 this._cropEl = null; 87783 this._imageEl = null; 87784 87785 this.removeRectText(vmlRoot); 87786 }; 87787 87788 ZImage.prototype.onAdd = function (vmlRoot) { 87789 append(vmlRoot, this._vmlEl); 87790 this.appendRectText(vmlRoot); 87791 }; 87792 87793 87794 /*************************************************** 87795 * TEXT 87796 **************************************************/ 87797 87798 var DEFAULT_STYLE_NORMAL = 'normal'; 87799 87800 var fontStyleCache = {}; 87801 var fontStyleCacheCount = 0; 87802 var MAX_FONT_CACHE_SIZE = 100; 87803 var fontEl = document.createElement('div'); 87804 87805 var getFontStyle = function (fontString) { 87806 var fontStyle = fontStyleCache[fontString]; 87807 if (!fontStyle) { 87808 // Clear cache 87809 if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) { 87810 fontStyleCacheCount = 0; 87811 fontStyleCache = {}; 87812 } 87813 87814 var style = fontEl.style; 87815 var fontFamily; 87816 try { 87817 style.font = fontString; 87818 fontFamily = style.fontFamily.split(',')[0]; 87819 } 87820 catch (e) { 87821 } 87822 87823 fontStyle = { 87824 style: style.fontStyle || DEFAULT_STYLE_NORMAL, 87825 variant: style.fontVariant || DEFAULT_STYLE_NORMAL, 87826 weight: style.fontWeight || DEFAULT_STYLE_NORMAL, 87827 size: parseFloat(style.fontSize || 12) | 0, 87828 family: fontFamily || 'Microsoft YaHei' 87829 }; 87830 87831 fontStyleCache[fontString] = fontStyle; 87832 fontStyleCacheCount++; 87833 } 87834 return fontStyle; 87835 }; 87836 87837 var textMeasureEl; 87838 // Overwrite measure text method 87839 $override$1('measureText', function (text, textFont) { 87840 var doc$$1 = doc; 87841 if (!textMeasureEl) { 87842 textMeasureEl = doc$$1.createElement('div'); 87843 textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;' 87844 + 'padding:0;margin:0;border:none;white-space:pre;'; 87845 doc.body.appendChild(textMeasureEl); 87846 } 87847 87848 try { 87849 textMeasureEl.style.font = textFont; 87850 } catch (ex) {
87851 // Ignore failures to set to invalid font. 87852 } 87853 textMeasureEl.innerHTML = ''; 87854 // Don't use innerHTML or innerText because they allow markup/whitespace. 87855 textMeasureEl.appendChild(doc$$1.createTextNode(text)); 87856 return { 87857 width: textMeasureEl.offsetWidth 87858 }; 87859 }); 87860 87861 var tmpRect$2 = new BoundingRect(); 87862 87863 var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) { 87864 87865 var style = this.style; 87866 87867 // Optimize, avoid normalize every time. 87868 this.__dirty && normalizeTextStyle(style, true); 87869 87870 var text = style.text; 87871 // Convert to string 87872 text != null && (text += ''); 87873 if (!text) { 87874 return; 87875 } 87876 87877 // Convert rich text to plain text. Rich text is not supported in 87878 // IE8-, but tags in rich text template will be removed. 87879 if (style.rich) { 87880 var contentBlock = parseRichText(text, style); 87881 text = []; 87882 for (var i = 0; i < contentBlock.lines.length; i++) { 87883 var tokens = contentBlock.lines[i].tokens; 87884 var textLine = []; 87885 for (var j = 0; j < tokens.length; j++) { 87886 textLine.push(tokens[j].text); 87887 } 87888 text.push(textLine.join('')); 87889 } 87890 text = text.join('\n'); 87891 } 87892 87893 var x; 87894 var y; 87895 var align = style.textAlign; 87896 var verticalAlign = style.textVerticalAlign; 87897 87898 var fontStyle = getFontStyle(style.font); 87899 // FIXME encodeHtmlAttribute ? 87900 var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' ' 87901 + fontStyle.size + 'px "' + fontStyle.family + '"'; 87902 87903 textRect = textRect || getBoundingRect(text, font, align, verticalAlign); 87904 87905 // Transform rect to view space 87906 var m = this.transform; 87907 // Ignore transform for text in other element 87908 if (m && !fromTextEl) { 87909 tmpRect$2.copy(rect); 87910 tmpRect$2.applyTransform(m); 87911 rect = tmpRect$2; 87912 } 87913 87914 if (!fromTextEl) { 87915 var textPosition = style.textPosition; 87916 var distance$$1 = style.textDistance; 87917 // Text position represented by coord 87918 if (textPosition instanceof Array) { 87919 x = rect.x + parsePercent$3(textPosition[0], rect.width); 87920 y = rect.y + parsePercent$3(textPosition[1], rect.height); 87921 87922 align = align || 'left'; 87923 } 87924 else { 87925 var res = adjustTextPositionOnRect( 87926 textPosition, rect, distance$$1 87927 ); 87928 x = res.x; 87929 y = res.y; 87930 87931 // Default align and baseline when has textPosition 87932 align = align || res.textAlign; 87933 verticalAlign = verticalAlign || res.textVerticalAlign; 87934 } 87935 } 87936 else { 87937 x = rect.x; 87938 y = rect.y; 87939 } 87940 87941 x = adjustTextX(x, textRect.width, align); 87942 y = adjustTextY(y, textRect.height, verticalAlign); 87943 87944 // Force baseline 'middle' 87945 y += textRect.height / 2; 87946 87947 // var fontSize = fontStyle.size; 87948 // 1.75 is an arbitrary number, as there is no info about the text baseline 87949 // switch (baseline) { 87950 // case 'hanging': 87951 // case 'top': 87952 // y += fontSize / 1.75; 87953 // break; 87954 // case 'middle': 87955 // break; 87956 // default: 87957 // // case null: 87958 // // case 'alphabetic': 87959 // // case 'ideographic': 87960 // // case 'bottom': 87961 // y -= fontSize / 2.25; 87962 // break; 87963 // } 87964 87965 // switch (align) { 87966 // case 'left': 87967 // break; 87968 // case 'center': 87969 // x -= textRect.width / 2; 87970 // break; 87971 // case 'right': 87972 // x -= textRect.width; 87973 // break; 87974 // case 'end': 87975 // align = elementStyle.direction == 'ltr' ? 'right' : 'left'; 87976 // break; 87977 // case 'start': 87978 // align = elementStyle.direction == 'rtl' ? 'right' : 'left'; 87979 // break; 87980 // default: 87981 // align = 'left'; 87982 // } 87983 87984 var createNode$$1 = createNode; 87985 87986 var textVmlEl = this._textVmlEl; 87987 var pathEl; 87988 var textPathEl; 87989 var skewEl; 87990 if (!textVmlEl) { 87991 textVmlEl = createNode$$1('line'); 87992 pathEl = createNode$$1('path'); 87993 textPathEl = createNode$$1('textpath'); 87994 skewEl = createNode$$1('skew'); 87995 87996 // FIXME Why here is not cammel case 87997 // Align 'center' seems wrong 87998 textPathEl.style['v-text-align'] = 'left'; 87999 88000 initRootElStyle(textVmlEl); 88001 88002 pathEl.textpathok = true; 88003 textPathEl.on = true; 88004 88005 textVmlEl.from = '0 0'; 88006 textVmlEl.to = '1000 0.05'; 88007 88008 append(textVmlEl, skewEl); 88009 append(textVmlEl, pathEl); 88010 append(textVmlEl, textPathEl); 88011 88012 this._textVmlEl = textVmlEl; 88013 } 88014 else { 88015 // è¿â¢Ã©â¡ÅæË¯åŨåâ°Âé¢ appendChild ä¿Âè¯Â顺åºÂçšâÃ¥â°ÂæÂÂä¸⹠88016 skewEl = textVmlEl.firstChild; 88017 pathEl = skewEl.nextSibling; 88018 textPathEl = pathEl.nextSibling; 88019 } 88020 88021 var coords = [x, y]; 88022 var textVmlElStyle = textVmlEl.style; 88023 // Ignore transform for text in other element 88024 if (m && fromTextEl) { 88025 applyTransform(coords, coords, m); 88026 88027 skewEl.on = true; 88028 88029 skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma + 88030 m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0'; 88031 88032 // Text position 88033 skewEl.offset = (round$3(coords[0]) || 0) + ',' + (round$3(coords[1]) || 0); 88034 // Left top point as origin 88035 skewEl.origin = '0 0'; 88036 88037 textVmlElStyle.left = '0px'; 88038 textVmlElStyle.top = '0px'; 88039 } 88040 else { 88041 skewEl.on = false; 88042 textVmlElStyle.left = round$3(x) + 'px'; 88043 textVmlElStyle.top = round$3(y) + 'px'; 88044 } 88045 88046 textPathEl.string = encodeHtmlAttribute(text); 88047 // TODO 88048 try { 88049 textPathEl.style.font = font; 88050 } 88051 // Error font format 88052 catch (e) {} 88053 88054 updateFillAndStroke(textVmlEl, 'fill', { 88055 fill: style.textFill, 88056 opacity: style.opacity 88057 }, this); 88058 updateFillAndStroke(textVmlEl, 'stroke', { 88059 stroke: style.textStroke, 88060 opacity: style.opacity, 88061 lineDash: style.lineDash 88062 }, this); 88063 88064 textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); 88065 88066 // Attached to root 88067 append(vmlRoot, textVmlEl); 88068 }; 88069 88070 var removeRectText = function (vmlRoot) { 88071 remove(vmlRoot, this._textVmlEl); 88072 this._textVmlEl = null; 88073 }; 88074 88075 var appendRectText = function (vmlRoot) { 88076 append(vmlRoot, this._textVmlEl); 88077 }; 88078 88079 var list = [RectText, Displayable, ZImage, Path, Text]; 88080 88081 // In case Displayable has been mixed in RectText 88082 for (var i$3 = 0; i$3 < list.length; i$3++) { 88083 var proto$8 = list[i$3].prototype; 88084 proto$8.drawRectText = drawRectText; 88085 proto$8.removeRectText = removeRectText;
88086 proto$8.appendRectText = appendRectText; 88087 } 88088 88089 Text.prototype.brushVML = function (vmlRoot) { 88090 var style = this.style; 88091 if (style.text != null) { 88092 this.drawRectText(vmlRoot, { 88093 x: style.x || 0, y: style.y || 0, 88094 width: 0, height: 0 88095 }, this.getBoundingRect(), true); 88096 } 88097 else { 88098 this.removeRectText(vmlRoot); 88099 } 88100 }; 88101 88102 Text.prototype.onRemove = function (vmlRoot) { 88103 this.removeRectText(vmlRoot); 88104 }; 88105 88106 Text.prototype.onAdd = function (vmlRoot) { 88107 this.appendRectText(vmlRoot); 88108 }; 88109 } 88110 88111 /** 88112 * VML Painter. 88113 * 88114 * @module zrender/vml/Painter 88115 */ 88116 88117 function parseInt10$1(val) { 88118 return parseInt(val, 10); 88119 } 88120 88121 /** 88122 * @alias module:zrender/vml/Painter 88123 */ 88124 function VMLPainter(root, storage) { 88125 88126 initVML(); 88127 88128 this.root = root; 88129 88130 this.storage = storage; 88131 88132 var vmlViewport = document.createElement('div'); 88133 88134 var vmlRoot = document.createElement('div'); 88135 88136 vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;'; 88137 88138 vmlRoot.style.cssText = 'position:absolute;left:0;top:0;'; 88139 88140 root.appendChild(vmlViewport); 88141 88142 this._vmlRoot = vmlRoot; 88143 this._vmlViewport = vmlViewport; 88144 88145 this.resize(); 88146 88147 // Modify storage 88148 var oldDelFromStorage = storage.delFromStorage; 88149 var oldAddToStorage = storage.addToStorage; 88150 storage.delFromStorage = function (el) { 88151 oldDelFromStorage.call(storage, el); 88152 88153 if (el) { 88154 el.onRemove && el.onRemove(vmlRoot); 88155 } 88156 }; 88157 88158 storage.addToStorage = function (el) { 88159 // Displayable already has a vml node 88160 el.onAdd && el.onAdd(vmlRoot); 88161 88162 oldAddToStorage.call(storage, el); 88163 }; 88164 88165 this._firstPaint = true; 88166 } 88167 88168 VMLPainter.prototype = { 88169 88170 constructor: VMLPainter, 88171 88172 getType: function () { 88173 return 'vml'; 88174 }, 88175 88176 /** 88177 * @return {HTMLDivElement} 88178 */ 88179 getViewportRoot: function () { 88180 return this._vmlViewport; 88181 }, 88182 88183 getViewportRootOffset: function () { 88184 var viewportRoot = this.getViewportRoot(); 88185 if (viewportRoot) { 88186 return { 88187 offsetLeft: viewportRoot.offsetLeft || 0, 88188 offsetTop: viewportRoot.offsetTop || 0 88189 }; 88190 } 88191 }, 88192 88193 /** 88194 * å˷æâ° 88195 */ 88196 refresh: function () { 88197 88198 var list = this.storage.getDisplayList(true, true); 88199 88200 this._paintList(list); 88201 }, 88202 88203 _paintList: function (list) { 88204 var vmlRoot = this._vmlRoot; 88205 for (var i = 0; i < list.length; i++) { 88206 var el = list[i]; 88207 if (el.invisible || el.ignore) { 88208 if (!el.__alreadyNotVisible) { 88209 el.onRemove(vmlRoot); 88210 } 88211 // Set as already invisible 88212 el.__alreadyNotVisible = true; 88213 } 88214 else { 88215 if (el.__alreadyNotVisible) { 88216 el.onAdd(vmlRoot); 88217 } 88218 el.__alreadyNotVisible = false; 88219 if (el.__dirty) { 88220 el.beforeBrush && el.beforeBrush(); 88221 (el.brushVML || el.brush).call(el, vmlRoot); 88222 el.afterBrush && el.afterBrush(); 88223 } 88224 } 88225 el.__dirty = false; 88226 } 88227 88228 if (this._firstPaint) { 88229 // Detached from document at first time 88230 // to avoid page refreshing too many times 88231 88232 // FIXME å¦âæžÅæ¯Â次éÆÂ½Ã¥â¦Ë removeChild å¯èÆÂ½Ã¤Â¼Å¡Ã¥Â¯Â¼Ã¨â¡Â´Ã¤Â¸â¬Ã¤ÂºâºÃ¥Â¡Â«Ã¥â¦â¦Ã¥âÅæÂÂè¾¹çšâæâ¢ËæžÅæâ¹åÂË 88233 this._vmlViewport.appendChild(vmlRoot); 88234 this._firstPaint = false; 88235 } 88236 }, 88237 88238 resize: function (width, height) { 88239 var width = width == null ? this._getWidth() : width; 88240 var height = height == null ? this._getHeight() : height; 88241 88242 if (this._width != width || this._height != height) { 88243 this._width = width; 88244 this._height = height; 88245 88246 var vmlViewportStyle = this._vmlViewport.style; 88247 vmlViewportStyle.width = width + 'px'; 88248 vmlViewportStyle.height = height + 'px'; 88249 } 88250 }, 88251 88252 dispose: function () { 88253 this.root.innerHTML = ''; 88254 88255 this._vmlRoot = 88256 this._vmlViewport = 88257 this.storage = null; 88258 }, 88259 88260 getWidth: function () { 88261 return this._width; 88262 }, 88263 88264 getHeight: function () { 88265 return this._height; 88266 }, 88267 88268 clear: function () { 88269 if (this._vmlViewport) { 88270 this.root.removeChild(this._vmlViewport); 88271 } 88272 }, 88273 88274 _getWidth: function () { 88275 var root = this.root; 88276 var stl = root.currentStyle; 88277 88278 return ((root.clientWidth || parseInt10$1(stl.width)) 88279 - parseInt10$1(stl.paddingLeft) 88280 - parseInt10$1(stl.paddingRight)) | 0; 88281 }, 88282 88283 _getHeight: function () { 88284 var root = this.root; 88285 var stl = root.currentStyle; 88286 88287 return ((root.clientHeight || parseInt10$1(stl.height)) 88288 - parseInt10$1(stl.paddingTop) 88289 - parseInt10$1(stl.paddingBottom)) | 0; 88290 } 88291 }; 88292 88293// Not supported methods 88294 function createMethodNotSupport(method) { 88295 return function () {
88296 zrLog('In IE8.0 VML mode painter not support method "' + method + '"'); 88297 }; 88298 } 88299 88300// Unsupported methods 88301 each$1([ 88302 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', 88303 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage' 88304 ], function (name) { 88305 VMLPainter.prototype[name] = createMethodNotSupport(name); 88306 }); 88307 88308 registerPainter('vml', VMLPainter); 88309 88310 var svgURI = 'http://www.w3.org/2000/svg'; 88311 88312 function createElement(name) { 88313 return document.createElementNS(svgURI, name); 88314 } 88315 88316// TODO 88317// 1. shadow 88318// 2. Image: sx, sy, sw, sh 88319 88320 var CMD$4 = PathProxy.CMD; 88321 var arrayJoin = Array.prototype.join; 88322 88323 var NONE = 'none'; 88324 var mathRound = Math.round; 88325 var mathSin$3 = Math.sin; 88326 var mathCos$3 = Math.cos; 88327 var PI$5 = Math.PI; 88328 var PI2$7 = Math.PI * 2; 88329 var degree = 180 / PI$5; 88330 88331 var EPSILON$4 = 1e-4; 88332 88333 function round4(val) { 88334 return mathRound(val * 1e4) / 1e4; 88335 } 88336 88337 function isAroundZero$1(val) { 88338 return val < EPSILON$4 && val > -EPSILON$4; 88339 } 88340 88341 function pathHasFill(style, isText) { 88342 var fill = isText ? style.textFill : style.fill; 88343 return fill != null && fill !== NONE; 88344 } 88345 88346 function pathHasStroke(style, isText) { 88347 var stroke = isText ? style.textStroke : style.stroke; 88348 return stroke != null && stroke !== NONE; 88349 } 88350 88351 function setTransform(svgEl, m) { 88352 if (m) { 88353 attr(svgEl, 'transform', 'matrix(' + arrayJoin.call(m, ',') + ')'); 88354 } 88355 } 88356 88357 function attr(el, key, val) { 88358 if (!val || val.type !== 'linear' && val.type !== 'radial') { 88359 // Don't set attribute for gradient, since it need new dom nodes 88360 if (typeof val === 'string' && val.indexOf('NaN') > -1) { 88361 console.log(val); 88362 } 88363 el.setAttribute(key, val); 88364 } 88365 } 88366 88367 function attrXLink(el, key, val) { 88368 el.setAttributeNS('http://www.w3.org/1999/xlink', key, val); 88369 } 88370 88371 function bindStyle(svgEl, style, isText) { 88372 if (pathHasFill(style, isText)) { 88373 var fill = isText ? style.textFill : style.fill; 88374 fill = fill === 'transparent' ? NONE : fill; 88375 88376 /** 88377 * FIXME: 88378 * This is a temporary fix for Chrome's clipping bug 88379 * that happens when a clip-path is referring another one. 88380 * This fix should be used before Chrome's bug is fixed. 88381 * For an element that has clip-path, and fill is none, 88382 * set it to be "rgba(0, 0, 0, 0.002)" will hide the element. 88383 * Otherwise, it will show black fill color. 88384 * 0.002 is used because this won't work for alpha values smaller 88385 * than 0.002. 88386 * 88387 * See 88388 * https://bugs.chromium.org/p/chromium/issues/detail?id=659790 88389 * for more information. 88390 */ 88391 if (svgEl.getAttribute('clip-path') !== 'none' && fill === NONE) { 88392 fill = 'rgba(0, 0, 0, 0.002)'; 88393 } 88394 88395 attr(svgEl, 'fill', fill); 88396 attr(svgEl, 'fill-opacity', style.opacity); 88397 } 88398 else { 88399 attr(svgEl, 'fill', NONE); 88400 } 88401 88402 if (pathHasStroke(style, isText)) { 88403 var stroke = isText ? style.textStroke : style.stroke; 88404 stroke = stroke === 'transparent' ? NONE : stroke; 88405 attr(svgEl, 'stroke', stroke); 88406 var strokeWidth = isText 88407 ? style.textStrokeWidth 88408 : style.lineWidth; 88409 var strokeScale = !isText && style.strokeNoScale 88410 ? style.host.getLineScale() 88411 : 1; 88412 attr(svgEl, 'stroke-width', strokeWidth / strokeScale); 88413 // stroke then fill for text; fill then stroke for others 88414 attr(svgEl, 'paint-order', isText ? 'stroke' : 'fill'); 88415 attr(svgEl, 'stroke-opacity', style.opacity); 88416 var lineDash = style.lineDash; 88417 if (lineDash) { 88418 attr(svgEl, 'stroke-dasharray', style.lineDash.join(',')); 88419 attr(svgEl, 'stroke-dashoffset', mathRound(style.lineDashOffset || 0)); 88420 } 88421 else { 88422 attr(svgEl, 'stroke-dasharray', ''); 88423 } 88424 88425 // PENDING 88426 style.lineCap && attr(svgEl, 'stroke-linecap', style.lineCap); 88427 style.lineJoin && attr(svgEl, 'stroke-linejoin', style.lineJoin); 88428 style.miterLimit && attr(svgEl, 'stroke-miterlimit', style.miterLimit); 88429 } 88430 else { 88431 attr(svgEl, 'stroke', NONE); 88432 } 88433 } 88434 88435 /*************************************************** 88436 * PATH 88437 **************************************************/ 88438 function pathDataToString$1(path) { 88439 var str = []; 88440 var data = path.data; 88441 var dataLength = path.len(); 88442 for (var i = 0; i < dataLength;) { 88443 var cmd = data[i++]; 88444 var cmdStr = ''; 88445 var nData = 0; 88446 switch (cmd) { 88447 case CMD$4.M: 88448 cmdStr = 'M'; 88449 nData = 2; 88450 break; 88451 case CMD$4.L: 88452 cmdStr = 'L'; 88453 nData = 2; 88454 break; 88455 case CMD$4.Q: 88456 cmdStr = 'Q'; 88457 nData = 4; 88458 break; 88459 case CMD$4.C: 88460 cmdStr = 'C'; 88461 nData = 6; 88462 break; 88463 case CMD$4.A: 88464 var cx = data[i++]; 88465 var cy = data[i++]; 88466 var rx = data[i++]; 88467 var ry = data[i++]; 88468 var theta = data[i++]; 88469 var dTheta = data[i++]; 88470 var psi = data[i++]; 88471 var clockwise = data[i++]; 88472 88473 var dThetaPositive = Math.abs(dTheta); 88474 var isCircle = isAroundZero$1(dThetaPositive - PI2$7) 88475 && !isAroundZero$1(dThetaPositive); 88476 88477 var large = false; 88478 if (dThetaPositive >= PI2$7) { 88479 large = true; 88480 } 88481 else if (isAroundZero$1(dThetaPositive)) { 88482 large = false; 88483 } 88484 else { 88485 large = (dTheta > -PI$5 && dTheta < 0 || dTheta > PI$5) 88486 === !!clockwise; 88487 } 88488 88489 var x0 = round4(cx + rx * mathCos$3(theta)); 88490 var y0 = round4(cy + ry * mathSin$3(theta)); 88491 88492 // It will not draw if start point and end point are exactly the same 88493 // We need to shift the end point with a small value 88494 // FIXME A better way to draw circle ? 88495 if (isCircle) { 88496 if (clockwise) { 88497 dTheta = PI2$7 - 1e-4; 88498 } 88499 else { 88500 dTheta = -PI2$7 + 1e-4; 88501 } 88502 88503 large = true; 88504 88505 if (i === 9) { 88506 // Move to (x0, y0) only when CMD.A comes at the 88507 // first position of a shape. 88508 // For instance, when drawing a ring, CMD.A comes 88509 // after CMD.M, so it's unnecessary to move to 88510 // (x0, y0). 88511 str.push('M', x0, y0); 88512 } 88513 } 88514 88515 var x = round4(cx + rx * mathCos$3(theta + dTheta)); 88516 var y = round4(cy + ry * mathSin$3(theta + dTheta)); 88517 88518 // FIXME Ellipse 88519 str.push('A', round4(rx), round4(ry), 88520 mathRound(psi * degree), +large, +clockwise, x, y); 88521 break; 88522 case CMD$4.Z: 88523 cmdStr = 'Z'; 88524 break; 88525 case CMD$4.R: 88526 var x = round4(data[i++]); 88527 var y = round4(data[i++]); 88528 var w = round4(data[i++]); 88529 var h = round4(data[i++]); 88530 str.push( 88531 'M', x, y, 88532 'L', x + w, y, 88533 'L', x + w, y + h, 88534 'L', x, y + h, 88535 'L', x, y 88536 ); 88537 break; 88538 } 88539 cmdStr && str.push(cmdStr); 88540 for (var j = 0; j < nData; j++) { 88541 // PENDING With scale 88542 str.push(round4(data[i++])); 88543 } 88544 } 88545 return str.join(' '); 88546 } 88547 88548 var svgPath = {}; 88549 svgPath.brush = function (el) { 88550 var style = el.style; 88551 88552 var svgEl = el.__svgEl; 88553 if (!svgEl) { 88554 svgEl = createElement('path'); 88555 el.__svgEl = svgEl; 88556 } 88557 88558 if (!el.path) { 88559 el.createPathProxy(); 88560 } 88561 var path = el.path; 88562 88563 if (el.__dirtyPath) { 88564 path.beginPath(); 88565 el.buildPath(path, el.shape); 88566 el.__dirtyPath = false; 88567 88568 var pathStr = pathDataToString$1(path);
88569 if (pathStr.indexOf('NaN') < 0) { 88570 // Ignore illegal path, which may happen such in out-of-range 88571 // data in Calendar series. 88572 attr(svgEl, 'd', pathStr); 88573 } 88574 } 88575 88576 bindStyle(svgEl, style); 88577 setTransform(svgEl, el.transform); 88578 88579 if (style.text != null) { 88580 svgTextDrawRectText(el, el.getBoundingRect()); 88581 } 88582 }; 88583 88584 /*************************************************** 88585 * IMAGE 88586 **************************************************/ 88587 var svgImage = {}; 88588 svgImage.brush = function (el) { 88589 var style = el.style; 88590 var image = style.image; 88591 88592 if (image instanceof HTMLImageElement) { 88593 var src = image.src; 88594 image = src; 88595 } 88596 if (! image) { 88597 return; 88598 } 88599 88600 var x = style.x || 0; 88601 var y = style.y || 0; 88602 88603 var dw = style.width; 88604 var dh = style.height; 88605 88606 var svgEl = el.__svgEl; 88607 if (! svgEl) { 88608 svgEl = createElement('image'); 88609 el.__svgEl = svgEl; 88610 } 88611 88612 if (image !== el.__imageSrc) { 88613 attrXLink(svgEl, 'href', image); 88614 // Caching image src 88615 el.__imageSrc = image; 88616 } 88617 88618 attr(svgEl, 'width', dw); 88619 attr(svgEl, 'height', dh); 88620 88621 attr(svgEl, 'x', x); 88622 attr(svgEl, 'y', y); 88623 88624 setTransform(svgEl, el.transform); 88625 88626 if (style.text != null) { 88627 svgTextDrawRectText(el, el.getBoundingRect()); 88628 } 88629 }; 88630 88631 /*************************************************** 88632 * TEXT 88633 **************************************************/ 88634 var svgText = {}; 88635 var tmpRect$3 = new BoundingRect(); 88636 88637 var svgTextDrawRectText = function (el, rect, textRect) { 88638 var style = el.style; 88639 88640 el.__dirty && normalizeTextStyle(style, true); 88641 88642 var text = style.text; 88643 // Convert to string 88644 if (text == null) { 88645 // Draw no text only when text is set to null, but not '' 88646 return; 88647 } 88648 else { 88649 text += ''; 88650 } 88651 88652 var textSvgEl = el.__textSvgEl; 88653 if (! textSvgEl) { 88654 textSvgEl = createElement('text'); 88655 el.__textSvgEl = textSvgEl; 88656 } 88657 88658 var x; 88659 var y; 88660 var textPosition = style.textPosition; 88661 var distance = style.textDistance; 88662 var align = style.textAlign || 'left'; 88663 88664 if (typeof style.fontSize === 'number') { 88665 style.fontSize += 'px'; 88666 } 88667 var font = style.font 88668 || [ 88669 style.fontStyle || '', 88670 style.fontWeight || '', 88671 style.fontSize || '', 88672 style.fontFamily || '' 88673 ].join(' ') 88674 || DEFAULT_FONT; 88675 88676 var verticalAlign = getVerticalAlignForSvg(style.textVerticalAlign); 88677 88678 textRect = getBoundingRect(text, font, align, 88679 verticalAlign); 88680 88681 var lineHeight = textRect.lineHeight; 88682 // Text position represented by coord 88683 if (textPosition instanceof Array) { 88684 x = rect.x + textPosition[0]; 88685 y = rect.y + textPosition[1]; 88686 } 88687 else { 88688 var newPos = adjustTextPositionOnRect( 88689 textPosition, rect, distance 88690 ); 88691 x = newPos.x; 88692 y = newPos.y; 88693 verticalAlign = getVerticalAlignForSvg(newPos.textVerticalAlign); 88694 align = newPos.textAlign; 88695 } 88696 88697 attr(textSvgEl, 'alignment-baseline', verticalAlign); 88698 88699 if (font) { 88700 textSvgEl.style.font = font; 88701 } 88702 88703 var textPadding = style.textPadding; 88704 88705 // Make baseline top 88706 attr(textSvgEl, 'x', x); 88707 attr(textSvgEl, 'y', y); 88708 88709 bindStyle(textSvgEl, style, true); 88710 if (el instanceof Text || el.style.transformText) { 88711 // Transform text with element 88712 setTransform(textSvgEl, el.transform); 88713 } 88714 else { 88715 if (el.transform) { 88716 tmpRect$3.copy(rect); 88717 tmpRect$3.applyTransform(el.transform); 88718 rect = tmpRect$3; 88719 } 88720 else { 88721 var pos = el.transformCoordToGlobal(rect.x, rect.y); 88722 rect.x = pos[0]; 88723 rect.y = pos[1]; 88724 } 88725 88726 // Text rotation, but no element transform 88727 var origin = style.textOrigin; 88728 if (origin === 'center') { 88729 x = textRect.width / 2 + x; 88730 y = textRect.height / 2 + y; 88731 } 88732 else if (origin) { 88733 x = origin[0] + x; 88734 y = origin[1] + y; 88735 } 88736 var rotate$$1 = -style.textRotation || 0; 88737 var transform = create$1(); 88738 // Apply textRotate to element matrix 88739 rotate(transform, el.transform, rotate$$1); 88740 setTransform(textSvgEl, transform); 88741 } 88742 88743 var textLines = text.split('\n'); 88744 var nTextLines = textLines.length; 88745 var textAnchor = align; 88746 // PENDING 88747 if (textAnchor === 'left') { 88748 textAnchor = 'start'; 88749 textPadding && (x += textPadding[3]); 88750 } 88751 else if (textAnchor === 'right') { 88752 textAnchor = 'end'; 88753 textPadding && (x -= textPadding[1]); 88754 } 88755 else if (textAnchor === 'center') { 88756 textAnchor = 'middle'; 88757 textPadding && (x += (textPadding[3] - textPadding[1]) / 2); 88758 } 88759 88760 var dy = 0; 88761 if (verticalAlign === 'baseline') { 88762 dy = -textRect.height + lineHeight; 88763 textPadding && (dy -= textPadding[2]); 88764 } 88765 else if (verticalAlign === 'middle') { 88766 dy = (-textRect.height + lineHeight) / 2; 88767 textPadding && (y += (textPadding[0] - textPadding[2]) / 2); 88768 } 88769 else { 88770 textPadding && (dy += textPadding[0]); 88771 } 88772 88773 // Font may affect position of each tspan elements 88774 if (el.__text !== text || el.__textFont !== font) { 88775 var tspanList = el.__tspanList || []; 88776 el.__tspanList = tspanList; 88777 for (var i = 0; i < nTextLines; i++) { 88778 // Using cached tspan elements 88779 var tspan = tspanList[i]; 88780 if (! tspan) { 88781 tspan = tspanList[i] = createElement('tspan'); 88782 textSvgEl.appendChild(tspan); 88783 attr(tspan, 'alignment-baseline', verticalAlign); 88784 attr(tspan, 'text-anchor', textAnchor); 88785 } 88786 else { 88787 tspan.innerHTML = ''; 88788 } 88789 attr(tspan, 'x', x); 88790 attr(tspan, 'y', y + i * lineHeight + dy);
88791 tspan.appendChild(document.createTextNode(textLines[i])); 88792 } 88793 // Remove unsed tspan elements 88794 for (; i < tspanList.length; i++) { 88795 textSvgEl.removeChild(tspanList[i]); 88796 } 88797 tspanList.length = nTextLines; 88798 88799 el.__text = text; 88800 el.__textFont = font; 88801 } 88802 else if (el.__tspanList.length) { 88803 // Update span x and y 88804 var len = el.__tspanList.length; 88805 for (var i = 0; i < len; ++i) { 88806 var tspan = el.__tspanList[i]; 88807 if (tspan) { 88808 attr(tspan, 'x', x); 88809 attr(tspan, 'y', y + i * lineHeight + dy); 88810 } 88811 } 88812 } 88813 }; 88814 88815 function getVerticalAlignForSvg(verticalAlign) { 88816 if (verticalAlign === 'middle') { 88817 return 'middle'; 88818 } 88819 else if (verticalAlign === 'bottom') { 88820 return 'baseline'; 88821 } 88822 else { 88823 return 'hanging'; 88824 } 88825 } 88826 88827 svgText.drawRectText = svgTextDrawRectText; 88828 88829 svgText.brush = function (el) { 88830 var style = el.style; 88831 if (style.text != null) { 88832 // 强å˶设置 textPosition 88833 style.textPosition = [0, 0]; 88834 svgTextDrawRectText(el, { 88835 x: style.x || 0, y: style.y || 0, 88836 width: 0, height: 0 88837 }, el.getBoundingRect()); 88838 } 88839 }; 88840 88841// Myers' Diff Algorithm 88842// Modified from https://github.com/kpdecker/jsdiff/blob/master/src/diff/base.js 88843 88844 function Diff() {} 88845 88846 Diff.prototype = { 88847 diff: function (oldArr, newArr, equals) { 88848 if (!equals) { 88849 equals = function (a, b) { 88850 return a === b; 88851 }; 88852 } 88853 this.equals = equals; 88854 88855 var self = this; 88856 88857 oldArr = oldArr.slice(); 88858 newArr = newArr.slice(); 88859 // Allow subclasses to massage the input prior to running 88860 var newLen = newArr.length; 88861 var oldLen = oldArr.length; 88862 var editLength = 1; 88863 var maxEditLength = newLen + oldLen; 88864 var bestPath = [{ newPos: -1, components: [] }]; 88865 88866 // Seed editLength = 0, i.e. the content starts with the same values 88867 var oldPos = this.extractCommon(bestPath[0], newArr, oldArr, 0); 88868 if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { 88869 var indices = []; 88870 for (var i = 0; i < newArr.length; i++) { 88871 indices.push(i); 88872 } 88873 // Identity per the equality and tokenizer 88874 return [{ 88875 indices: indices, count: newArr.length 88876 }]; 88877 } 88878 88879 // Main worker method. checks all permutations of a given edit length for acceptance. 88880 function execEditLength() { 88881 for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { 88882 var basePath; 88883 var addPath = bestPath[diagonalPath - 1]; 88884 var removePath = bestPath[diagonalPath + 1]; 88885 var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; 88886 if (addPath) { 88887 // No one else is going to attempt to use this value, clear it 88888 bestPath[diagonalPath - 1] = undefined; 88889 } 88890 88891 var canAdd = addPath && addPath.newPos + 1 < newLen; 88892 var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; 88893 if (!canAdd && !canRemove) { 88894 // If this path is a terminal then prune 88895 bestPath[diagonalPath] = undefined; 88896 continue; 88897 } 88898 88899 // Select the diagonal that we want to branch from. We select the prior 88900 // path whose position in the new string is the farthest from the origin 88901 // and does not pass the bounds of the diff graph 88902 if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { 88903 basePath = clonePath(removePath); 88904 self.pushComponent(basePath.components, undefined, true); 88905 } 88906 else { 88907 basePath = addPath; // No need to clone, we've pulled it from the list 88908 basePath.newPos++; 88909 self.pushComponent(basePath.components, true, undefined); 88910 } 88911 88912 oldPos = self.extractCommon(basePath, newArr, oldArr, diagonalPath); 88913 88914 // If we have hit the end of both strings, then we are done 88915 if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { 88916 return buildValues(self, basePath.components, newArr, oldArr); 88917 } 88918 else { 88919 // Otherwise track this path as a potential candidate and continue. 88920 bestPath[diagonalPath] = basePath; 88921 } 88922 } 88923 88924 editLength++; 88925 } 88926 88927 while (editLength <= maxEditLength) { 88928 var ret = execEditLength(); 88929 if (ret) { 88930 return ret; 88931 } 88932 } 88933 }, 88934 88935 pushComponent: function (components, added, removed) { 88936 var last = components[components.length - 1]; 88937 if (last && last.added === added && last.removed === removed) { 88938 // We need to clone here as the component clone operation is just 88939 // as shallow array clone 88940 components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; 88941 } 88942 else { 88943 components.push({count: 1, added: added, removed: removed }); 88944 } 88945 }, 88946 extractCommon: function (basePath, newArr, oldArr, diagonalPath) { 88947 var newLen = newArr.length; 88948 var oldLen = oldArr.length; 88949 var newPos = basePath.newPos; 88950 var oldPos = newPos - diagonalPath; 88951 var commonCount = 0; 88952 88953 while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newArr[newPos + 1], oldArr[oldPos + 1])) { 88954 newPos++; 88955 oldPos++; 88956 commonCount++; 88957 } 88958 88959 if (commonCount) { 88960 basePath.components.push({count: commonCount}); 88961 } 88962 88963 basePath.newPos = newPos; 88964 return oldPos; 88965 }, 88966 tokenize: function (value) { 88967 return value.slice(); 88968 }, 88969 join: function (value) { 88970 return value.slice(); 88971 } 88972 }; 88973 88974 function buildValues(diff, components, newArr, oldArr) { 88975 var componentPos = 0; 88976 var componentLen = components.length; 88977 var newPos = 0; 88978 var oldPos = 0; 88979 88980 for (; componentPos < componentLen; componentPos++) { 88981 var component = components[componentPos]; 88982 if (!component.removed) { 88983 var indices = []; 88984 for (var i = newPos; i < newPos + component.count; i++) { 88985 indices.push(i); 88986 } 88987 component.indices = indices; 88988 newPos += component.count; 88989 // Common case 88990 if (!component.added) { 88991 oldPos += component.count; 88992 } 88993 } 88994 else { 88995 var indices = []; 88996 for (var i = oldPos; i < oldPos + component.count; i++) { 88997 indices.push(i); 88998 } 88999 component.indices = indices; 89000 oldPos += component.count; 89001 } 89002 } 89003 89004 return components; 89005 } 89006 89007 function clonePath(path) { 89008 return { newPos: path.newPos, components: path.components.slice(0) }; 89009 } 89010 89011 var arrayDiff = new Diff(); 89012 89013 var arrayDiff$1 = function (oldArr, newArr, callback) { 89014 return arrayDiff.diff(oldArr, newArr, callback); 89015 }; 89016 89017 /** 89018 * @file Manages elements that can be defined in <defs> in SVG, 89019 * e.g., gradients, clip path, etc. 89020 * @author Zhang Wenli 89021 */ 89022 89023 var MARK_UNUSED = '0'; 89024 var MARK_USED = '1'; 89025 89026 /** 89027 * Manages elements that can be defined in <defs> in SVG, 89028 * e.g., gradients, clip path, etc. 89029 * 89030 * @class 89031 * @param {number} zrId zrender instance id 89032 * @param {SVGElement} svgRoot root of SVG document 89033 * @param {string|string[]} tagNames possible tag names 89034 * @param {string} markLabel label name to make if the element 89035 * is used 89036 */ 89037 function Definable( 89038 zrId, 89039 svgRoot, 89040 tagNames, 89041 markLabel, 89042 domName 89043 ) { 89044 this._zrId = zrId; 89045 this._svgRoot = svgRoot; 89046 this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames; 89047 this._markLabel = markLabel; 89048 this._domName = domName || '_dom'; 89049 89050 this.nextId = 0; 89051 } 89052 89053 89054 Definable.prototype.createElement = createElement; 89055 89056 89057 /** 89058 * Get the <defs> tag for svgRoot; optionally creates one if not exists. 89059 * 89060 * @param {boolean} isForceCreating if need to create when not exists 89061 * @return {SVGDefsElement} SVG <defs> element, null if it doesn't 89062 * exist and isForceCreating is false 89063 */ 89064 Definable.prototype.getDefs = function (isForceCreating) { 89065 var svgRoot = this._svgRoot; 89066 var defs = this._svgRoot.getElementsByTagName('defs'); 89067 if (defs.length === 0) { 89068 // Not exist 89069 if (isForceCreating) { 89070 defs = svgRoot.insertBefore( 89071 this.createElement('defs'), // Create new tag 89072 svgRoot.firstChild // Insert in the front of svg 89073 ); 89074 if (!defs.contains) { 89075 // IE doesn't support contains method 89076 defs.contains = function (el) {
89077 var children = defs.children; 89078 if (!children) { 89079 return false; 89080 } 89081 for (var i = children.length - 1; i >= 0; --i) { 89082 if (children[i] === el) { 89083 return true; 89084 } 89085 } 89086 return false; 89087 }; 89088 } 89089 return defs; 89090 } 89091 else { 89092 return null; 89093 } 89094 } 89095 else { 89096 return defs[0]; 89097 } 89098 }; 89099 89100 89101 /** 89102 * Update DOM element if necessary. 89103 * 89104 * @param {Object|string} element style element. e.g., for gradient, 89105 * it may be '#ccc' or {type: 'linear', ...} 89106 * @param {Function|undefined} onUpdate update callback 89107 */ 89108 Definable.prototype.update = function (element, onUpdate) { 89109 if (!element) { 89110 return; 89111 } 89112 89113 var defs = this.getDefs(false); 89114 if (element[this._domName] && defs.contains(element[this._domName])) { 89115 // Update DOM 89116 if (typeof onUpdate === 'function') { 89117 onUpdate(element); 89118 } 89119 } 89120 else { 89121 // No previous dom, create new 89122 var dom = this.add(element); 89123 if (dom) { 89124 element[this._domName] = dom; 89125 } 89126 } 89127 }; 89128 89129 89130 /** 89131 * Add gradient dom to defs 89132 * 89133 * @param {SVGElement} dom DOM to be added to <defs> 89134 */ 89135 Definable.prototype.addDom = function (dom) { 89136 var defs = this.getDefs(true); 89137 defs.appendChild(dom); 89138 }; 89139 89140 89141 /** 89142 * Remove DOM of a given element. 89143 * 89144 * @param {SVGElement} element element to remove dom 89145 */ 89146 Definable.prototype.removeDom = function (element) { 89147 var defs = this.getDefs(false); 89148 if (defs && element[this._domName]) { 89149 defs.removeChild(element[this._domName]); 89150 element[this._domName] = null; 89151 } 89152 }; 89153 89154 89155 /** 89156 * Get DOMs of this element. 89157 * 89158 * @return {HTMLDomElement} doms of this defineable elements in <defs> 89159 */ 89160 Definable.prototype.getDoms = function () { 89161 var defs = this.getDefs(false); 89162 if (!defs) { 89163 // No dom when defs is not defined 89164 return []; 89165 } 89166 89167 var doms = []; 89168 each$1(this._tagNames, function (tagName) { 89169 var tags = defs.getElementsByTagName(tagName); 89170 // Note that tags is HTMLCollection, which is array-like 89171 // rather than real array. 89172 // So `doms.concat(tags)` add tags as one object. 89173 doms = doms.concat([].slice.call(tags)); 89174 }); 89175 89176 return doms; 89177 }; 89178 89179 89180 /** 89181 * Mark DOMs to be unused before painting, and clear unused ones at the end 89182 * of the painting. 89183 */ 89184 Definable.prototype.markAllUnused = function () { 89185 var doms = this.getDoms(); 89186 var that = this; 89187 each$1(doms, function (dom) { 89188 dom[that._markLabel] = MARK_UNUSED; 89189 }); 89190 }; 89191 89192 89193 /** 89194 * Mark a single DOM to be used. 89195 * 89196 * @param {SVGElement} dom DOM to mark 89197 */ 89198 Definable.prototype.markUsed = function (dom) { 89199 if (dom) { 89200 dom[this._markLabel] = MARK_USED; 89201 } 89202 }; 89203 89204 89205 /** 89206 * Remove unused DOMs defined in <defs> 89207 */ 89208 Definable.prototype.removeUnused = function () { 89209 var defs = this.getDefs(false); 89210 if (!defs) { 89211 // Nothing to remove 89212 return; 89213 } 89214 89215 var doms = this.getDoms(); 89216 var that = this; 89217 each$1(doms, function (dom) { 89218 if (dom[that._markLabel] !== MARK_USED) { 89219 // Remove gradient 89220 defs.removeChild(dom); 89221 } 89222 }); 89223 }; 89224 89225 89226 /** 89227 * Get SVG proxy. 89228 * 89229 * @param {Displayable} displayable displayable element 89230 * @return {Path|Image|Text} svg proxy of given element 89231 */ 89232 Definable.prototype.getSvgProxy = function (displayable) { 89233 if (displayable instanceof Path) { 89234 return svgPath; 89235 } 89236 else if (displayable instanceof ZImage) { 89237 return svgImage; 89238 } 89239 else if (displayable instanceof Text) { 89240 return svgText; 89241 } 89242 else { 89243 return svgPath; 89244 } 89245 }; 89246 89247 89248 /** 89249 * Get text SVG element. 89250 * 89251 * @param {Displayable} displayable displayable element 89252 * @return {SVGElement} SVG element of text 89253 */ 89254 Definable.prototype.getTextSvgElement = function (displayable) {
89255 return displayable.__textSvgEl; 89256 }; 89257 89258 89259 /** 89260 * Get SVG element. 89261 * 89262 * @param {Displayable} displayable displayable element 89263 * @return {SVGElement} SVG element 89264 */ 89265 Definable.prototype.getSvgElement = function (displayable) { 89266 return displayable.__svgEl; 89267 }; 89268 89269 /** 89270 * @file Manages SVG gradient elements. 89271 * @author Zhang Wenli 89272 */ 89273 89274 /** 89275 * Manages SVG gradient elements. 89276 * 89277 * @class 89278 * @extends Definable 89279 * @param {number} zrId zrender instance id 89280 * @param {SVGElement} svgRoot root of SVG document 89281 */ 89282 function GradientManager(zrId, svgRoot) { 89283 Definable.call( 89284 this, 89285 zrId, 89286 svgRoot, 89287 ['linearGradient', 'radialGradient'], 89288 '__gradient_in_use__' 89289 ); 89290 } 89291 89292 89293 inherits(GradientManager, Definable); 89294 89295 89296 /** 89297 * Create new gradient DOM for fill or stroke if not exist, 89298 * but will not update gradient if exists. 89299 * 89300 * @param {SvgElement} svgElement SVG element to paint 89301 * @param {Displayable} displayable zrender displayable element 89302 */ 89303 GradientManager.prototype.addWithoutUpdate = function ( 89304 svgElement, 89305 displayable 89306 ) { 89307 if (displayable && displayable.style) { 89308 var that = this; 89309 each$1(['fill', 'stroke'], function (fillOrStroke) { 89310 if (displayable.style[fillOrStroke] 89311 && (displayable.style[fillOrStroke].type === 'linear' 89312 || displayable.style[fillOrStroke].type === 'radial') 89313 ) { 89314 var gradient = displayable.style[fillOrStroke]; 89315 var defs = that.getDefs(true); 89316 89317 // Create dom in <defs> if not exists 89318 var dom; 89319 if (gradient._dom) { 89320 // Gradient exists 89321 dom = gradient._dom; 89322 if (!defs.contains(gradient._dom)) { 89323 // _dom is no longer in defs, recreate 89324 that.addDom(dom); 89325 } 89326 } 89327 else { 89328 // New dom 89329 dom = that.add(gradient); 89330 } 89331 89332 that.markUsed(displayable); 89333 89334 var id = dom.getAttribute('id'); 89335 svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')'); 89336 } 89337 }); 89338 } 89339 }; 89340 89341 89342 /** 89343 * Add a new gradient tag in <defs> 89344 * 89345 * @param {Gradient} gradient zr gradient instance 89346 * @return {SVGLinearGradientElement | SVGRadialGradientElement} 89347 * created DOM 89348 */ 89349 GradientManager.prototype.add = function (gradient) { 89350 var dom; 89351 if (gradient.type === 'linear') { 89352 dom = this.createElement('linearGradient'); 89353 } 89354 else if (gradient.type === 'radial') { 89355 dom = this.createElement('radialGradient'); 89356 } 89357 else { 89358 zrLog('Illegal gradient type.'); 89359 return null; 89360 } 89361 89362 // Set dom id with gradient id, since each gradient instance 89363 // will have no more than one dom element. 89364 // id may exists before for those dirty elements, in which case 89365 // id should remain the same, and other attributes should be 89366 // updated. 89367 gradient.id = gradient.id || this.nextId++; 89368 dom.setAttribute('id', 'zr' + this._zrId 89369 + '-gradient-' + gradient.id); 89370 89371 this.updateDom(gradient, dom); 89372 this.addDom(dom); 89373 89374 return dom; 89375 }; 89376 89377 89378 /** 89379 * Update gradient. 89380 * 89381 * @param {Gradient} gradient zr gradient instance 89382 */ 89383 GradientManager.prototype.update = function (gradient) { 89384 var that = this; 89385 Definable.prototype.update.call(this, gradient, function () { 89386 var type = gradient.type; 89387 var tagName = gradient._dom.tagName; 89388 if (type === 'linear' && tagName === 'linearGradient' 89389 || type === 'radial' && tagName === 'radialGradient' 89390 ) { 89391 // Gradient type is not changed, update gradient 89392 that.updateDom(gradient, gradient._dom); 89393 } 89394 else { 89395 // Remove and re-create if type is changed 89396 that.removeDom(gradient); 89397 that.add(gradient); 89398 } 89399 }); 89400 }; 89401 89402 89403 /** 89404 * Update gradient dom 89405 * 89406 * @param {Gradient} gradient zr gradient instance 89407 * @param {SVGLinearGradientElement | SVGRadialGradientElement} dom 89408 * DOM to update 89409 */ 89410 GradientManager.prototype.updateDom = function (gradient, dom) { 89411 if (gradient.type === 'linear') { 89412 dom.setAttribute('x1', gradient.x); 89413 dom.setAttribute('y1', gradient.y); 89414 dom.setAttribute('x2', gradient.x2); 89415 dom.setAttribute('y2', gradient.y2); 89416 } 89417 else if (gradient.type === 'radial') { 89418 dom.setAttribute('cx', gradient.x); 89419 dom.setAttribute('cy', gradient.y); 89420 dom.setAttribute('r', gradient.r); 89421 } 89422 else {
89423 zrLog('Illegal gradient type.'); 89424 return; 89425 } 89426 89427 if (gradient.global) { 89428 // x1, x2, y1, y2 in range of 0 to canvas width or height 89429 dom.setAttribute('gradientUnits', 'userSpaceOnUse'); 89430 } 89431 else { 89432 // x1, x2, y1, y2 in range of 0 to 1 89433 dom.setAttribute('gradientUnits', 'objectBoundingBox'); 89434 } 89435 89436 // Remove color stops if exists 89437 dom.innerHTML = ''; 89438 89439 // Add color stops 89440 var colors = gradient.colorStops; 89441 for (var i = 0, len = colors.length; i < len; ++i) { 89442 var stop = this.createElement('stop'); 89443 stop.setAttribute('offset', colors[i].offset * 100 + '%'); 89444 stop.setAttribute('stop-color', colors[i].color); 89445 dom.appendChild(stop); 89446 } 89447 89448 // Store dom element in gradient, to avoid creating multiple 89449 // dom instances for the same gradient element 89450 gradient._dom = dom; 89451 }; 89452 89453 /** 89454 * Mark a single gradient to be used 89455 * 89456 * @param {Displayable} displayable displayable element 89457 */ 89458 GradientManager.prototype.markUsed = function (displayable) { 89459 if (displayable.style) { 89460 var gradient = displayable.style.fill; 89461 if (gradient && gradient._dom) { 89462 Definable.prototype.markUsed.call(this, gradient._dom); 89463 } 89464 89465 gradient = displayable.style.stroke; 89466 if (gradient && gradient._dom) { 89467 Definable.prototype.markUsed.call(this, gradient._dom); 89468 } 89469 } 89470 }; 89471 89472 /** 89473 * @file Manages SVG clipPath elements. 89474 * @author Zhang Wenli 89475 */ 89476 89477 /** 89478 * Manages SVG clipPath elements. 89479 * 89480 * @class 89481 * @extends Definable 89482 * @param {number} zrId zrender instance id 89483 * @param {SVGElement} svgRoot root of SVG document 89484 */ 89485 function ClippathManager(zrId, svgRoot) { 89486 Definable.call(this, zrId, svgRoot, 'clipPath', '__clippath_in_use__'); 89487 } 89488 89489 89490 inherits(ClippathManager, Definable); 89491 89492 89493 /** 89494 * Update clipPath. 89495 * 89496 * @param {Displayable} displayable displayable element 89497 */ 89498 ClippathManager.prototype.update = function (displayable) { 89499 var svgEl = this.getSvgElement(displayable); 89500 if (svgEl) { 89501 this.updateDom(svgEl, displayable.__clipPaths, false); 89502 } 89503 89504 var textEl = this.getTextSvgElement(displayable); 89505 if (textEl) { 89506 // Make another clipPath for text, since it's transform 89507 // matrix is not the same with svgElement 89508 this.updateDom(textEl, displayable.__clipPaths, true); 89509 } 89510 89511 this.markUsed(displayable); 89512 }; 89513 89514 89515 /** 89516 * Create an SVGElement of displayable and create a <clipPath> of its 89517 * clipPath 89518 * 89519 * @param {Displayable} parentEl parent element 89520 * @param {ClipPath[]} clipPaths clipPaths of parent element 89521 * @param {boolean} isText if parent element is Text 89522 */ 89523 ClippathManager.prototype.updateDom = function ( 89524 parentEl, 89525 clipPaths, 89526 isText 89527 ) { 89528 if (clipPaths && clipPaths.length > 0) { 89529 // Has clipPath, create <clipPath> with the first clipPath 89530 var defs = this.getDefs(true); 89531 var clipPath = clipPaths[0]; 89532 var clipPathEl; 89533 var id; 89534 89535 var dom = isText ? '_textDom' : '_dom'; 89536 89537 if (clipPath[dom]) { 89538 // Use a dom that is already in <defs> 89539 id = clipPath[dom].getAttribute('id'); 89540 clipPathEl = clipPath[dom]; 89541 89542 // Use a dom that is already in <defs> 89543 if (!defs.contains(clipPathEl)) { 89544 // This happens when set old clipPath that has 89545 // been previously removed 89546 defs.appendChild(clipPathEl); 89547 } 89548 } 89549 else { 89550 // New <clipPath>
89551 id = 'zr' + this._zrId + '-clip-' + this.nextId; 89552 ++this.nextId; 89553 clipPathEl = this.createElement('clipPath'); 89554 clipPathEl.setAttribute('id', id); 89555 defs.appendChild(clipPathEl); 89556 89557 clipPath[dom] = clipPathEl; 89558 } 89559 89560 // Build path and add to <clipPath> 89561 var svgProxy = this.getSvgProxy(clipPath); 89562 if (clipPath.transform 89563 && clipPath.parent.invTransform 89564 && !isText 89565 ) { 89566 /** 89567 * If a clipPath has a parent with transform, the transform 89568 * of parent should not be considered when setting transform 89569 * of clipPath. So we need to transform back from parent's 89570 * transform, which is done by multiplying parent's inverse 89571 * transform. 89572 */ 89573 // Store old transform 89574 var transform = Array.prototype.slice.call( 89575 clipPath.transform 89576 ); 89577 89578 // Transform back from parent, and brush path 89579 mul$1( 89580 clipPath.transform, 89581 clipPath.parent.invTransform, 89582 clipPath.transform 89583 ); 89584 svgProxy.brush(clipPath); 89585 89586 // Set back transform of clipPath 89587 clipPath.transform = transform; 89588 } 89589 else { 89590 svgProxy.brush(clipPath); 89591 } 89592 89593 var pathEl = this.getSvgElement(clipPath); 89594 89595 clipPathEl.innerHTML = ''; 89596 /** 89597 * Use `cloneNode()` here to appendChild to multiple parents, 89598 * which may happend when Text and other shapes are using the same 89599 * clipPath. Since Text will create an extra clipPath DOM due to 89600 * different transform rules. 89601 */ 89602 clipPathEl.appendChild(pathEl.cloneNode()); 89603 89604 parentEl.setAttribute('clip-path', 'url(#' + id + ')'); 89605 89606 if (clipPaths.length > 1) { 89607 // Make the other clipPaths recursively 89608 this.updateDom(clipPathEl, clipPaths.slice(1), isText); 89609 } 89610 } 89611 else { 89612 // No clipPath 89613 if (parentEl) { 89614 parentEl.setAttribute('clip-path', 'none'); 89615 } 89616 } 89617 }; 89618 89619 /** 89620 * Mark a single clipPath to be used 89621 * 89622 * @param {Displayable} displayable displayable element 89623 */ 89624 ClippathManager.prototype.markUsed = function (displayable) { 89625 var that = this; 89626 if (displayable.__clipPaths && displayable.__clipPaths.length > 0) { 89627 each$1(displayable.__clipPaths, function (clipPath) { 89628 if (clipPath._dom) { 89629 Definable.prototype.markUsed.call(that, clipPath._dom); 89630 } 89631 if (clipPath._textDom) { 89632 Definable.prototype.markUsed.call(that, clipPath._textDom); 89633 } 89634 }); 89635 } 89636 }; 89637 89638 /** 89639 * @file Manages SVG shadow elements. 89640 * @author Zhang Wenli 89641 */ 89642 89643 /** 89644 * Manages SVG shadow elements. 89645 * 89646 * @class 89647 * @extends Definable 89648 * @param {number} zrId zrender instance id 89649 * @param {SVGElement} svgRoot root of SVG document 89650 */ 89651 function ShadowManager(zrId, svgRoot) { 89652 Definable.call( 89653 this, 89654 zrId, 89655 svgRoot, 89656 ['filter'], 89657 '__filter_in_use__', 89658 '_shadowDom' 89659 ); 89660 } 89661 89662 89663 inherits(ShadowManager, Definable); 89664 89665 89666 /** 89667 * Create new shadow DOM for fill or stroke if not exist, 89668 * but will not update shadow if exists. 89669 * 89670 * @param {SvgElement} svgElement SVG element to paint 89671 * @param {Displayable} displayable zrender displayable element 89672 */ 89673 ShadowManager.prototype.addWithoutUpdate = function ( 89674 svgElement, 89675 displayable 89676 ) { 89677 if (displayable && hasShadow(displayable.style)) { 89678 var style = displayable.style; 89679 89680 // Create dom in <defs> if not exists 89681 var dom; 89682 if (style._shadowDom) { 89683 // Gradient exists 89684 dom = style._shadowDom; 89685 89686 var defs = this.getDefs(true); 89687 if (!defs.contains(style._shadowDom)) { 89688 // _shadowDom is no longer in defs, recreate 89689 this.addDom(dom); 89690 } 89691 } 89692 else { 89693 // New dom 89694 dom = this.add(displayable); 89695 } 89696 89697 this.markUsed(displayable); 89698 89699 var id = dom.getAttribute('id'); 89700 svgElement.style.filter = 'url(#' + id + ')'; 89701 } 89702 }; 89703 89704 89705 /** 89706 * Add a new shadow tag in <defs> 89707 * 89708 * @param {Displayable} displayable zrender displayable element 89709 * @return {SVGFilterElement} created DOM 89710 */ 89711 ShadowManager.prototype.add = function (displayable) { 89712 var dom = this.createElement('filter'); 89713 var style = displayable.style; 89714 89715 // Set dom id with shadow id, since each shadow instance 89716 // will have no more than one dom element. 89717 // id may exists before for those dirty elements, in which case 89718 // id should remain the same, and other attributes should be 89719 // updated. 89720 style._shadowDomId = style._shadowDomId || this.nextId++; 89721 dom.setAttribute('id', 'zr' + this._zrId 89722 + '-shadow-' + style._shadowDomId); 89723 89724 this.updateDom(displayable, dom); 89725 this.addDom(dom); 89726 89727 return dom; 89728 }; 89729 89730 89731 /** 89732 * Update shadow. 89733 * 89734 * @param {Displayable} displayable zrender displayable element 89735 */ 89736 ShadowManager.prototype.update = function (svgElement, displayable) { 89737 var style = displayable.style; 89738 if (hasShadow(style)) { 89739 var that = this; 89740 Definable.prototype.update.call(this, displayable, function (style) { 89741 that.updateDom(displayable, style._shadowDom); 89742 }); 89743 } 89744 else { 89745 // Remove shadow 89746 this.remove(svgElement, style); 89747 } 89748 }; 89749 89750 89751 /** 89752 * Remove DOM and clear parent filter 89753 */ 89754 ShadowManager.prototype.remove = function (svgElement, style) { 89755 if (style._shadowDomId != null) { 89756 this.removeDom(style); 89757 svgElement.style.filter = ''; 89758 } 89759 }; 89760 89761 89762 /** 89763 * Update shadow dom 89764 * 89765 * @param {Displayable} displayable zrender displayable element 89766 * @param {SVGFilterElement} dom DOM to update 89767 */ 89768 ShadowManager.prototype.updateDom = function (displayable, dom) { 89769 var domChild = dom.getElementsByTagName('feDropShadow'); 89770 if (domChild.length === 0) { 89771 domChild = this.createElement('feDropShadow'); 89772 } 89773 else { 89774 domChild = domChild[0]; 89775 } 89776 89777 var style = displayable.style; 89778 var scaleX = displayable.scale ? (displayable.scale[0] || 1) : 1; 89779 var scaleY = displayable.scale ? (displayable.scale[1] || 1) : 1; 89780 89781 // TODO: textBoxShadowBlur is not supported yet 89782 var offsetX, offsetY, blur, color; 89783 if (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY) { 89784 offsetX = style.shadowOffsetX || 0; 89785 offsetY = style.shadowOffsetY || 0; 89786 blur = style.shadowBlur; 89787 color = style.shadowColor; 89788 } 89789 else if (style.textShadowBlur) { 89790 offsetX = style.textShadowOffsetX || 0; 89791 offsetY = style.textShadowOffsetY || 0; 89792 blur = style.textShadowBlur; 89793 color = style.textShadowColor; 89794 } 89795 else { 89796 // Remove shadow 89797 this.removeDom(dom, style); 89798 return; 89799 } 89800 89801 domChild.setAttribute('dx', offsetX / scaleX); 89802 domChild.setAttribute('dy', offsetY / scaleY); 89803 domChild.setAttribute('flood-color', color); 89804 89805 // Divide by two here so that it looks the same as in canvas 89806 // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur 89807 var stdDx = blur / 2 / scaleX; 89808 var stdDy = blur / 2 / scaleY; 89809 var stdDeviation = stdDx + ' ' + stdDy; 89810 domChild.setAttribute('stdDeviation', stdDeviation); 89811 89812 // Fix filter clipping problem 89813 dom.setAttribute('x', '-100%'); 89814 dom.setAttribute('y', '-100%'); 89815 dom.setAttribute('width', Math.ceil(blur / 2 * 200) + '%'); 89816 dom.setAttribute('height', Math.ceil(blur / 2 * 200) + '%'); 89817 89818 dom.appendChild(domChild); 89819 89820 // Store dom element in shadow, to avoid creating multiple 89821 // dom instances for the same shadow element 89822 style._shadowDom = dom; 89823 }; 89824 89825 /** 89826 * Mark a single shadow to be used 89827 * 89828 * @param {Displayable} displayable displayable element 89829 */ 89830 ShadowManager.prototype.markUsed = function (displayable) { 89831 var style = displayable.style; 89832 if (style && style._shadowDom) { 89833 Definable.prototype.markUsed.call(this, style._shadowDom); 89834 } 89835 }; 89836 89837 function hasShadow(style) { 89838 // TODO: textBoxShadowBlur is not supported yet 89839 return style 89840 && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY 89841 || style.textShadowBlur || style.textShadowOffsetX 89842 || style.textShadowOffsetY); 89843 } 89844 89845 /** 89846 * SVG Painter 89847 * @module zrender/svg/Painter 89848 */ 89849 89850 function parseInt10$2(val) { 89851 return parseInt(val, 10); 89852 } 89853 89854 function getSvgProxy(el) { 89855 if (el instanceof Path) { 89856 return svgPath; 89857 } 89858 else if (el instanceof ZImage) { 89859 return svgImage; 89860 } 89861 else if (el instanceof Text) { 89862 return svgText; 89863 } 89864 else { 89865 return svgPath; 89866 } 89867 } 89868 89869 function checkParentAvailable(parent, child) { 89870 return child && parent && child.parentNode !== parent; 89871 } 89872 89873 function insertAfter(parent, child, prevSibling) { 89874 if (checkParentAvailable(parent, child) && prevSibling) { 89875 var nextSibling = prevSibling.nextSibling; 89876 nextSibling ? parent.insertBefore(child, nextSibling) 89877 : parent.appendChild(child); 89878 } 89879 } 89880 89881 function prepend(parent, child) { 89882 if (checkParentAvailable(parent, child)) { 89883 var firstChild = parent.firstChild; 89884 firstChild ? parent.insertBefore(child, firstChild) 89885 : parent.appendChild(child); 89886 } 89887 } 89888 89889 function remove$1(parent, child) { 89890 if (child && parent && child.parentNode === parent) { 89891 parent.removeChild(child); 89892 } 89893 } 89894 89895 function getTextSvgElement(displayable) {
89896 return displayable.__textSvgEl; 89897 } 89898 89899 function getSvgElement(displayable) { 89900 return displayable.__svgEl; 89901 } 89902 89903 /** 89904 * @alias module:zrender/svg/Painter 89905 * @constructor 89906 * @param {HTMLElement} root ç»ËÃ¥âºÂ¾Ã¥Â®Â¹Ã¥â¢Â¨ 89907 * @param {module:zrender/Storage} storage 89908 * @param {Object} opts 89909 */ 89910 var SVGPainter = function (root, storage, opts, zrId) { 89911 89912 this.root = root; 89913 this.storage = storage; 89914 this._opts = opts = extend({}, opts || {}); 89915 89916 var svgRoot = createElement('svg'); 89917 svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 89918 svgRoot.setAttribute('version', '1.1'); 89919 svgRoot.setAttribute('baseProfile', 'full'); 89920 svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;'; 89921 89922 this.gradientManager = new GradientManager(zrId, svgRoot); 89923 this.clipPathManager = new ClippathManager(zrId, svgRoot); 89924 this.shadowManager = new ShadowManager(zrId, svgRoot); 89925 89926 var viewport = document.createElement('div'); 89927 viewport.style.cssText = 'overflow:hidden;position:relative'; 89928 89929 this._svgRoot = svgRoot; 89930 this._viewport = viewport; 89931 89932 root.appendChild(viewport); 89933 viewport.appendChild(svgRoot); 89934 89935 this.resize(opts.width, opts.height); 89936 89937 this._visibleList = []; 89938 }; 89939 89940 SVGPainter.prototype = { 89941 89942 constructor: SVGPainter, 89943 89944 getType: function () { 89945 return 'svg'; 89946 }, 89947 89948 getViewportRoot: function () { 89949 return this._viewport; 89950 }, 89951 89952 getViewportRootOffset: function () { 89953 var viewportRoot = this.getViewportRoot(); 89954 if (viewportRoot) { 89955 return { 89956 offsetLeft: viewportRoot.offsetLeft || 0, 89957 offsetTop: viewportRoot.offsetTop || 0 89958 }; 89959 } 89960 }, 89961 89962 refresh: function () { 89963 89964 var list = this.storage.getDisplayList(true); 89965 89966 this._paintList(list); 89967 }, 89968 89969 setBackgroundColor: function (backgroundColor) { 89970 // TODO gradient 89971 this._viewport.style.background = backgroundColor; 89972 }, 89973 89974 _paintList: function (list) { 89975 this.gradientManager.markAllUnused(); 89976 this.clipPathManager.markAllUnused(); 89977 this.shadowManager.markAllUnused(); 89978 89979 var svgRoot = this._svgRoot; 89980 var visibleList = this._visibleList; 89981 var listLen = list.length; 89982 89983 var newVisibleList = []; 89984 var i; 89985 for (i = 0; i < listLen; i++) { 89986 var displayable = list[i]; 89987 var svgProxy = getSvgProxy(displayable); 89988 var svgElement = getSvgElement(displayable) 89989 || getTextSvgElement(displayable); 89990 if (!displayable.invisible) { 89991 if (displayable.__dirty) { 89992 svgProxy && svgProxy.brush(displayable); 89993 89994 // Update clipPath 89995 this.clipPathManager.update(displayable); 89996 89997 // Update gradient and shadow 89998 if (displayable.style) { 89999 this.gradientManager 90000 .update(displayable.style.fill); 90001 this.gradientManager 90002 .update(displayable.style.stroke); 90003 90004 this.shadowManager 90005 .update(svgElement, displayable); 90006 } 90007 90008 displayable.__dirty = false; 90009 } 90010 newVisibleList.push(displayable); 90011 } 90012 } 90013 90014 var diff = arrayDiff$1(visibleList, newVisibleList); 90015 var prevSvgElement; 90016 90017 // First do remove, in case element moved to the head and do remove 90018 // after add 90019 for (i = 0; i < diff.length; i++) { 90020 var item = diff[i]; 90021 if (item.removed) { 90022 for (var k = 0; k < item.count;
90022 k++) { 90023 var displayable = visibleList[item.indices[k]]; 90024 var svgElement = getSvgElement(displayable); 90025 var textSvgElement = getTextSvgElement(displayable); 90026 remove$1(svgRoot, svgElement); 90027 remove$1(svgRoot, textSvgElement); 90028 } 90029 } 90030 } 90031 for (i = 0; i < diff.length; i++) { 90032 var item = diff[i]; 90033 if (item.added) { 90034 for (var k = 0; k < item.count; k++) { 90035 var displayable = newVisibleList[item.indices[k]]; 90036 var svgElement = getSvgElement(displayable); 90037 var textSvgElement = getTextSvgElement(displayable); 90038 prevSvgElement 90039 ? insertAfter(svgRoot, svgElement, prevSvgElement) 90040 : prepend(svgRoot, svgElement); 90041 if (svgElement) { 90042 insertAfter(svgRoot, textSvgElement, svgElement); 90043 } 90044 else if (prevSvgElement) { 90045 insertAfter( 90046 svgRoot, textSvgElement, prevSvgElement 90047 ); 90048 } 90049 else { 90050 prepend(svgRoot, textSvgElement); 90051 } 90052 // Insert text 90053 insertAfter(svgRoot, textSvgElement, svgElement); 90054 prevSvgElement = textSvgElement || svgElement 90055 || prevSvgElement; 90056 90057 this.gradientManager 90058 .addWithoutUpdate(svgElement, displayable); 90059 this.shadowManager 90060 .addWithoutUpdate(prevSvgElement, displayable); 90061 this.clipPathManager.markUsed(displayable); 90062 } 90063 } 90064 else if (!item.removed) { 90065 for (var k = 0; k < item.count; k++) { 90066 var displayable = newVisibleList[item.indices[k]]; 90067 prevSvgElement 90068 = svgElement 90069 = getTextSvgElement(displayable) 90070 || getSvgElement(displayable) 90071 || prevSvgElement; 90072 90073 this.gradientManager.markUsed(displayable); 90074 this.gradientManager 90075 .addWithoutUpdate(svgElement, displayable); 90076 90077 this.shadowManager.markUsed(displayable); 90078 this.shadowManager 90079 .addWithoutUpdate(svgElement, displayable); 90080 90081 this.clipPathManager.markUsed(displayable); 90082 } 90083 } 90084 } 90085 90086 this.gradientManager.removeUnused(); 90087 this.clipPathManager.removeUnused(); 90088 this.shadowManager.removeUnused(); 90089 90090 this._visibleList = newVisibleList; 90091 }, 90092 90093 _getDefs: function (isForceCreating) { 90094 var svgRoot = this._svgRoot; 90095 var defs = this._svgRoot.getElementsByTagName('defs'); 90096 if (defs.length === 0) { 90097 // Not exist 90098 if (isForceCreating) { 90099 var defs = svgRoot.insertBefore( 90100 createElement('defs'), // Create new tag 90101 svgRoot.firstChild // Insert in the front of svg 90102 ); 90103 if (!defs.contains) { 90104 // IE doesn't support contains method 90105 defs.contains = function (el) { 90106 var children = defs.children; 90107 if (!children) { 90108 return false; 90109 } 90110 for (var i = children.length - 1; i >= 0; --i) { 90111 if (children[i] === el) { 90112 return true; 90113 } 90114 } 90115 return false; 90116 }; 90117 } 90118 return defs; 90119 } 90120 else { 90121 return null; 90122 } 90123 } 90124 else { 90125 return defs[0]; 90126 } 90127 }, 90128 90129 resize: function (width, height) { 90130 var viewport = this._viewport; 90131 // FIXME Why ? 90132 viewport.style.display = 'none'; 90133 90134 // Save input w/h 90135 var opts = this._opts; 90136 width != null && (opts.width = width); 90137 height != null && (opts.height = height); 90138 90139 width = this._getSize(0); 90140 height = this._getSize(1); 90141 90142 viewport.style.display = ''; 90143 90144 if (this._width !== width || this._height !== height) { 90145 this._width = width; 90146 this._height = height; 90147 90148 var viewportStyle = viewport.style; 90149 viewportStyle.width = width + 'px'; 90150 viewportStyle.height = height + 'px'; 90151 90152 var svgRoot = this._svgRoot; 90153 // Set width by 'svgRoot.width = width' is invali
90153d 90154 svgRoot.setAttribute('width', width); 90155 svgRoot.setAttribute('height', height); 90156 } 90157 }, 90158 90159 /** 90160 * 获åÂâç»ËÃ¥âºÂ¾Ã¥ÅºåŸŸå®½åº¦ 90161 */ 90162 getWidth: function () { 90163 return this._width; 90164 }, 90165 90166 /** 90167 * 获åÂâç»ËÃ¥âºÂ¾Ã¥ÅºåŸŸé«Ë度 90168 */ 90169 getHeight: function () { 90170 return this._height; 90171 }, 90172 90173 _getSize: function (whIdx) { 90174 var opts = this._opts; 90175 var wh = ['width', 'height'][whIdx]; 90176 var cwh = ['clientWidth', 'clientHeight'][whIdx]; 90177 var plt = ['paddingLeft', 'paddingTop'][whIdx]; 90178 var prb = ['paddingRight', 'paddingBottom'][whIdx]; 90179 90180 if (opts[wh] != null && opts[wh] !== 'auto') { 90181 return parseFloat(opts[wh]); 90182 } 90183 90184 var root = this.root; 90185 // IE8 does not support getComputedStyle, but it use VML. 90186 var stl = document.defaultView.getComputedStyle(root); 90187 90188 return ( 90189 (root[cwh] || parseInt10$2(stl[wh]) || parseInt10$2(root.style[wh])) 90190 - (parseInt10$2(stl[plt]) || 0) 90191 - (parseInt10$2(stl[prb]) || 0) 90192 ) | 0; 90193 }, 90194 90195 dispose: function () { 90196 this.root.innerHTML = ''; 90197 90198 this._svgRoot 90199 = this._viewport 90200 = this.storage 90201 = null; 90202 }, 90203 90204 clear: function () { 90205 if (this._viewport) { 90206 this.root.removeChild(this._viewport); 90207 } 90208 }, 90209 90210 pathToDataUrl: function () { 90211 this.refresh(); 90212 var html = this._svgRoot.outerHTML; 90213 return 'data:image/svg+xml;charset=UTF-8,' + html; 90214 } 90215 }; 90216 90217// Not supported methods 90218 function createMethodNotSupport$1(method) { 90219 return function () { 90220 zrLog('In SVG mode painter not support method "' + method + '"'); 90221 }; 90222 } 90223 90224// Unsuppoted methods 90225 each$1([ 90226 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 90227 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer', 90228 'toDataURL', 'pathToImage' 90229 ], function (name) { 90230 SVGPainter.prototype[name] = createMethodNotSupport$1(name); 90231 }); 90232 90233 registerPainter('svg', SVGPainter); 90234 90235 /* 90236* Licensed to the Apache Software Foundation (ASF) under one 90237* or more contributor license agreements. See the NOTICE file 90238* distributed with this work for additional information 90239* regarding copyright ownership. The ASF licenses this file 90240* to you under the Apache License, Version 2.0 (the 90241* "License"); you may not use this file except in compliance 90242* with the License. You may obtain a copy of the License at 90243* 90244* http://www.apache.org/licenses/LICENSE-2.0 90245* 90246* Unless required by applicable law or agreed to in writing, 90247* software distributed under the License is distributed on an 90248* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 90249* KIND, either express or implied. See the License for the 90250* specific language governing permissions and limitations 90251* under the License. 90252*/ 90253 90254// Import all charts and components 90255 90256 exports.version = version; 90257 exports.dependencies = dependencies; 90258 exports.PRIORITY = PRIORITY; 90259 exports.init = init; 90260 exports.connect = connect; 90261 exports.disConnect = disConnect; 90262 exports.disconnect = disconnect; 90263 exports.dispose = dispose; 90264 exports.getInstanceByDom = getInstanceByDom; 90265 exports.getInstanceById = getInstanceById; 90266 exports.registerTheme = registerTheme; 90267 exports.registerPreprocessor = registerPreprocessor; 90268 exports.registerProcessor = registerProcessor; 90269 exports.registerPostUpdate = registerPostUpdate; 90270 exports.registerAction = registerAction; 90271 exports.registerCoordinateSystem = registerCoordinateSystem; 90272 exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions; 90273 exports.registerLayout = registerLayout; 90274 exports.registerVisual = registerVisual; 90275 exports.registerLoading = registerLoading; 90276 exports.extendComponentModel = extendComponentModel; 90277 exports.extendComponentView = extendComponentView; 90278 exports.extendSeriesModel = extendSeriesModel; 90279 exports.extendChartView = extendChartView; 90280 exports.setCanvasCreator = setCanvasCreator; 90281 exports.registerMap = registerMap; 90282 exports.getMap = getMap;
90283 exports.dataTool = dataTool; 90284 exports.zrender = zrender; 90285 exports.graphic = graphic; 90286 exports.number = number; 90287 exports.format = format; 90288 exports.throttle = throttle; 90289 exports.helper = helper; 90290 exports.matrix = matrix; 90291 exports.vector = vector; 90292 exports.color = color; 90293 exports.parseGeoJSON = parseGeoJson$1; 90294 exports.parseGeoJson = parseGeoJson; 90295 exports.util = ecUtil; 90296 exports.List = List; 90297 exports.Model = Model; 90298 exports.Axis = Axis; 90299 exports.env = env$1; 90300 90301}))); 90302//# sourceMappingURL=echarts.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.