echarts Symbol 源码
echarts Symbol 代码
文件路径:/src/chart/helper/Symbol.ts
/*
* 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.
*/
import {createSymbol, normalizeSymbolOffset, normalizeSymbolSize} from '../../util/symbol';
import * as graphic from '../../util/graphic';
import {getECData} from '../../util/innerStore';
import { enterEmphasis, leaveEmphasis, toggleHoverEmphasis } from '../../util/states';
import {getDefaultLabel} from './labelHelper';
import SeriesData from '../../data/SeriesData';
import { ColorString, BlurScope, AnimationOption, ZRColor, AnimationOptionMixin } from '../../util/types';
import SeriesModel from '../../model/Series';
import { PathProps } from 'zrender/src/graphic/Path';
import { SymbolDrawSeriesScope, SymbolDrawItemModelOption } from './SymbolDraw';
import { extend } from 'zrender/src/core/util';
import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
import ZRImage from 'zrender/src/graphic/Image';
import { saveOldStyle } from '../../animation/basicTransition';
import Model from '../../model/Model';
type ECSymbol = ReturnType<typeof createSymbol>;
interface SymbolOpts {
disableAnimation?: boolean
useNameLabel?: boolean
symbolInnerColor?: ZRColor
}
class Symbol extends graphic.Group {
private _symbolType: string;
/**
* Original scale
*/
private _sizeX: number;
private _sizeY: number;
private _z2: number;
constructor(data: SeriesData, idx: number, seriesScope?: SymbolDrawSeriesScope, opts?: SymbolOpts) {
super();
this.updateData(data, idx, seriesScope, opts);
}
_createSymbol(
symbolType: string,
data: SeriesData,
idx: number,
symbolSize: number[],
keepAspect: boolean
) {
// Remove paths created before
this.removeAll();
// let 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.
const symbolPath = createSymbol(
symbolType, -1, -1, 2, 2, null, keepAspect
);
symbolPath.attr({
z2: 100,
culling: true,
scaleX: symbolSize[0] / 2,
scaleY: symbolSize[1] / 2
});
// Rewrite drift method
symbolPath.drift = driftSymbol;
this._symbolType = symbolType;
this.add(symbolPath);
}
/**
* Stop animation
* @param {boolean} toLastFrame
*/
stopSymbolAnimation(toLastFrame: boolean) {
this.childAt(0).stopAnimation(null, toLastFrame);
}
getSymbolType() {
return this._symbolType;
}
/**
* 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.
*/
getSymbolPath() {
return this.childAt(0) as ECSymbol;
}
/**
* Highlight symbol
*/
highlight() {
enterEmphasis(this.childAt(0));
}
/**
* Downplay symbol
*/
downplay() {
leaveEmphasis(this.childAt(0));
}
/**
* @param {number} zlevel
* @param {number} z
*/
setZ(zlevel: number, z: number) {
const symbolPath = this.childAt(0) as ECSymbol;
symbolPath.zlevel = zlevel;
symbolPath.z = z;
}
setDraggable(draggable: boolean, hasCursorOption?: boolean) {
const symbolPath = this.childAt(0) as ECSymbol;
symbolPath.draggable = draggable;
symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor;
}
/**
* Update symbol properties
*/
updateData(data: SeriesData, idx: number, seriesScope?: SymbolDrawSeriesScope, opts?: SymbolOpts) {
this.silent = false;
const symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
const seriesModel = data.hostModel as SeriesModel;
const symbolSize = Symbol.getSymbolSize(data, idx);
const isInit = symbolType !== this._symbolType;
const disableAnimation = opts && opts.disableAnimation;
if (isInit) {
const keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');
this._createSymbol(symbolType as string, data, idx, symbolSize, keepAspect);
}
else {
const symbolPath = this.childAt(0) as ECSymbol;
symbolPath.silent = false;
const target = {
scaleX: symbolSize[0] / 2,
scaleY: symbolSize[1] / 2
};
disableAnimation ? symbolPath.attr(target)
: graphic.updateProps(symbolPath, target, seriesModel, idx);
saveOldStyle(symbolPath);
}
this._updateCommon(data, idx, symbolSize, seriesScope, opts);
if (isInit) {
const symbolPath = this.childAt(0) as ECSymbol;
if (!disableAnimation) {
const target: PathProps = {
scaleX: this._sizeX,
scaleY: this._sizeY,
style: {
// Always fadeIn. Because it has fadeOut animation when symbol is removed..
opacity: symbolPath.style.opacity
}
};
symbolPath.scaleX = symbolPath.scaleY = 0;
symbolPath.style.opacity = 0;
graphic.initProps(symbolPath, target, seriesModel, idx);
}
}
if (disableAnimation) {
// Must stop leave transition manually if don't call initProps or updateProps.
this.childAt(0).stopAnimation('leave');
}
}
_updateCommon(
data: SeriesData,
idx: number,
symbolSize: number[],
seriesScope?: SymbolDrawSeriesScope,
opts?: SymbolOpts
) {
const symbolPath = this.childAt(0) as ECSymbol;
const seriesModel = data.hostModel as SeriesModel;
let emphasisItemStyle;
let blurItemStyle;
let selectItemStyle;
let focus;
let blurScope: BlurScope;
let emphasisDisabled: boolean;
let labelStatesModels;
let hoverScale: SymbolDrawSeriesScope['hoverScale'];
let cursorStyle: SymbolDrawSeriesScope['cursorStyle'];
if (seriesScope) {
emphasisItemStyle = seriesScope.emphasisItemStyle;
blurItemStyle = seriesScope.blurItemStyle;
selectItemStyle = seriesScope.selectItemStyle;
focus = seriesScope.focus;
blurScope = seriesScope.blurScope;
labelStatesModels = seriesScope.labelStatesModels;
hoverScale = seriesScope.hoverScale;
cursorStyle = seriesScope.cursorStyle;
emphasisDisabled = seriesScope.emphasisDisabled;
}
if (!seriesScope || data.hasItemOption) {
const itemModel = (seriesScope && seriesScope.itemModel)
? seriesScope.itemModel : data.getItemModel<SymbolDrawItemModelOption>(idx);
const emphasisModel = itemModel.getModel('emphasis');
emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();
selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();
blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();
focus = emphasisModel.get('focus');
blurScope = emphasisModel.get('blurScope');
emphasisDisabled = emphasisModel.get('disabled');
labelStatesModels = getLabelStatesModels(itemModel);
hoverScale = emphasisModel.getShallow('scale');
cursorStyle = itemModel.getShallow('cursor');
}
const symbolRotate = data.getItemVisual(idx, 'symbolRotate');
symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
const symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);
if (symbolOffset) {
symbolPath.x = symbolOffset[0];
symbolPath.y = symbolOffset[1];
}
cursorStyle && symbolPath.attr('cursor', cursorStyle);
const symbolStyle = data.getItemVisual(idx, 'style');
const visualColor = symbolStyle.fill;
if (symbolPath instanceof ZRImage) {
const pathStyle = symbolPath.style;
symbolPath.useStyle(extend({
// TODO other properties like x, y ?
image: pathStyle.image,
x: pathStyle.x, y: pathStyle.y,
width: pathStyle.width, height: pathStyle.height
}, symbolStyle));
}
else {
if (symbolPath.__isEmptyBrush) {
// fill and stroke will be swapped if it's empty.
// So we cloned a new style to avoid it affecting the original style in visual storage.
// TODO Better implementation. No empty logic!
symbolPath.useStyle(extend({}, symbolStyle));
}
else {
symbolPath.useStyle(symbolStyle);
}
// Disable decal because symbol scale will been applied on the decal.
symbolPath.style.decal = null;
symbolPath.setColor(visualColor, opts && opts.symbolInnerColor);
symbolPath.style.strokeNoScale = true;
}
const liftZ = data.getItemVisual(idx, 'liftZ');
const z2Origin = this._z2;
if (liftZ != null) {
if (z2Origin == null) {
this._z2 = symbolPath.z2;
symbolPath.z2 += liftZ;
}
}
else if (z2Origin != null) {
symbolPath.z2 = z2Origin;
this._z2 = null;
}
const useNameLabel = opts && opts.useNameLabel;
setLabelStyle(
symbolPath, labelStatesModels,
{
labelFetcher: seriesModel,
labelDataIndex: idx,
defaultText: getLabelDefaultText,
inheritColor: visualColor as ColorString,
defaultOpacity: symbolStyle.opacity
}
);
// Do not execute util needed.
function getLabelDefaultText(idx: number) {
return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);
}
this._sizeX = symbolSize[0] / 2;
this._sizeY = symbolSize[1] / 2;
const emphasisState = symbolPath.ensureState('emphasis');
emphasisState.style = emphasisItemStyle;
symbolPath.ensureState('select').style = selectItemStyle;
symbolPath.ensureState('blur').style = blurItemStyle;
// null / undefined / true means to use default strategy.
// 0 / false / negative number / NaN / Infinity means no scale.
const scaleRatio =
hoverScale == null || hoverScale === true
? Math.max(1.1, 3 / this._sizeY)
// PENDING: restrict hoverScale > 1? It seems unreasonable to scale down
: isFinite(hoverScale as number) && hoverScale > 0
? +hoverScale
: 1;
// always set scale to allow resetting
emphasisState.scaleX = this._sizeX * scaleRatio;
emphasisState.scaleY = this._sizeY * scaleRatio;
this.setSymbolScale(1);
toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);
}
setSymbolScale(scale: number) {
this.scaleX = this.scaleY = scale;
}
fadeOut(cb: () => void, seriesModel: Model<AnimationOptionMixin>, opt?: {
fadeLabel: boolean,
animation?: AnimationOption
}) {
const symbolPath = this.childAt(0) as ECSymbol;
const dataIndex = getECData(this).dataIndex;
const animationOpt = opt && opt.animation;
// Avoid mistaken hover when fading out
this.silent = symbolPath.silent = true;
// Not show text when animating
if (opt && opt.fadeLabel) {
const textContent = symbolPath.getTextContent();
if (textContent) {
graphic.removeElement(textContent, {
style: {
opacity: 0
}
}, seriesModel, {
dataIndex,
removeOpt: animationOpt,
cb() {
symbolPath.removeTextContent();
}
});
}
}
else {
symbolPath.removeTextContent();
}
graphic.removeElement(
symbolPath,
{
style: {
opacity: 0
},
scaleX: 0,
scaleY: 0
},
seriesModel,
{ dataIndex, cb, removeOpt: animationOpt}
);
}
static getSymbolSize(data: SeriesData, idx: number) {
return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
}
}
function driftSymbol(this: ECSymbol, dx: number, dy: number) {
this.parent.drift(dx, dy);
}
export default Symbol;
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦