echarts SymbolDraw 源码

  • 2022-10-20
  • 浏览 (622)

echarts SymbolDraw 代码

文件路径:/src/chart/helper/SymbolDraw.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 * as graphic from '../../util/graphic';
import SymbolClz from './Symbol';
import { isObject } from 'zrender/src/core/util';
import SeriesData from '../../data/SeriesData';
import type Displayable from 'zrender/src/graphic/Displayable';
import {
    StageHandlerProgressParams,
    LabelOption,
    SymbolOptionMixin,
    ItemStyleOption,
    ZRColor,
    AnimationOptionMixin,
    ZRStyleProps,
    StatesOptionMixin,
    BlurScope,
    DisplayState,
    DefaultEmphasisFocus
} from '../../util/types';
import { CoordinateSystemClipArea } from '../../coord/CoordinateSystem';
import Model from '../../model/Model';
import { ScatterSeriesOption } from '../scatter/ScatterSeries';
import { getLabelStatesModels } from '../../label/labelStyle';
import Element from 'zrender/src/Element';
import SeriesModel from '../../model/Series';

interface UpdateOpt {
    isIgnore?(idx: number): boolean
    clipShape?: CoordinateSystemClipArea,
    getSymbolPoint?(idx: number): number[]

    disableAnimation?: boolean
}

interface SymbolLike extends graphic.Group {
    updateData(data: SeriesData, idx: number, scope?: SymbolDrawSeriesScope, opt?: UpdateOpt): void
    fadeOut?(cb: () => void, seriesModel: SeriesModel): void
}

interface SymbolLikeCtor {
    new(data: SeriesData, idx: number, scope?: SymbolDrawSeriesScope, opt?: UpdateOpt): SymbolLike
}

function symbolNeedsDraw(data: SeriesData, point: number[], idx: number, opt: UpdateOpt) {
    return point && !isNaN(point[0]) && !isNaN(point[1])
        && !(opt.isIgnore && opt.isIgnore(idx))
        // We do not set clipShape on group, because it will cut part of
        // the symbol element shape. We use the same clip shape here as
        // the line clip.
        && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1]))
        && data.getItemVisual(idx, 'symbol') !== 'none';
}

function normalizeUpdateOpt(opt: UpdateOpt) {
    if (opt != null && !isObject(opt)) {
        opt = {isIgnore: opt};
    }
    return opt || {};
}

interface RippleEffectOption {
    period?: number
    /**
     * Scale of ripple
     */
    scale?: number

    brushType?: 'fill' | 'stroke'

    color?: ZRColor,

    /**
     * ripple number
     */
    number?: number
}

interface SymbolDrawStateOption {
    itemStyle?: ItemStyleOption
    label?: LabelOption
}

// TODO Separate series and item?
export interface SymbolDrawItemModelOption extends SymbolOptionMixin<object>,
    StatesOptionMixin<SymbolDrawStateOption, {
        emphasis?: {
            focus?: DefaultEmphasisFocus
            scale?: boolean | number
        }
    }>,
    SymbolDrawStateOption {

    cursor?: string

    // If has ripple effect
    rippleEffect?: RippleEffectOption
}

export interface SymbolDrawSeriesScope {
    emphasisItemStyle?: ZRStyleProps
    blurItemStyle?: ZRStyleProps
    selectItemStyle?: ZRStyleProps

    focus?: DefaultEmphasisFocus
    blurScope?: BlurScope
    emphasisDisabled?: boolean

    labelStatesModels: Record<DisplayState, Model<LabelOption>>

    itemModel?: Model<SymbolDrawItemModelOption>

    hoverScale?: boolean | number

    cursorStyle?: string
    fadeIn?: boolean
}

function makeSeriesScope(data: SeriesData): SymbolDrawSeriesScope {
    const seriesModel = data.hostModel as Model<ScatterSeriesOption>;
    const emphasisModel = seriesModel.getModel('emphasis');
    return {
        emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(),
        blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(),
        selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(),

        focus: emphasisModel.get('focus'),
        blurScope: emphasisModel.get('blurScope'),
        emphasisDisabled: emphasisModel.get('disabled'),

        hoverScale: emphasisModel.get('scale'),

        labelStatesModels: getLabelStatesModels(seriesModel),

        cursorStyle: seriesModel.get('cursor')
    };
}

export type ListForSymbolDraw = SeriesData<Model<SymbolDrawItemModelOption & AnimationOptionMixin>>;

class SymbolDraw {
    group = new graphic.Group();

    private _data: ListForSymbolDraw;

    private _SymbolCtor: SymbolLikeCtor;

    private _seriesScope: SymbolDrawSeriesScope;

    private _getSymbolPoint: UpdateOpt['getSymbolPoint'];

    private _progressiveEls: SymbolLike[];

    constructor(SymbolCtor?: SymbolLikeCtor) {
        this._SymbolCtor = SymbolCtor || SymbolClz as SymbolLikeCtor;
    }

    /**
     * Update symbols draw by new data
     */
    updateData(data: ListForSymbolDraw, opt?: UpdateOpt) {
        // Remove progressive els.
        this._progressiveEls = null;

        opt = normalizeUpdateOpt(opt);

        const group = this.group;
        const seriesModel = data.hostModel;
        const oldData = this._data;
        const SymbolCtor = this._SymbolCtor;
        const disableAnimation = opt.disableAnimation;

        const seriesScope = makeSeriesScope(data);

        const symbolUpdateOpt = { disableAnimation };

        const getSymbolPoint = opt.getSymbolPoint || function (idx: number) {
            return data.getItemLayout(idx);
        };


        // There is no oldLineData only when first rendering or switching from
        // stream mode to normal mode, where previous elements should be removed.
        if (!oldData) {
            group.removeAll();
        }

        data.diff(oldData)
            .add(function (newIdx) {
                const point = getSymbolPoint(newIdx);
                if (symbolNeedsDraw(data, point, newIdx, opt)) {
                    const symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);
                    symbolEl.setPosition(point);
                    data.setItemGraphicEl(newIdx, symbolEl);
                    group.add(symbolEl);
                }
            })
            .update(function (newIdx, oldIdx) {
                let symbolEl = oldData.getItemGraphicEl(oldIdx) as SymbolLike;

                const point = getSymbolPoint(newIdx) as number[];
                if (!symbolNeedsDraw(data, point, newIdx, opt)) {
                    group.remove(symbolEl);
                    return;
                }
                const newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle';
                const oldSymbolType = symbolEl
                    && (symbolEl as SymbolClz).getSymbolType
                    && (symbolEl as SymbolClz).getSymbolType();

                if (!symbolEl
                    // Create a new if symbol type changed.
                    || (oldSymbolType && oldSymbolType !== newSymbolType)
                ) {
                    group.remove(symbolEl);
                    symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);
                    symbolEl.setPosition(point);
                }
                else {
                    symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt);
                    const target = {
                        x: point[0],
                        y: point[1]
                    };
                    disableAnimation
                        ? symbolEl.attr(target)
                        : graphic.updateProps(symbolEl, target, seriesModel);
                }

                // Add back
                group.add(symbolEl);

                data.setItemGraphicEl(newIdx, symbolEl);
            })
            .remove(function (oldIdx) {
                const el = oldData.getItemGraphicEl(oldIdx) as SymbolLike;
                el && el.fadeOut(function () {
                    group.remove(el);
                }, seriesModel as SeriesModel);
            })
            .execute();

        this._getSymbolPoint = getSymbolPoint;
        this._data = data;
    };

    updateLayout() {
        const data = this._data;
        if (data) {
            // Not use animation
            data.eachItemGraphicEl((el, idx) => {
                const point = this._getSymbolPoint(idx);
                el.setPosition(point);
                el.markRedraw();
            });
        }
    };

    incrementalPrepareUpdate(data: ListForSymbolDraw) {
        this._seriesScope = makeSeriesScope(data);
        this._data = null;
        this.group.removeAll();
    };

    /**
     * Update symbols draw by new data
     */
    incrementalUpdate(taskParams: StageHandlerProgressParams, data: ListForSymbolDraw, opt?: UpdateOpt) {

        // Clear
        this._progressiveEls = [];

        opt = normalizeUpdateOpt(opt);

        function updateIncrementalAndHover(el: Displayable) {
            if (!el.isGroup) {
                el.incremental = true;
                el.ensureState('emphasis').hoverLayer = true;
            }
        }
        for (let idx = taskParams.start; idx < taskParams.end; idx++) {
            const point = data.getItemLayout(idx) as number[];
            if (symbolNeedsDraw(data, point, idx, opt)) {
                const el = new this._SymbolCtor(data, idx, this._seriesScope);
                el.traverse(updateIncrementalAndHover);
                el.setPosition(point);
                this.group.add(el);
                data.setItemGraphicEl(idx, el);
                this._progressiveEls.push(el);
            }
        }
    };

    eachRendered(cb: (el: Element) => boolean | void) {
        graphic.traverseElements(this._progressiveEls || this.group, cb);
    }

    remove(enableAnimation?: boolean) {
        const group = this.group;
        const data = this._data;
        // Incremental model do not have this._data.
        if (data && enableAnimation) {
            data.eachItemGraphicEl(function (el: SymbolLike) {
                el.fadeOut(function () {
                    group.remove(el);
                }, data.hostModel as SeriesModel);
            });
        }
        else {
            group.removeAll();
        }
    };

}

export default SymbolDraw;

相关信息

echarts 源码目录

相关文章

echarts EffectLine 源码

echarts EffectPolyline 源码

echarts EffectSymbol 源码

echarts LargeLineDraw 源码

echarts LargeSymbolDraw 源码

echarts Line 源码

echarts LineDraw 源码

echarts LinePath 源码

echarts Polyline 源码

echarts Symbol 源码

0  赞