CustomView.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { __extends } from "tslib";
  41. import { hasOwn, assert, isString, retrieve2, retrieve3, defaults, each, indexOf, map } from 'zrender/lib/core/util.js';
  42. import * as graphicUtil from '../../util/graphic.js';
  43. import { setDefaultStateProxy, toggleHoverEmphasis } from '../../util/states.js';
  44. import * as labelStyleHelper from '../../label/labelStyle.js';
  45. import { getDefaultLabel } from '../helper/labelHelper.js';
  46. import { getLayoutOnAxis } from '../../layout/barGrid.js';
  47. import DataDiffer from '../../data/DataDiffer.js';
  48. import ChartView from '../../view/Chart.js';
  49. import { createClipPath } from '../helper/createClipPathFromCoordSys.js';
  50. import prepareCartesian2d from '../../coord/cartesian/prepareCustom.js';
  51. import prepareGeo from '../../coord/geo/prepareCustom.js';
  52. import prepareSingleAxis from '../../coord/single/prepareCustom.js';
  53. import preparePolar from '../../coord/polar/prepareCustom.js';
  54. import prepareCalendar from '../../coord/calendar/prepareCustom.js';
  55. import prepareMatrix from '../../coord/matrix/prepareCustom.js';
  56. import Displayable from 'zrender/lib/graphic/Displayable.js';
  57. import { convertToEC4StyleForCustomSerise, isEC4CompatibleStyle, convertFromEC4CompatibleStyle, warnDeprecated } from '../../util/styleCompat.js';
  58. import { throwError } from '../../util/log.js';
  59. import { createOrUpdatePatternFromDecal } from '../../util/decal.js';
  60. import { STYLE_VISUAL_TYPE, NON_STYLE_VISUAL_PROPS, customInnerStore } from './CustomSeries.js';
  61. import { applyLeaveTransition, applyUpdateTransition } from '../../animation/customGraphicTransition.js';
  62. import { applyKeyframeAnimation, stopPreviousKeyframeAnimationAndRestore } from '../../animation/customGraphicKeyframeAnimation.js';
  63. import { getCustomSeries } from './customSeriesRegister.js';
  64. import tokens from '../../visual/tokens.js';
  65. var EMPHASIS = 'emphasis';
  66. var NORMAL = 'normal';
  67. var BLUR = 'blur';
  68. var SELECT = 'select';
  69. var STATES = [NORMAL, EMPHASIS, BLUR, SELECT];
  70. var PATH_ITEM_STYLE = {
  71. normal: ['itemStyle'],
  72. emphasis: [EMPHASIS, 'itemStyle'],
  73. blur: [BLUR, 'itemStyle'],
  74. select: [SELECT, 'itemStyle']
  75. };
  76. var PATH_LABEL = {
  77. normal: ['label'],
  78. emphasis: [EMPHASIS, 'label'],
  79. blur: [BLUR, 'label'],
  80. select: [SELECT, 'label']
  81. };
  82. var DEFAULT_TRANSITION = ['x', 'y'];
  83. // Use prefix to avoid index to be the same as el.name,
  84. // which will cause weird update animation.
  85. var GROUP_DIFF_PREFIX = 'e\0\0';
  86. var attachedTxInfoTmp = {
  87. normal: {},
  88. emphasis: {},
  89. blur: {},
  90. select: {}
  91. };
  92. /**
  93. * FIXME: register rather than import directly, for size.
  94. *
  95. * To reduce total package size of each coordinate systems, the modules `prepareCustom`
  96. * of each coordinate systems are not required by each coordinate systems directly, but
  97. * required by the module `custom`.
  98. *
  99. * prepareInfoForCustomSeries {Function}: optional
  100. * @return {Object} {coordSys: {...}, api: {
  101. * coord: function (data, clamp) {}, // return point in global.
  102. * size: function (dataSize, dataItem) {} // return size of each axis in coordSys.
  103. * }}
  104. */
  105. var prepareCustoms = {
  106. cartesian2d: prepareCartesian2d,
  107. geo: prepareGeo,
  108. single: prepareSingleAxis,
  109. polar: preparePolar,
  110. calendar: prepareCalendar,
  111. matrix: prepareMatrix
  112. };
  113. function isPath(el) {
  114. return el instanceof graphicUtil.Path;
  115. }
  116. function isDisplayable(el) {
  117. return el instanceof Displayable;
  118. }
  119. function copyElement(sourceEl, targetEl) {
  120. targetEl.copyTransform(sourceEl);
  121. if (isDisplayable(targetEl) && isDisplayable(sourceEl)) {
  122. targetEl.setStyle(sourceEl.style);
  123. targetEl.z = sourceEl.z;
  124. targetEl.z2 = sourceEl.z2;
  125. targetEl.zlevel = sourceEl.zlevel;
  126. targetEl.invisible = sourceEl.invisible;
  127. targetEl.ignore = sourceEl.ignore;
  128. if (isPath(targetEl) && isPath(sourceEl)) {
  129. targetEl.setShape(sourceEl.shape);
  130. }
  131. }
  132. }
  133. var CustomChartView = /** @class */function (_super) {
  134. __extends(CustomChartView, _super);
  135. function CustomChartView() {
  136. var _this = _super !== null && _super.apply(this, arguments) || this;
  137. _this.type = CustomChartView.type;
  138. return _this;
  139. }
  140. CustomChartView.prototype.render = function (customSeries, ecModel, api, payload) {
  141. // Clear previously rendered progressive elements.
  142. this._progressiveEls = null;
  143. var oldData = this._data;
  144. var data = customSeries.getData();
  145. var group = this.group;
  146. var renderItem = makeRenderItem(customSeries, data, ecModel, api);
  147. if (!oldData) {
  148. // Previous render is incremental render or first render.
  149. // Needs remove the incremental rendered elements.
  150. group.removeAll();
  151. }
  152. data.diff(oldData).add(function (newIdx) {
  153. createOrUpdateItem(api, null, newIdx, renderItem(newIdx, payload), customSeries, group, data);
  154. }).remove(function (oldIdx) {
  155. var el = oldData.getItemGraphicEl(oldIdx);
  156. el && applyLeaveTransition(el, customInnerStore(el).option, customSeries);
  157. }).update(function (newIdx, oldIdx) {
  158. var oldEl = oldData.getItemGraphicEl(oldIdx);
  159. createOrUpdateItem(api, oldEl, newIdx, renderItem(newIdx, payload), customSeries, group, data);
  160. }).execute();
  161. // Do clipping
  162. var clipPath = customSeries.get('clip', true) ? createClipPath(customSeries.coordinateSystem, false, customSeries) : null;
  163. if (clipPath) {
  164. group.setClipPath(clipPath);
  165. } else {
  166. group.removeClipPath();
  167. }
  168. this._data = data;
  169. };
  170. CustomChartView.prototype.incrementalPrepareRender = function (customSeries, ecModel, api) {
  171. this.group.removeAll();
  172. this._data = null;
  173. };
  174. CustomChartView.prototype.incrementalRender = function (params, customSeries, ecModel, api, payload) {
  175. var data = customSeries.getData();
  176. var renderItem = makeRenderItem(customSeries, data, ecModel, api);
  177. var progressiveEls = this._progressiveEls = [];
  178. function setIncrementalAndHoverLayer(el) {
  179. if (!el.isGroup) {
  180. el.incremental = true;
  181. el.ensureState('emphasis').hoverLayer = true;
  182. }
  183. }
  184. for (var idx = params.start; idx < params.end; idx++) {
  185. var el = createOrUpdateItem(null, null, idx, renderItem(idx, payload), customSeries, this.group, data);
  186. if (el) {
  187. el.traverse(setIncrementalAndHoverLayer);
  188. progressiveEls.push(el);
  189. }
  190. }
  191. };
  192. CustomChartView.prototype.eachRendered = function (cb) {
  193. graphicUtil.traverseElements(this._progressiveEls || this.group, cb);
  194. };
  195. CustomChartView.prototype.filterForExposedEvent = function (eventType, query, targetEl, packedEvent) {
  196. var elementName = query.element;
  197. if (elementName == null || targetEl.name === elementName) {
  198. return true;
  199. }
  200. // Enable to give a name on a group made by `renderItem`, and listen
  201. // events that are triggered by its descendents.
  202. while ((targetEl = targetEl.__hostTarget || targetEl.parent) && targetEl !== this.group) {
  203. if (targetEl.name === elementName) {
  204. return true;
  205. }
  206. }
  207. return false;
  208. };
  209. CustomChartView.type = 'custom';
  210. return CustomChartView;
  211. }(ChartView);
  212. export default CustomChartView;
  213. function createEl(elOption) {
  214. var graphicType = elOption.type;
  215. var el;
  216. // Those graphic elements are not shapes. They should not be
  217. // overwritten by users, so do them first.
  218. if (graphicType === 'path') {
  219. var shape = elOption.shape;
  220. // Using pathRect brings convenience to users sacle svg path.
  221. var pathRect = shape.width != null && shape.height != null ? {
  222. x: shape.x || 0,
  223. y: shape.y || 0,
  224. width: shape.width,
  225. height: shape.height
  226. } : null;
  227. var pathData = getPathData(shape);
  228. // Path is also used for icon, so layout 'center' by default.
  229. el = graphicUtil.makePath(pathData, null, pathRect, shape.layout || 'center');
  230. customInnerStore(el).customPathData = pathData;
  231. } else if (graphicType === 'image') {
  232. el = new graphicUtil.Image({});
  233. customInnerStore(el).customImagePath = elOption.style.image;
  234. } else if (graphicType === 'text') {
  235. el = new graphicUtil.Text({});
  236. // customInnerStore(el).customText = (elOption.style as TextStyleProps).text;
  237. } else if (graphicType === 'group') {
  238. el = new graphicUtil.Group();
  239. } else if (graphicType === 'compoundPath') {
  240. var shape = elOption.shape;
  241. if (!shape || !shape.paths) {
  242. var errMsg = '';
  243. if (process.env.NODE_ENV !== 'production') {
  244. errMsg = 'shape.paths must be specified in compoundPath';
  245. }
  246. throwError(errMsg);
  247. }
  248. var paths = map(shape.paths, function (path) {
  249. if (path.type === 'path') {
  250. return graphicUtil.makePath(path.shape.pathData, path, null);
  251. }
  252. var Clz = graphicUtil.getShapeClass(path.type);
  253. if (!Clz) {
  254. var errMsg = '';
  255. if (process.env.NODE_ENV !== 'production') {
  256. errMsg = 'graphic type "' + graphicType + '" can not be found.';
  257. }
  258. throwError(errMsg);
  259. }
  260. return new Clz();
  261. });
  262. el = new graphicUtil.CompoundPath({
  263. shape: {
  264. paths: paths
  265. }
  266. });
  267. } else {
  268. var Clz = graphicUtil.getShapeClass(graphicType);
  269. if (!Clz) {
  270. var errMsg = '';
  271. if (process.env.NODE_ENV !== 'production') {
  272. errMsg = 'graphic type "' + graphicType + '" can not be found.';
  273. }
  274. throwError(errMsg);
  275. }
  276. el = new Clz();
  277. }
  278. customInnerStore(el).customGraphicType = graphicType;
  279. el.name = elOption.name;
  280. // Compat ec4: the default z2 lift is 1. If changing the number,
  281. // some cases probably be broken: hierarchy layout along z, like circle packing,
  282. // where emphasis only intending to modify color/border rather than lift z2.
  283. el.z2EmphasisLift = 1;
  284. el.z2SelectLift = 1;
  285. return el;
  286. }
  287. function updateElNormal(
  288. // Can be null/undefined
  289. api, el, dataIndex, elOption, attachedTxInfo, seriesModel, isInit) {
  290. // Stop and restore before update any other attributes.
  291. stopPreviousKeyframeAnimationAndRestore(el);
  292. var txCfgOpt = attachedTxInfo && attachedTxInfo.normal.cfg;
  293. if (txCfgOpt) {
  294. // PENDING: whether use user object directly rather than clone?
  295. // TODO:5.0 textConfig transition animation?
  296. el.setTextConfig(txCfgOpt);
  297. }
  298. // Default transition ['x', 'y']
  299. if (elOption && elOption.transition == null) {
  300. elOption.transition = DEFAULT_TRANSITION;
  301. }
  302. // Do some normalization on style.
  303. var styleOpt = elOption && elOption.style;
  304. if (styleOpt) {
  305. if (el.type === 'text') {
  306. var textOptionStyle = styleOpt;
  307. // Compatible with ec4: if `textFill` or `textStroke` exists use them.
  308. hasOwn(textOptionStyle, 'textFill') && (textOptionStyle.fill = textOptionStyle.textFill);
  309. hasOwn(textOptionStyle, 'textStroke') && (textOptionStyle.stroke = textOptionStyle.textStroke);
  310. }
  311. var decalPattern = void 0;
  312. var decalObj = isPath(el) ? styleOpt.decal : null;
  313. if (api && decalObj) {
  314. decalObj.dirty = true;
  315. decalPattern = createOrUpdatePatternFromDecal(decalObj, api);
  316. }
  317. // Always overwrite in case user specify this prop.
  318. styleOpt.__decalPattern = decalPattern;
  319. }
  320. if (isDisplayable(el)) {
  321. if (styleOpt) {
  322. var decalPattern = styleOpt.__decalPattern;
  323. if (decalPattern) {
  324. styleOpt.decal = decalPattern;
  325. }
  326. }
  327. }
  328. applyUpdateTransition(el, elOption, seriesModel, {
  329. dataIndex: dataIndex,
  330. isInit: isInit,
  331. clearStyle: true
  332. });
  333. applyKeyframeAnimation(el, elOption.keyframeAnimation, seriesModel);
  334. }
  335. function updateElOnState(state, el, elStateOpt, styleOpt, attachedTxInfo) {
  336. var elDisplayable = el.isGroup ? null : el;
  337. var txCfgOpt = attachedTxInfo && attachedTxInfo[state].cfg;
  338. // PENDING:5.0 support customize scale change and transition animation?
  339. if (elDisplayable) {
  340. // By default support auto lift color when hover whether `emphasis` specified.
  341. var stateObj = elDisplayable.ensureState(state);
  342. if (styleOpt === false) {
  343. var existingEmphasisState = elDisplayable.getState(state);
  344. if (existingEmphasisState) {
  345. existingEmphasisState.style = null;
  346. }
  347. } else {
  348. // style is needed to enable default emphasis.
  349. stateObj.style = styleOpt || null;
  350. }
  351. // If `elOption.styleEmphasis` or `elOption.emphasis.style` is `false`,
  352. // remove hover style.
  353. // If `elOption.textConfig` or `elOption.emphasis.textConfig` is null/undefined, it does not
  354. // make sense. So for simplicity, we do not ditinguish `hasOwnProperty` and null/undefined.
  355. if (txCfgOpt) {
  356. stateObj.textConfig = txCfgOpt;
  357. }
  358. setDefaultStateProxy(elDisplayable);
  359. }
  360. }
  361. function updateZ(el, elOption, seriesModel) {
  362. // Group not support textContent and not support z yet.
  363. if (el.isGroup) {
  364. return;
  365. }
  366. var elDisplayable = el;
  367. var currentZ = seriesModel.currentZ;
  368. var currentZLevel = seriesModel.currentZLevel;
  369. // Always erase.
  370. elDisplayable.z = currentZ;
  371. elDisplayable.zlevel = currentZLevel;
  372. // z2 must not be null/undefined, otherwise sort error may occur.
  373. var optZ2 = elOption.z2;
  374. optZ2 != null && (elDisplayable.z2 = optZ2 || 0);
  375. for (var i = 0; i < STATES.length; i++) {
  376. updateZForEachState(elDisplayable, elOption, STATES[i]);
  377. }
  378. }
  379. function updateZForEachState(elDisplayable, elOption, state) {
  380. var isNormal = state === NORMAL;
  381. var elStateOpt = isNormal ? elOption : retrieveStateOption(elOption, state);
  382. var optZ2 = elStateOpt ? elStateOpt.z2 : null;
  383. var stateObj;
  384. if (optZ2 != null) {
  385. // Do not `ensureState` until required.
  386. stateObj = isNormal ? elDisplayable : elDisplayable.ensureState(state);
  387. stateObj.z2 = optZ2 || 0;
  388. }
  389. }
  390. function makeRenderItem(customSeries, data, ecModel, api) {
  391. var renderItem = customSeries.get('renderItem');
  392. if (typeof renderItem === 'string') {
  393. // Find renderItem in registered custom series
  394. var registeredRenderItem = getCustomSeries(renderItem);
  395. if (registeredRenderItem) {
  396. renderItem = registeredRenderItem;
  397. } else if (process.env.NODE_ENV !== 'production') {
  398. console.warn("Custom series renderItem '" + renderItem + "' not found.\n Call 'echarts.registerCustomSeries' to register it.");
  399. }
  400. }
  401. var coordSys = customSeries.coordinateSystem;
  402. var prepareResult = {};
  403. if (coordSys) {
  404. if (process.env.NODE_ENV !== 'production') {
  405. assert(renderItem, 'series.render is required.');
  406. assert(coordSys.prepareCustoms || prepareCustoms[coordSys.type], 'This coordSys does not support custom series.');
  407. }
  408. // `coordSys.prepareCustoms` is used for external coord sys like bmap.
  409. prepareResult = coordSys.prepareCustoms ? coordSys.prepareCustoms(coordSys) : prepareCustoms[coordSys.type](coordSys);
  410. }
  411. var userAPI = defaults({
  412. getWidth: api.getWidth,
  413. getHeight: api.getHeight,
  414. getZr: api.getZr,
  415. getDevicePixelRatio: api.getDevicePixelRatio,
  416. value: value,
  417. style: style,
  418. ordinalRawValue: ordinalRawValue,
  419. styleEmphasis: styleEmphasis,
  420. visual: visual,
  421. barLayout: barLayout,
  422. currentSeriesIndices: currentSeriesIndices,
  423. font: font
  424. }, prepareResult.api || {});
  425. var userParams = {
  426. // The life cycle of context: current round of rendering.
  427. // The global life cycle is probably not necessary, because
  428. // user can store global status by themselves.
  429. context: {},
  430. seriesId: customSeries.id,
  431. seriesName: customSeries.name,
  432. seriesIndex: customSeries.seriesIndex,
  433. coordSys: prepareResult.coordSys,
  434. dataInsideLength: data.count(),
  435. encode: wrapEncodeDef(customSeries.getData()),
  436. itemPayload: customSeries.get('itemPayload') || {}
  437. };
  438. // If someday intending to refactor them to a class, should consider do not
  439. // break change: currently these attribute member are encapsulated in a closure
  440. // so that do not need to force user to call these method with a scope.
  441. // Do not support call `api` asynchronously without dataIndexInside input.
  442. var currDataIndexInside;
  443. var currItemModel;
  444. var currItemStyleModels = {};
  445. var currLabelModels = {};
  446. var seriesItemStyleModels = {};
  447. var seriesLabelModels = {};
  448. for (var i = 0; i < STATES.length; i++) {
  449. var stateName = STATES[i];
  450. seriesItemStyleModels[stateName] = customSeries.getModel(PATH_ITEM_STYLE[stateName]);
  451. seriesLabelModels[stateName] = customSeries.getModel(PATH_LABEL[stateName]);
  452. }
  453. function getItemModel(dataIndexInside) {
  454. return dataIndexInside === currDataIndexInside ? currItemModel || (currItemModel = data.getItemModel(dataIndexInside)) : data.getItemModel(dataIndexInside);
  455. }
  456. function getItemStyleModel(dataIndexInside, state) {
  457. return !data.hasItemOption ? seriesItemStyleModels[state] : dataIndexInside === currDataIndexInside ? currItemStyleModels[state] || (currItemStyleModels[state] = getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state])) : getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state]);
  458. }
  459. function getLabelModel(dataIndexInside, state) {
  460. return !data.hasItemOption ? seriesLabelModels[state] : dataIndexInside === currDataIndexInside ? currLabelModels[state] || (currLabelModels[state] = getItemModel(dataIndexInside).getModel(PATH_LABEL[state])) : getItemModel(dataIndexInside).getModel(PATH_LABEL[state]);
  461. }
  462. return function (dataIndexInside, payload) {
  463. currDataIndexInside = dataIndexInside;
  464. currItemModel = null;
  465. currItemStyleModels = {};
  466. currLabelModels = {};
  467. return renderItem && renderItem(defaults({
  468. dataIndexInside: dataIndexInside,
  469. dataIndex: data.getRawIndex(dataIndexInside),
  470. // Can be used for optimization when zoom or roam.
  471. actionType: payload ? payload.type : null
  472. }, userParams), userAPI);
  473. };
  474. /**
  475. * @public
  476. * @param dim by default 0.
  477. * @param dataIndexInside by default `currDataIndexInside`.
  478. */
  479. function value(dim, dataIndexInside) {
  480. dataIndexInside == null && (dataIndexInside = currDataIndexInside);
  481. return data.getStore().get(data.getDimensionIndex(dim || 0), dataIndexInside);
  482. }
  483. /**
  484. * @public
  485. * @param dim by default 0.
  486. * @param dataIndexInside by default `currDataIndexInside`.
  487. */
  488. function ordinalRawValue(dim, dataIndexInside) {
  489. dataIndexInside == null && (dataIndexInside = currDataIndexInside);
  490. dim = dim || 0;
  491. var dimInfo = data.getDimensionInfo(dim);
  492. if (!dimInfo) {
  493. var dimIndex = data.getDimensionIndex(dim);
  494. return dimIndex >= 0 ? data.getStore().get(dimIndex, dataIndexInside) : undefined;
  495. }
  496. var val = data.get(dimInfo.name, dataIndexInside);
  497. var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
  498. return ordinalMeta ? ordinalMeta.categories[val] : val;
  499. }
  500. /**
  501. * @deprecated The original intention of `api.style` is enable to set itemStyle
  502. * like other series. But it is not necessary and not easy to give a strict definition
  503. * of what it returns. And since echarts5 it needs to be make compat work. So
  504. * deprecates it since echarts5.
  505. *
  506. * By default, `visual` is applied to style (to support visualMap).
  507. * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`,
  508. * it can be implemented as:
  509. * `api.style({stroke: api.visual('color'), fill: null})`;
  510. *
  511. * [Compat]: since ec5, RectText has been separated from its hosts el.
  512. * so `api.style()` will only return the style from `itemStyle` but not handle `label`
  513. * any more. But `series.label` config is never published in doc.
  514. * We still compat it in `api.style()`. But not encourage to use it and will still not
  515. * to pulish it to doc.
  516. * @public
  517. * @param dataIndexInside by default `currDataIndexInside`.
  518. */
  519. function style(userProps, dataIndexInside) {
  520. if (process.env.NODE_ENV !== 'production') {
  521. warnDeprecated('api.style', 'Please write literal style directly instead.');
  522. }
  523. dataIndexInside == null && (dataIndexInside = currDataIndexInside);
  524. var style = data.getItemVisual(dataIndexInside, 'style');
  525. var visualColor = style && style.fill;
  526. var opacity = style && style.opacity;
  527. var itemStyle = getItemStyleModel(dataIndexInside, NORMAL).getItemStyle();
  528. visualColor != null && (itemStyle.fill = visualColor);
  529. opacity != null && (itemStyle.opacity = opacity);
  530. var opt = {
  531. inheritColor: isString(visualColor) ? visualColor : tokens.color.neutral99
  532. };
  533. var labelModel = getLabelModel(dataIndexInside, NORMAL);
  534. // Now that the feature of "auto adjust text fill/stroke" has been migrated to zrender
  535. // since ec5, we should set `isAttached` as `false` here and make compat in
  536. // `convertToEC4StyleForCustomSerise`.
  537. var textStyle = labelStyleHelper.createTextStyle(labelModel, null, opt, false, true);
  538. textStyle.text = labelModel.getShallow('show') ? retrieve2(customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null;
  539. var textConfig = labelStyleHelper.createTextConfig(labelModel, opt, false);
  540. preFetchFromExtra(userProps, itemStyle);
  541. itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig);
  542. userProps && applyUserPropsAfter(itemStyle, userProps);
  543. itemStyle.legacy = true;
  544. return itemStyle;
  545. }
  546. /**
  547. * @deprecated The reason see `api.style()`
  548. * @public
  549. * @param dataIndexInside by default `currDataIndexInside`.
  550. */
  551. function styleEmphasis(userProps, dataIndexInside) {
  552. if (process.env.NODE_ENV !== 'production') {
  553. warnDeprecated('api.styleEmphasis', 'Please write literal style directly instead.');
  554. }
  555. dataIndexInside == null && (dataIndexInside = currDataIndexInside);
  556. var itemStyle = getItemStyleModel(dataIndexInside, EMPHASIS).getItemStyle();
  557. var labelModel = getLabelModel(dataIndexInside, EMPHASIS);
  558. var textStyle = labelStyleHelper.createTextStyle(labelModel, null, null, true, true);
  559. textStyle.text = labelModel.getShallow('show') ? retrieve3(customSeries.getFormattedLabel(dataIndexInside, EMPHASIS), customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null;
  560. var textConfig = labelStyleHelper.createTextConfig(labelModel, null, true);
  561. preFetchFromExtra(userProps, itemStyle);
  562. itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig);
  563. userProps && applyUserPropsAfter(itemStyle, userProps);
  564. itemStyle.legacy = true;
  565. return itemStyle;
  566. }
  567. function applyUserPropsAfter(itemStyle, extra) {
  568. for (var key in extra) {
  569. if (hasOwn(extra, key)) {
  570. itemStyle[key] = extra[key];
  571. }
  572. }
  573. }
  574. function preFetchFromExtra(extra, itemStyle) {
  575. // A trick to retrieve those props firstly, which are used to
  576. // apply auto inside fill/stroke in `convertToEC4StyleForCustomSerise`.
  577. // (It's not reasonable but only for a degree of compat)
  578. if (extra) {
  579. extra.textFill && (itemStyle.textFill = extra.textFill);
  580. extra.textPosition && (itemStyle.textPosition = extra.textPosition);
  581. }
  582. }
  583. /**
  584. * @public
  585. * @param dataIndexInside by default `currDataIndexInside`.
  586. */
  587. function visual(visualType, dataIndexInside) {
  588. dataIndexInside == null && (dataIndexInside = currDataIndexInside);
  589. if (hasOwn(STYLE_VISUAL_TYPE, visualType)) {
  590. var style_1 = data.getItemVisual(dataIndexInside, 'style');
  591. return style_1 ? style_1[STYLE_VISUAL_TYPE[visualType]] : null;
  592. }
  593. // Only support these visuals. Other visual might be inner tricky
  594. // for performance (like `style`), do not expose to users.
  595. if (hasOwn(NON_STYLE_VISUAL_PROPS, visualType)) {
  596. return data.getItemVisual(dataIndexInside, visualType);
  597. }
  598. }
  599. /**
  600. * @public
  601. * @return If not support, return undefined.
  602. */
  603. function barLayout(opt) {
  604. if (coordSys.type === 'cartesian2d') {
  605. var baseAxis = coordSys.getBaseAxis();
  606. return getLayoutOnAxis(defaults({
  607. axis: baseAxis
  608. }, opt));
  609. }
  610. }
  611. /**
  612. * @public
  613. */
  614. function currentSeriesIndices() {
  615. return ecModel.getCurrentSeriesIndices();
  616. }
  617. /**
  618. * @public
  619. * @return font string
  620. */
  621. function font(opt) {
  622. return labelStyleHelper.getFont(opt, ecModel);
  623. }
  624. }
  625. function wrapEncodeDef(data) {
  626. var encodeDef = {};
  627. each(data.dimensions, function (dimName) {
  628. var dimInfo = data.getDimensionInfo(dimName);
  629. if (!dimInfo.isExtraCoord) {
  630. var coordDim = dimInfo.coordDim;
  631. var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || [];
  632. dataDims[dimInfo.coordDimIndex] = data.getDimensionIndex(dimName);
  633. }
  634. });
  635. return encodeDef;
  636. }
  637. function createOrUpdateItem(api, existsEl, dataIndex, elOption, seriesModel, group, data) {
  638. // [Rule]
  639. // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing.
  640. // (It seems that violate the "merge" principle, but most of users probably intuitively
  641. // regard "return;" as "show nothing element whatever", so make a exception to meet the
  642. // most cases.)
  643. // The rule or "merge" see [STRATEGY_MERGE].
  644. // If `elOption` is `null`/`undefined`/`false` (when `renderItem` returns nothing).
  645. if (!elOption) {
  646. group.remove(existsEl);
  647. return;
  648. }
  649. var el = doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group);
  650. el && data.setItemGraphicEl(dataIndex, el);
  651. el && toggleHoverEmphasis(el, elOption.focus, elOption.blurScope, elOption.emphasisDisabled);
  652. return el;
  653. }
  654. function doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group) {
  655. if (process.env.NODE_ENV !== 'production') {
  656. assert(elOption, 'should not have an null/undefined element setting');
  657. }
  658. var toBeReplacedIdx = -1;
  659. var oldEl = existsEl;
  660. if (existsEl && doesElNeedRecreate(existsEl, elOption, seriesModel)
  661. // || (
  662. // // PENDING: even in one-to-one mapping case, if el is marked as morph,
  663. // // do not sure whether the el will be mapped to another el with different
  664. // // hierarchy in Group tree. So always recreate el rather than reuse the el.
  665. // morphHelper && morphHelper.isOneToOneFrom(el)
  666. // )
  667. ) {
  668. // Should keep at the original index, otherwise "merge by index" will be incorrect.
  669. toBeReplacedIdx = indexOf(group.childrenRef(), existsEl);
  670. existsEl = null;
  671. }
  672. var isInit = !existsEl;
  673. var el = existsEl;
  674. if (!el) {
  675. el = createEl(elOption);
  676. if (oldEl) {
  677. copyElement(oldEl, el);
  678. }
  679. } else {
  680. // FIMXE:NEXT unified clearState?
  681. // If in some case the performance issue arised, consider
  682. // do not clearState but update cached normal state directly.
  683. el.clearStates();
  684. }
  685. // Need to set morph: false explictly to disable automatically morphing.
  686. if (elOption.morph === false) {
  687. el.disableMorphing = true;
  688. } else if (el.disableMorphing) {
  689. el.disableMorphing = false;
  690. }
  691. if (elOption.tooltipDisabled) {
  692. el.tooltipDisabled = true;
  693. }
  694. attachedTxInfoTmp.normal.cfg = attachedTxInfoTmp.normal.conOpt = attachedTxInfoTmp.emphasis.cfg = attachedTxInfoTmp.emphasis.conOpt = attachedTxInfoTmp.blur.cfg = attachedTxInfoTmp.blur.conOpt = attachedTxInfoTmp.select.cfg = attachedTxInfoTmp.select.conOpt = null;
  695. attachedTxInfoTmp.isLegacy = false;
  696. doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfoTmp);
  697. doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit);
  698. updateElNormal(api, el, dataIndex, elOption, attachedTxInfoTmp, seriesModel, isInit);
  699. // `elOption.info` enables user to mount some info on
  700. // elements and use them in event handlers.
  701. // Update them only when user specified, otherwise, remain.
  702. hasOwn(elOption, 'info') && (customInnerStore(el).info = elOption.info);
  703. for (var i = 0; i < STATES.length; i++) {
  704. var stateName = STATES[i];
  705. if (stateName !== NORMAL) {
  706. var otherStateOpt = retrieveStateOption(elOption, stateName);
  707. var otherStyleOpt = retrieveStyleOptionOnState(elOption, otherStateOpt, stateName);
  708. updateElOnState(stateName, el, otherStateOpt, otherStyleOpt, attachedTxInfoTmp);
  709. }
  710. }
  711. updateZ(el, elOption, seriesModel);
  712. if (elOption.type === 'group') {
  713. mergeChildren(api, el, dataIndex, elOption, seriesModel);
  714. }
  715. if (toBeReplacedIdx >= 0) {
  716. group.replaceAt(el, toBeReplacedIdx);
  717. } else {
  718. group.add(el);
  719. }
  720. return el;
  721. }
  722. // `el` must not be null/undefined.
  723. function doesElNeedRecreate(el, elOption, seriesModel) {
  724. var elInner = customInnerStore(el);
  725. var elOptionType = elOption.type;
  726. var elOptionShape = elOption.shape;
  727. var elOptionStyle = elOption.style;
  728. return (
  729. // Always create new if universal transition is enabled.
  730. // Because we do transition after render. It needs to know what old element is. Replacement will loose it.
  731. seriesModel.isUniversalTransitionEnabled()
  732. // If `elOptionType` is `null`, follow the merge principle.
  733. || elOptionType != null && elOptionType !== elInner.customGraphicType || elOptionType === 'path' && hasOwnPathData(elOptionShape) && getPathData(elOptionShape) !== elInner.customPathData || elOptionType === 'image' && hasOwn(elOptionStyle, 'image') && elOptionStyle.image !== elInner.customImagePath
  734. // // FIXME test and remove this restriction?
  735. // || (elOptionType === 'text'
  736. // && hasOwn(elOptionStyle, 'text')
  737. // && (elOptionStyle as TextStyleProps).text !== elInner.customText
  738. // )
  739. );
  740. }
  741. function doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit) {
  742. // Based on the "merge" principle, if no clipPath provided,
  743. // do nothing. The exists clip will be totally removed only if
  744. // `el.clipPath` is `false`. Otherwise it will be merged/replaced.
  745. var clipPathOpt = elOption.clipPath;
  746. if (clipPathOpt === false) {
  747. if (el && el.getClipPath()) {
  748. el.removeClipPath();
  749. }
  750. } else if (clipPathOpt) {
  751. var clipPath = el.getClipPath();
  752. if (clipPath && doesElNeedRecreate(clipPath, clipPathOpt, seriesModel)) {
  753. clipPath = null;
  754. }
  755. if (!clipPath) {
  756. clipPath = createEl(clipPathOpt);
  757. if (process.env.NODE_ENV !== 'production') {
  758. assert(isPath(clipPath), 'Only any type of `path` can be used in `clipPath`, rather than ' + clipPath.type + '.');
  759. }
  760. el.setClipPath(clipPath);
  761. }
  762. updateElNormal(null, clipPath, dataIndex, clipPathOpt, null, seriesModel, isInit);
  763. }
  764. // If not define `clipPath` in option, do nothing unnecessary.
  765. }
  766. function doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfo) {
  767. // Group does not support textContent temporarily until necessary.
  768. if (el.isGroup || el.type === 'compoundPath') {
  769. return;
  770. }
  771. // Normal must be called before emphasis, for `isLegacy` detection.
  772. processTxInfo(elOption, null, attachedTxInfo);
  773. processTxInfo(elOption, EMPHASIS, attachedTxInfo);
  774. // If `elOption.textConfig` or `elOption.textContent` is null/undefined, it does not make sense.
  775. // So for simplicity, if "elOption hasOwnProperty of them but be null/undefined", we do not
  776. // trade them as set to null to el.
  777. // Especially:
  778. // `elOption.textContent: false` means remove textContent.
  779. // `elOption.textContent.emphasis.style: false` means remove the style from emphasis state.
  780. var txConOptNormal = attachedTxInfo.normal.conOpt;
  781. var txConOptEmphasis = attachedTxInfo.emphasis.conOpt;
  782. var txConOptBlur = attachedTxInfo.blur.conOpt;
  783. var txConOptSelect = attachedTxInfo.select.conOpt;
  784. if (txConOptNormal != null || txConOptEmphasis != null || txConOptSelect != null || txConOptBlur != null) {
  785. var textContent = el.getTextContent();
  786. if (txConOptNormal === false) {
  787. textContent && el.removeTextContent();
  788. } else {
  789. txConOptNormal = attachedTxInfo.normal.conOpt = txConOptNormal || {
  790. type: 'text'
  791. };
  792. if (!textContent) {
  793. textContent = createEl(txConOptNormal);
  794. el.setTextContent(textContent);
  795. } else {
  796. // If in some case the performance issue arised, consider
  797. // do not clearState but update cached normal state directly.
  798. textContent.clearStates();
  799. }
  800. updateElNormal(null, textContent, dataIndex, txConOptNormal, null, seriesModel, isInit);
  801. var txConStlOptNormal = txConOptNormal && txConOptNormal.style;
  802. for (var i = 0; i < STATES.length; i++) {
  803. var stateName = STATES[i];
  804. if (stateName !== NORMAL) {
  805. var txConOptOtherState = attachedTxInfo[stateName].conOpt;
  806. updateElOnState(stateName, textContent, txConOptOtherState, retrieveStyleOptionOnState(txConOptNormal, txConOptOtherState, stateName), null);
  807. }
  808. }
  809. txConStlOptNormal ? textContent.dirty() : textContent.markRedraw();
  810. }
  811. }
  812. }
  813. function processTxInfo(elOption, state, attachedTxInfo) {
  814. var stateOpt = !state ? elOption : retrieveStateOption(elOption, state);
  815. var styleOpt = !state ? elOption.style : retrieveStyleOptionOnState(elOption, stateOpt, EMPHASIS);
  816. var elType = elOption.type;
  817. var txCfg = stateOpt ? stateOpt.textConfig : null;
  818. var txConOptNormal = elOption.textContent;
  819. var txConOpt = !txConOptNormal ? null : !state ? txConOptNormal : retrieveStateOption(txConOptNormal, state);
  820. if (styleOpt && (
  821. // Because emphasis style has little info to detect legacy,
  822. // if normal is legacy, emphasis is trade as legacy.
  823. attachedTxInfo.isLegacy || isEC4CompatibleStyle(styleOpt, elType, !!txCfg, !!txConOpt))) {
  824. attachedTxInfo.isLegacy = true;
  825. var convertResult = convertFromEC4CompatibleStyle(styleOpt, elType, !state);
  826. // Explicitly specified `textConfig` and `textContent` has higher priority than
  827. // the ones generated by legacy style. Otherwise if users use them and `api.style`
  828. // at the same time, they not both work and hardly to known why.
  829. if (!txCfg && convertResult.textConfig) {
  830. txCfg = convertResult.textConfig;
  831. }
  832. if (!txConOpt && convertResult.textContent) {
  833. txConOpt = convertResult.textContent;
  834. }
  835. }
  836. if (!state && txConOpt) {
  837. var txConOptNormal_1 = txConOpt;
  838. // `textContent: {type: 'text'}`, the "type" is easy to be missing. So we tolerate it.
  839. !txConOptNormal_1.type && (txConOptNormal_1.type = 'text');
  840. if (process.env.NODE_ENV !== 'production') {
  841. // Do not tolerate incorrcet type for forward compat.
  842. assert(txConOptNormal_1.type === 'text', 'textContent.type must be "text"');
  843. }
  844. }
  845. var info = !state ? attachedTxInfo.normal : attachedTxInfo[state];
  846. info.cfg = txCfg;
  847. info.conOpt = txConOpt;
  848. }
  849. function retrieveStateOption(elOption, state) {
  850. return !state ? elOption : elOption ? elOption[state] : null;
  851. }
  852. function retrieveStyleOptionOnState(stateOptionNormal, stateOption, state) {
  853. var style = stateOption && stateOption.style;
  854. if (style == null && state === EMPHASIS && stateOptionNormal) {
  855. style = stateOptionNormal.styleEmphasis;
  856. }
  857. return style;
  858. }
  859. // Usage:
  860. // (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates
  861. // that the existing children will not be removed, and enables the feature
  862. // that update some of the props of some of the children simply by construct
  863. // the returned children of `renderItem` like:
  864. // `var children = group.children = []; children[3] = {opacity: 0.5};`
  865. // (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children
  866. // by child.name. But that might be lower performance.
  867. // (3) If `elOption.$mergeChildren` is `false`, the existing children will be
  868. // replaced totally.
  869. // (4) If `!elOption.children`, following the "merge" principle, nothing will
  870. // happen.
  871. // (5) If `elOption.$mergeChildren` is not `false` neither `'byName'` and the
  872. // `el` is a group, and if any of the new child is null, it means to remove
  873. // the element at the same index, if exists. On the other hand, if the new
  874. // child is and empty object `{}`, it means to keep the element not changed.
  875. //
  876. // For implementation simpleness, do not provide a direct way to remove single
  877. // child (otherwise the total indices of the children array have to be modified).
  878. // User can remove a single child by setting its `ignore` to `true`.
  879. function mergeChildren(api, el, dataIndex, elOption, seriesModel) {
  880. var newChildren = elOption.children;
  881. var newLen = newChildren ? newChildren.length : 0;
  882. var mergeChildren = elOption.$mergeChildren;
  883. // `diffChildrenByName` has been deprecated.
  884. var byName = mergeChildren === 'byName' || elOption.diffChildrenByName;
  885. var notMerge = mergeChildren === false;
  886. // For better performance on roam update, only enter if necessary.
  887. if (!newLen && !byName && !notMerge) {
  888. return;
  889. }
  890. if (byName) {
  891. diffGroupChildren({
  892. api: api,
  893. oldChildren: el.children() || [],
  894. newChildren: newChildren || [],
  895. dataIndex: dataIndex,
  896. seriesModel: seriesModel,
  897. group: el
  898. });
  899. return;
  900. }
  901. notMerge && el.removeAll();
  902. // Mapping children of a group simply by index, which
  903. // might be better performance.
  904. var index = 0;
  905. for (; index < newLen; index++) {
  906. var newChild = newChildren[index];
  907. var oldChild = el.childAt(index);
  908. if (newChild) {
  909. if (newChild.ignore == null) {
  910. // The old child is set to be ignored if null (see comments
  911. // below). So we need to set ignore to be false back.
  912. newChild.ignore = false;
  913. }
  914. doCreateOrUpdateEl(api, oldChild, dataIndex, newChild, seriesModel, el);
  915. } else {
  916. if (process.env.NODE_ENV !== 'production') {
  917. assert(oldChild, 'renderItem should not return a group containing elements' + ' as null/undefined/{} if they do not exist before.');
  918. }
  919. // If the new element option is null, it means to remove the old
  920. // element. But we cannot really remove the element from the group
  921. // directly, because the element order may not be stable when this
  922. // element is added back. So we set the element to be ignored.
  923. oldChild.ignore = true;
  924. }
  925. }
  926. for (var i = el.childCount() - 1; i >= index; i--) {
  927. var child = el.childAt(i);
  928. removeChildFromGroup(el, child, seriesModel);
  929. }
  930. }
  931. function removeChildFromGroup(group, child, seriesModel) {
  932. // Do not support leave elements that are not mentioned in the latest
  933. // `renderItem` return. Otherwise users may not have a clear and simple
  934. // concept that how to control all of the elements.
  935. child && applyLeaveTransition(child, customInnerStore(group).option, seriesModel);
  936. }
  937. function diffGroupChildren(context) {
  938. new DataDiffer(context.oldChildren, context.newChildren, getKey, getKey, context).add(processAddUpdate).update(processAddUpdate).remove(processRemove).execute();
  939. }
  940. function getKey(item, idx) {
  941. var name = item && item.name;
  942. return name != null ? name : GROUP_DIFF_PREFIX + idx;
  943. }
  944. function processAddUpdate(newIndex, oldIndex) {
  945. var context = this.context;
  946. var childOption = newIndex != null ? context.newChildren[newIndex] : null;
  947. var child = oldIndex != null ? context.oldChildren[oldIndex] : null;
  948. doCreateOrUpdateEl(context.api, child, context.dataIndex, childOption, context.seriesModel, context.group);
  949. }
  950. function processRemove(oldIndex) {
  951. var context = this.context;
  952. var child = context.oldChildren[oldIndex];
  953. child && applyLeaveTransition(child, customInnerStore(child).option, context.seriesModel);
  954. }
  955. /**
  956. * @return SVG Path data.
  957. */
  958. function getPathData(shape) {
  959. // "d" follows the SVG convention.
  960. return shape && (shape.pathData || shape.d);
  961. }
  962. function hasOwnPathData(shape) {
  963. return shape && (hasOwn(shape, 'pathData') || hasOwn(shape, 'd'));
  964. }