CustomView.js 39 KB

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