/* * 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 zrUtil = require("zrender/lib/core/util"); var _symbol = require("../../util/symbol"); var createSymbol = _symbol.createSymbol; var graphic = require("../../util/graphic"); var _number = require("../../util/number"); var parsePercent = _number.parsePercent; var _labelHelper = require("./labelHelper"); var getDefaultLabel = _labelHelper.getDefaultLabel; /* * 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. */ /** * @module echarts/chart/helper/Symbol */ /** * @constructor * @alias {module:echarts/chart/helper/Symbol} * @param {module:echarts/data/List} data * @param {number} idx * @extends {module:zrender/graphic/Group} */ function SymbolClz(data, idx, seriesScope) { graphic.Group.call(this); this.updateData(data, idx, seriesScope); } var symbolProto = SymbolClz.prototype; /** * @public * @static * @param {module:echarts/data/List} data * @param {number} dataIndex * @return {Array.} [width, height] */ var getSymbolSize = SymbolClz.getSymbolSize = function (data, idx) { var symbolSize = data.getItemVisual(idx, 'symbolSize'); return symbolSize instanceof Array ? symbolSize.slice() : [+symbolSize, +symbolSize]; }; function getScale(symbolSize) { return [symbolSize[0] / 2, symbolSize[1] / 2]; } function driftSymbol(dx, dy) { this.parent.drift(dx, dy); } symbolProto._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) { // Remove paths created before this.removeAll(); var color = data.getItemVisual(idx, 'color'); // var symbolPath = createSymbol( // symbolType, -0.5, -0.5, 1, 1, color // ); // If width/height are set too small (e.g., set to 1) on ios10 // and macOS Sierra, a circle stroke become a rect, no matter what // the scale is set. So we set width/height as 2. See #4150. var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, color, keepAspect); symbolPath.attr({ z2: 100, culling: true, scale: getScale(symbolSize) }); // Rewrite drift method symbolPath.drift = driftSymbol; this._symbolType = symbolType; this.add(symbolPath); }; /** * Stop animation * @param {boolean} toLastFrame */ symbolProto.stopSymbolAnimation = function (toLastFrame) { this.childAt(0).stopAnimation(toLastFrame); }; /** * FIXME: * Caution: This method breaks the encapsulation of this module, * but it indeed brings convenience. So do not use the method * unless you detailedly know all the implements of `Symbol`, * especially animation. * * Get symbol path element. */ symbolProto.getSymbolPath = function () { return this.childAt(0); }; /** * Get scale(aka, current symbol size). * Including the change caused by animation */ symbolProto.getScale = function () { return this.childAt(0).scale; }; /** * Highlight symbol */ symbolProto.highlight = function () { this.childAt(0).trigger('emphasis'); }; /** * Downplay symbol */ symbolProto.downplay = function () { this.childAt(0).trigger('normal'); }; /** * @param {number} zlevel * @param {number} z */ symbolProto.setZ = function (zlevel, z) { var symbolPath = this.childAt(0); symbolPath.zlevel = zlevel; symbolPath.z = z; }; symbolProto.setDraggable = function (draggable) { var symbolPath = this.childAt(0); symbolPath.draggable = draggable; symbolPath.cursor = draggable ? 'move' : 'pointer'; }; /** * Update symbol properties * @param {module:echarts/data/List} data * @param {number} idx * @param {Object} [seriesScope] * @param {Object} [seriesScope.itemStyle] * @param {Object} [seriesScope.hoverItemStyle] * @param {Object} [seriesScope.symbolRotate] * @param {Object} [seriesScope.symbolOffset] * @param {module:echarts/model/Model} [seriesScope.labelModel] * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel] * @param {boolean} [seriesScope.hoverAnimation] * @param {Object} [seriesScope.cursorStyle] * @param {module:echarts/model/Model} [seriesScope.itemModel] * @param {string} [seriesScope.symbolInnerColor] * @param {Object} [seriesScope.fadeIn=false] */ symbolProto.updateData = function (data, idx, seriesScope) { this.silent = false; var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; var seriesModel = data.hostModel; var symbolSize = getSymbolSize(data, idx); var isInit = symbolType !== this._symbolType; if (isInit) { var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect'); this._createSymbol(symbolType, data, idx, symbolSize, keepAspect); } else { var symbolPath = this.childAt(0); symbolPath.silent = false; graphic.updateProps(symbolPath, { scale: getScale(symbolSize) }, seriesModel, idx); } this._updateCommon(data, idx, symbolSize, seriesScope); if (isInit) { var symbolPath = this.childAt(0); var fadeIn = seriesScope && seriesScope.fadeIn; var target = { scale: symbolPath.scale.slice() }; fadeIn && (target.style = { opacity: symbolPath.style.opacity }); symbolPath.scale = [0, 0]; fadeIn && (symbolPath.style.opacity = 0); graphic.initProps(symbolPath, target, seriesModel, idx); } this._seriesModel = seriesModel; }; // Update common properties var normalStyleAccessPath = ['itemStyle']; var emphasisStyleAccessPath = ['emphasis', 'itemStyle']; var normalLabelAccessPath = ['label']; var emphasisLabelAccessPath = ['emphasis', 'label']; /** * @param {module:echarts/data/List} data * @param {number} idx * @param {Array.} symbolSize * @param {Object} [seriesScope] */ symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) { var symbolPath = this.childAt(0); var seriesModel = data.hostModel; var color = data.getItemVisual(idx, 'color'); // Reset style if (symbolPath.type !== 'image') { symbolPath.useStyle({ strokeNoScale: true }); } var itemStyle = seriesScope && seriesScope.itemStyle; var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle; var symbolRotate = seriesScope && seriesScope.symbolRotate; var symbolOffset = seriesScope && seriesScope.symbolOffset; var labelModel = seriesScope && seriesScope.labelModel; var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; var hoverAnimation = seriesScope && seriesScope.hoverAnimation; var cursorStyle = seriesScope && seriesScope.cursorStyle; if (!seriesScope || data.hasItemOption) { var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx); // Color must be excluded. // Because symbol provide setColor individually to set fill and stroke itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']); hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); symbolRotate = itemModel.getShallow('symbolRotate'); symbolOffset = itemModel.getShallow('symbolOffset'); labelModel = itemModel.getModel(normalLabelAccessPath); hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath); hoverAnimation = itemModel.getShallow('hoverAnimation'); cursorStyle = itemModel.getShallow('cursor'); } else { hoverItemStyle = zrUtil.extend({}, hoverItemStyle); } var elStyle = symbolPath.style; symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0); if (symbolOffset) { symbolPath.attr('position', [parsePercent(symbolOffset[0], symbolSize[0]), parsePercent(symbolOffset[1], symbolSize[1])]); } cursorStyle && symbolPath.attr('cursor', cursorStyle); // PENDING setColor before setStyle!!! symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor); symbolPath.setStyle(itemStyle); var opacity = data.getItemVisual(idx, 'opacity'); if (opacity != null) { elStyle.opacity = opacity; } var liftZ = data.getItemVisual(idx, 'liftZ'); var z2Origin = symbolPath.__z2Origin; if (liftZ != null) { if (z2Origin == null) { symbolPath.__z2Origin = symbolPath.z2; symbolPath.z2 += liftZ; } } else if (z2Origin != null) { symbolPath.z2 = z2Origin; symbolPath.__z2Origin = null; } var useNameLabel = seriesScope && seriesScope.useNameLabel; graphic.setLabelStyle(elStyle, hoverItemStyle, labelModel, hoverLabelModel, { labelFetcher: seriesModel, labelDataIndex: idx, defaultText: getLabelDefaultText, isRectText: true, autoColor: color }); // Do not execute util needed. function getLabelDefaultText(idx, opt) { return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx); } symbolPath.off('mouseover').off('mouseout').off('emphasis').off('normal'); symbolPath.hoverStyle = hoverItemStyle; // FIXME // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead. graphic.setHoverStyle(symbolPath); symbolPath.__symbolOriginalScale = getScale(symbolSize); if (hoverAnimation && seriesModel.isAnimationEnabled()) { // Note: consider `off`, should use static function here. symbolPath.on('mouseover', onMouseOver).on('mouseout', onMouseOut).on('emphasis', onEmphasis).on('normal', onNormal); } }; function onMouseOver() { // see comment in `graphic.isInEmphasis` !graphic.isInEmphasis(this) && onEmphasis.call(this); } function onMouseOut() { // see comment in `graphic.isInEmphasis` !graphic.isInEmphasis(this) && onNormal.call(this); } function onEmphasis() { // Do not support this hover animation util some scenario required. // Animation can only be supported in hover layer when using `el.incremetal`. if (this.incremental || this.useHoverLayer) { return; } var scale = this.__symbolOriginalScale; var ratio = scale[1] / scale[0]; this.animateTo({ scale: [Math.max(scale[0] * 1.1, scale[0] + 3), Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)] }, 400, 'elasticOut'); } function onNormal() { if (this.incremental || this.useHoverLayer) { return; } this.animateTo({ scale: this.__symbolOriginalScale }, 400, 'elasticOut'); } /** * @param {Function} cb * @param {Object} [opt] * @param {Object} [opt.keepLabel=true] */ symbolProto.fadeOut = function (cb, opt) { var symbolPath = this.childAt(0); // Avoid mistaken hover when fading out this.silent = symbolPath.silent = true; // Not show text when animating !(opt && opt.keepLabel) && (symbolPath.style.text = null); graphic.updateProps(symbolPath, { style: { opacity: 0 }, scale: [0, 0] }, this._seriesModel, this.dataIndex, cb); }; zrUtil.inherits(SymbolClz, graphic.Group); var _default = SymbolClz; module.exports = _default;