123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var _config = require("../config");
- var __DEV__ = _config.__DEV__;
- var zrUtil = require("zrender/lib/core/util");
- var graphicUtil = require("../util/graphic");
- var _labelHelper = require("./helper/labelHelper");
- var getDefaultLabel = _labelHelper.getDefaultLabel;
- var createListFromArray = require("./helper/createListFromArray");
- var _barGrid = require("../layout/barGrid");
- var getLayoutOnAxis = _barGrid.getLayoutOnAxis;
- var DataDiffer = require("../data/DataDiffer");
- var SeriesModel = require("../model/Series");
- var Model = require("../model/Model");
- var ChartView = require("../view/Chart");
- var _createClipPathFromCoordSys = require("./helper/createClipPathFromCoordSys");
- var createClipPath = _createClipPathFromCoordSys.createClipPath;
- var prepareCartesian2d = require("../coord/cartesian/prepareCustom");
- var prepareGeo = require("../coord/geo/prepareCustom");
- var prepareSingleAxis = require("../coord/single/prepareCustom");
- var preparePolar = require("../coord/polar/prepareCustom");
- var prepareCalendar = require("../coord/calendar/prepareCustom");
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var CACHED_LABEL_STYLE_PROPERTIES = graphicUtil.CACHED_LABEL_STYLE_PROPERTIES;
- var ITEM_STYLE_NORMAL_PATH = ['itemStyle'];
- var ITEM_STYLE_EMPHASIS_PATH = ['emphasis', 'itemStyle'];
- var LABEL_NORMAL = ['label'];
- var LABEL_EMPHASIS = ['emphasis', 'label']; // Use prefix to avoid index to be the same as el.name,
- // which will cause weird udpate animation.
- var GROUP_DIFF_PREFIX = 'e\0\0';
- /**
- * To reduce total package size of each coordinate systems, the modules `prepareCustom`
- * of each coordinate systems are not required by each coordinate systems directly, but
- * required by the module `custom`.
- *
- * prepareInfoForCustomSeries {Function}: optional
- * @return {Object} {coordSys: {...}, api: {
- * coord: function (data, clamp) {}, // return point in global.
- * size: function (dataSize, dataItem) {} // return size of each axis in coordSys.
- * }}
- */
- var prepareCustoms = {
- cartesian2d: prepareCartesian2d,
- geo: prepareGeo,
- singleAxis: prepareSingleAxis,
- polar: preparePolar,
- calendar: prepareCalendar
- }; // ------
- // Model
- // ------
- SeriesModel.extend({
- type: 'series.custom',
- dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'],
- defaultOption: {
- coordinateSystem: 'cartesian2d',
- // Can be set as 'none'
- zlevel: 0,
- z: 2,
- legendHoverLink: true,
- useTransform: true,
- // Custom series will not clip by default.
- // Some case will use custom series to draw label
- // For example https://echarts.apache.org/examples/en/editor.html?c=custom-gantt-flight
- // Only works on polar and cartesian2d coordinate system.
- clip: false // Cartesian coordinate system
- // xAxisIndex: 0,
- // yAxisIndex: 0,
- // Polar coordinate system
- // polarIndex: 0,
- // Geo coordinate system
- // geoIndex: 0,
- // label: {}
- // itemStyle: {}
- },
- /**
- * @override
- */
- getInitialData: function (option, ecModel) {
- return createListFromArray(this.getSource(), this);
- },
- /**
- * @override
- */
- getDataParams: function (dataIndex, dataType, el) {
- var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
- el && (params.info = el.info);
- return params;
- }
- }); // -----
- // View
- // -----
- ChartView.extend({
- type: 'custom',
- /**
- * @private
- * @type {module:echarts/data/List}
- */
- _data: null,
- /**
- * @override
- */
- render: function (customSeries, ecModel, api, payload) {
- var oldData = this._data;
- var data = customSeries.getData();
- var group = this.group;
- var renderItem = makeRenderItem(customSeries, data, ecModel, api); // By default, merge mode is applied. In most cases, custom series is
- // used in the scenario that data amount is not large but graphic elements
- // is complicated, where merge mode is probably necessary for optimization.
- // For example, reuse graphic elements and only update the transform when
- // roam or data zoom according to `actionType`.
- data.diff(oldData).add(function (newIdx) {
- createOrUpdate(null, newIdx, renderItem(newIdx, payload), customSeries, group, data);
- }).update(function (newIdx, oldIdx) {
- var el = oldData.getItemGraphicEl(oldIdx);
- createOrUpdate(el, newIdx, renderItem(newIdx, payload), customSeries, group, data);
- }).remove(function (oldIdx) {
- var el = oldData.getItemGraphicEl(oldIdx);
- el && group.remove(el);
- }).execute(); // Do clipping
- var clipPath = customSeries.get('clip', true) ? createClipPath(customSeries.coordinateSystem, false, customSeries) : null;
- if (clipPath) {
- group.setClipPath(clipPath);
- } else {
- group.removeClipPath();
- }
- this._data = data;
- },
- incrementalPrepareRender: function (customSeries, ecModel, api) {
- this.group.removeAll();
- this._data = null;
- },
- incrementalRender: function (params, customSeries, ecModel, api, payload) {
- var data = customSeries.getData();
- var renderItem = makeRenderItem(customSeries, data, ecModel, api);
- function setIncrementalAndHoverLayer(el) {
- if (!el.isGroup) {
- el.incremental = true;
- el.useHoverLayer = true;
- }
- }
- for (var idx = params.start; idx < params.end; idx++) {
- var el = createOrUpdate(null, idx, renderItem(idx, payload), customSeries, this.group, data);
- el.traverse(setIncrementalAndHoverLayer);
- }
- },
- /**
- * @override
- */
- dispose: zrUtil.noop,
- /**
- * @override
- */
- filterForExposedEvent: function (eventType, query, targetEl, packedEvent) {
- var elementName = query.element;
- if (elementName == null || targetEl.name === elementName) {
- return true;
- } // Enable to give a name on a group made by `renderItem`, and listen
- // events that triggerd by its descendents.
- while ((targetEl = targetEl.parent) && targetEl !== this.group) {
- if (targetEl.name === elementName) {
- return true;
- }
- }
- return false;
- }
- });
- function createEl(elOption) {
- var graphicType = elOption.type;
- var el; // Those graphic elements are not shapes. They should not be
- // overwritten by users, so do them first.
- if (graphicType === 'path') {
- var shape = elOption.shape; // Using pathRect brings convenience to users sacle svg path.
- var pathRect = shape.width != null && shape.height != null ? {
- x: shape.x || 0,
- y: shape.y || 0,
- width: shape.width,
- height: shape.height
- } : null;
- var pathData = getPathData(shape); // Path is also used for icon, so layout 'center' by default.
- el = graphicUtil.makePath(pathData, null, pathRect, shape.layout || 'center');
- el.__customPathData = pathData;
- } else if (graphicType === 'image') {
- el = new graphicUtil.Image({});
- el.__customImagePath = elOption.style.image;
- } else if (graphicType === 'text') {
- el = new graphicUtil.Text({});
- el.__customText = elOption.style.text;
- } else if (graphicType === 'group') {
- el = new graphicUtil.Group();
- } else if (graphicType === 'compoundPath') {
- throw new Error('"compoundPath" is not supported yet.');
- } else {
- var Clz = graphicUtil.getShapeClass(graphicType);
- el = new Clz();
- }
- el.__customGraphicType = graphicType;
- el.name = elOption.name;
- return el;
- }
- function updateEl(el, dataIndex, elOption, animatableModel, data, isInit, isRoot) {
- var transitionProps = {};
- var elOptionStyle = elOption.style || {};
- elOption.shape && (transitionProps.shape = zrUtil.clone(elOption.shape));
- elOption.position && (transitionProps.position = elOption.position.slice());
- elOption.scale && (transitionProps.scale = elOption.scale.slice());
- elOption.origin && (transitionProps.origin = elOption.origin.slice());
- elOption.rotation && (transitionProps.rotation = elOption.rotation);
- if (el.type === 'image' && elOption.style) {
- var targetStyle = transitionProps.style = {};
- zrUtil.each(['x', 'y', 'width', 'height'], function (prop) {
- prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit);
- });
- }
- if (el.type === 'text' && elOption.style) {
- var targetStyle = transitionProps.style = {};
- zrUtil.each(['x', 'y'], function (prop) {
- prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit);
- }); // Compatible with previous: both support
- // textFill and fill, textStroke and stroke in 'text' element.
- !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && (elOptionStyle.textFill = elOptionStyle.fill);
- !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && (elOptionStyle.textStroke = elOptionStyle.stroke);
- }
- if (el.type !== 'group') {
- el.useStyle(elOptionStyle); // Init animation.
- if (isInit) {
- el.style.opacity = 0;
- var targetOpacity = elOptionStyle.opacity;
- targetOpacity == null && (targetOpacity = 1);
- graphicUtil.initProps(el, {
- style: {
- opacity: targetOpacity
- }
- }, animatableModel, dataIndex);
- }
- }
- if (isInit) {
- el.attr(transitionProps);
- } else {
- graphicUtil.updateProps(el, transitionProps, animatableModel, dataIndex);
- } // Merge by default.
- // z2 must not be null/undefined, otherwise sort error may occur.
- elOption.hasOwnProperty('z2') && el.attr('z2', elOption.z2 || 0);
- elOption.hasOwnProperty('silent') && el.attr('silent', elOption.silent);
- elOption.hasOwnProperty('invisible') && el.attr('invisible', elOption.invisible);
- elOption.hasOwnProperty('ignore') && el.attr('ignore', elOption.ignore); // `elOption.info` enables user to mount some info on
- // elements and use them in event handlers.
- // Update them only when user specified, otherwise, remain.
- elOption.hasOwnProperty('info') && el.attr('info', elOption.info); // If `elOption.styleEmphasis` is `false`, remove hover style. The
- // logic is ensured by `graphicUtil.setElementHoverStyle`.
- var styleEmphasis = elOption.styleEmphasis; // hoverStyle should always be set here, because if the hover style
- // may already be changed, where the inner cache should be reset.
- graphicUtil.setElementHoverStyle(el, styleEmphasis);
- if (isRoot) {
- graphicUtil.setAsHighDownDispatcher(el, styleEmphasis !== false);
- }
- }
- function prepareStyleTransition(prop, targetStyle, elOptionStyle, oldElStyle, isInit) {
- if (elOptionStyle[prop] != null && !isInit) {
- targetStyle[prop] = elOptionStyle[prop];
- elOptionStyle[prop] = oldElStyle[prop];
- }
- }
- function makeRenderItem(customSeries, data, ecModel, api) {
- var renderItem = customSeries.get('renderItem');
- var coordSys = customSeries.coordinateSystem;
- var prepareResult = {};
- if (coordSys) {
- prepareResult = coordSys.prepareCustoms ? coordSys.prepareCustoms() : prepareCustoms[coordSys.type](coordSys);
- }
- var userAPI = zrUtil.defaults({
- getWidth: api.getWidth,
- getHeight: api.getHeight,
- getZr: api.getZr,
- getDevicePixelRatio: api.getDevicePixelRatio,
- value: value,
- style: style,
- styleEmphasis: styleEmphasis,
- visual: visual,
- barLayout: barLayout,
- currentSeriesIndices: currentSeriesIndices,
- font: font
- }, prepareResult.api || {});
- var userParams = {
- // The life cycle of context: current round of rendering.
- // The global life cycle is probably not necessary, because
- // user can store global status by themselves.
- context: {},
- seriesId: customSeries.id,
- seriesName: customSeries.name,
- seriesIndex: customSeries.seriesIndex,
- coordSys: prepareResult.coordSys,
- dataInsideLength: data.count(),
- encode: wrapEncodeDef(customSeries.getData())
- }; // Do not support call `api` asynchronously without dataIndexInside input.
- var currDataIndexInside;
- var currDirty = true;
- var currItemModel;
- var currLabelNormalModel;
- var currLabelEmphasisModel;
- var currVisualColor;
- return function (dataIndexInside, payload) {
- currDataIndexInside = dataIndexInside;
- currDirty = true;
- return renderItem && renderItem(zrUtil.defaults({
- dataIndexInside: dataIndexInside,
- dataIndex: data.getRawIndex(dataIndexInside),
- // Can be used for optimization when zoom or roam.
- actionType: payload ? payload.type : null
- }, userParams), userAPI);
- }; // Do not update cache until api called.
- function updateCache(dataIndexInside) {
- dataIndexInside == null && (dataIndexInside = currDataIndexInside);
- if (currDirty) {
- currItemModel = data.getItemModel(dataIndexInside);
- currLabelNormalModel = currItemModel.getModel(LABEL_NORMAL);
- currLabelEmphasisModel = currItemModel.getModel(LABEL_EMPHASIS);
- currVisualColor = data.getItemVisual(dataIndexInside, 'color');
- currDirty = false;
- }
- }
- /**
- * @public
- * @param {number|string} dim
- * @param {number} [dataIndexInside=currDataIndexInside]
- * @return {number|string} value
- */
- function value(dim, dataIndexInside) {
- dataIndexInside == null && (dataIndexInside = currDataIndexInside);
- return data.get(data.getDimension(dim || 0), dataIndexInside);
- }
- /**
- * By default, `visual` is applied to style (to support visualMap).
- * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`,
- * it can be implemented as:
- * `api.style({stroke: api.visual('color'), fill: null})`;
- * @public
- * @param {Object} [extra]
- * @param {number} [dataIndexInside=currDataIndexInside]
- */
- function style(extra, dataIndexInside) {
- dataIndexInside == null && (dataIndexInside = currDataIndexInside);
- updateCache(dataIndexInside);
- var itemStyle = currItemModel.getModel(ITEM_STYLE_NORMAL_PATH).getItemStyle();
- currVisualColor != null && (itemStyle.fill = currVisualColor);
- var opacity = data.getItemVisual(dataIndexInside, 'opacity');
- opacity != null && (itemStyle.opacity = opacity);
- var labelModel = extra ? applyExtraBefore(extra, currLabelNormalModel) : currLabelNormalModel;
- graphicUtil.setTextStyle(itemStyle, labelModel, null, {
- autoColor: currVisualColor,
- isRectText: true
- });
- itemStyle.text = labelModel.getShallow('show') ? zrUtil.retrieve2(customSeries.getFormattedLabel(dataIndexInside, 'normal'), getDefaultLabel(data, dataIndexInside)) : null;
- extra && applyExtraAfter(itemStyle, extra);
- return itemStyle;
- }
- /**
- * @public
- * @param {Object} [extra]
- * @param {number} [dataIndexInside=currDataIndexInside]
- */
- function styleEmphasis(extra, dataIndexInside) {
- dataIndexInside == null && (dataIndexInside = currDataIndexInside);
- updateCache(dataIndexInside);
- var itemStyle = currItemModel.getModel(ITEM_STYLE_EMPHASIS_PATH).getItemStyle();
- var labelModel = extra ? applyExtraBefore(extra, currLabelEmphasisModel) : currLabelEmphasisModel;
- graphicUtil.setTextStyle(itemStyle, labelModel, null, {
- isRectText: true
- }, true);
- itemStyle.text = labelModel.getShallow('show') ? zrUtil.retrieve3(customSeries.getFormattedLabel(dataIndexInside, 'emphasis'), customSeries.getFormattedLabel(dataIndexInside, 'normal'), getDefaultLabel(data, dataIndexInside)) : null;
- extra && applyExtraAfter(itemStyle, extra);
- return itemStyle;
- }
- /**
- * @public
- * @param {string} visualType
- * @param {number} [dataIndexInside=currDataIndexInside]
- */
- function visual(visualType, dataIndexInside) {
- dataIndexInside == null && (dataIndexInside = currDataIndexInside);
- return data.getItemVisual(dataIndexInside, visualType);
- }
- /**
- * @public
- * @param {number} opt.count Positive interger.
- * @param {number} [opt.barWidth]
- * @param {number} [opt.barMaxWidth]
- * @param {number} [opt.barMinWidth]
- * @param {number} [opt.barGap]
- * @param {number} [opt.barCategoryGap]
- * @return {Object} {width, offset, offsetCenter} is not support, return undefined.
- */
- function barLayout(opt) {
- if (coordSys.getBaseAxis) {
- var baseAxis = coordSys.getBaseAxis();
- return getLayoutOnAxis(zrUtil.defaults({
- axis: baseAxis
- }, opt), api);
- }
- }
- /**
- * @public
- * @return {Array.<number>}
- */
- function currentSeriesIndices() {
- return ecModel.getCurrentSeriesIndices();
- }
- /**
- * @public
- * @param {Object} opt
- * @param {string} [opt.fontStyle]
- * @param {number} [opt.fontWeight]
- * @param {number} [opt.fontSize]
- * @param {string} [opt.fontFamily]
- * @return {string} font string
- */
- function font(opt) {
- return graphicUtil.getFont(opt, ecModel);
- }
- }
- function wrapEncodeDef(data) {
- var encodeDef = {};
- zrUtil.each(data.dimensions, function (dimName, dataDimIndex) {
- var dimInfo = data.getDimensionInfo(dimName);
- if (!dimInfo.isExtraCoord) {
- var coordDim = dimInfo.coordDim;
- var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || [];
- dataDims[dimInfo.coordDimIndex] = dataDimIndex;
- }
- });
- return encodeDef;
- }
- function createOrUpdate(el, dataIndex, elOption, animatableModel, group, data) {
- el = doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data, true);
- el && data.setItemGraphicEl(dataIndex, el);
- return el;
- }
- function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data, isRoot) {
- // [Rule]
- // By default, follow merge mode.
- // (It probably brings benifit for performance in some cases of large data, where
- // user program can be optimized to that only updated props needed to be re-calculated,
- // or according to `actionType` some calculation can be skipped.)
- // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing.
- // (It seems that violate the "merge" principle, but most of users probably intuitively
- // regard "return;" as "show nothing element whatever", so make a exception to meet the
- // most cases.)
- var simplyRemove = !elOption; // `null`/`undefined`/`false`
- elOption = elOption || {};
- var elOptionType = elOption.type;
- var elOptionShape = elOption.shape;
- var elOptionStyle = elOption.style;
- if (el && (simplyRemove // || elOption.$merge === false
- // If `elOptionType` is `null`, follow the merge principle.
- || elOptionType != null && elOptionType !== el.__customGraphicType || elOptionType === 'path' && hasOwnPathData(elOptionShape) && getPathData(elOptionShape) !== el.__customPathData || elOptionType === 'image' && hasOwn(elOptionStyle, 'image') && elOptionStyle.image !== el.__customImagePath // FIXME test and remove this restriction?
- || elOptionType === 'text' && hasOwn(elOptionShape, 'text') && elOptionStyle.text !== el.__customText)) {
- group.remove(el);
- el = null;
- } // `elOption.type` is undefined when `renderItem` returns nothing.
- if (simplyRemove) {
- return;
- }
- var isInit = !el;
- !el && (el = createEl(elOption));
- updateEl(el, dataIndex, elOption, animatableModel, data, isInit, isRoot);
- if (elOptionType === 'group') {
- mergeChildren(el, dataIndex, elOption, animatableModel, data);
- } // Always add whatever already added to ensure sequence.
- group.add(el);
- return el;
- } // Usage:
- // (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates that
- // the existing children will not be removed, and enables the feature that
- // update some of the props of some of the children simply by construct
- // the returned children of `renderItem` like:
- // `var children = group.children = []; children[3] = {opacity: 0.5};`
- // (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children
- // by child.name. But that might be lower performance.
- // (3) If `elOption.$mergeChildren` is `false`, the existing children will be
- // replaced totally.
- // (4) If `!elOption.children`, following the "merge" principle, nothing will happen.
- //
- // For implementation simpleness, do not provide a direct way to remove sinlge
- // child (otherwise the total indicies of the children array have to be modified).
- // User can remove a single child by set its `ignore` as `true` or replace
- // it by another element, where its `$merge` can be set as `true` if necessary.
- function mergeChildren(el, dataIndex, elOption, animatableModel, data) {
- var newChildren = elOption.children;
- var newLen = newChildren ? newChildren.length : 0;
- var mergeChildren = elOption.$mergeChildren; // `diffChildrenByName` has been deprecated.
- var byName = mergeChildren === 'byName' || elOption.diffChildrenByName;
- var notMerge = mergeChildren === false; // For better performance on roam update, only enter if necessary.
- if (!newLen && !byName && !notMerge) {
- return;
- }
- if (byName) {
- diffGroupChildren({
- oldChildren: el.children() || [],
- newChildren: newChildren || [],
- dataIndex: dataIndex,
- animatableModel: animatableModel,
- group: el,
- data: data
- });
- return;
- }
- notMerge && el.removeAll(); // Mapping children of a group simply by index, which
- // might be better performance.
- var index = 0;
- for (; index < newLen; index++) {
- newChildren[index] && doCreateOrUpdate(el.childAt(index), dataIndex, newChildren[index], animatableModel, el, data);
- }
- }
- function diffGroupChildren(context) {
- new DataDiffer(context.oldChildren, context.newChildren, getKey, getKey, context).add(processAddUpdate).update(processAddUpdate).remove(processRemove).execute();
- }
- function getKey(item, idx) {
- var name = item && item.name;
- return name != null ? name : GROUP_DIFF_PREFIX + idx;
- }
- function processAddUpdate(newIndex, oldIndex) {
- var context = this.context;
- var childOption = newIndex != null ? context.newChildren[newIndex] : null;
- var child = oldIndex != null ? context.oldChildren[oldIndex] : null;
- doCreateOrUpdate(child, context.dataIndex, childOption, context.animatableModel, context.group, context.data);
- } // `graphic#applyDefaultTextStyle` will cache
- // textFill, textStroke, textStrokeWidth.
- // We have to do this trick.
- function applyExtraBefore(extra, model) {
- var dummyModel = new Model({}, model);
- zrUtil.each(CACHED_LABEL_STYLE_PROPERTIES, function (stylePropName, modelPropName) {
- if (extra.hasOwnProperty(stylePropName)) {
- dummyModel.option[modelPropName] = extra[stylePropName];
- }
- });
- return dummyModel;
- }
- function applyExtraAfter(itemStyle, extra) {
- for (var key in extra) {
- if (extra.hasOwnProperty(key) || !CACHED_LABEL_STYLE_PROPERTIES.hasOwnProperty(key)) {
- itemStyle[key] = extra[key];
- }
- }
- }
- function processRemove(oldIndex) {
- var context = this.context;
- var child = context.oldChildren[oldIndex];
- child && context.group.remove(child);
- }
- function getPathData(shape) {
- // "d" follows the SVG convention.
- return shape && (shape.pathData || shape.d);
- }
- function hasOwnPathData(shape) {
- return shape && (shape.hasOwnProperty('pathData') || shape.hasOwnProperty('d'));
- }
- function hasOwn(host, prop) {
- return host && host.hasOwnProperty(prop);
- }
|