echarts EffectLine 源码
echarts EffectLine 代码
文件路径:/src/chart/helper/EffectLine.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.
*/
/**
* Provide effect for line
*/
import * as graphic from '../../util/graphic';
import Line from './Line';
import * as zrUtil from 'zrender/src/core/util';
import {createSymbol} from '../../util/symbol';
import * as vec2 from 'zrender/src/core/vector';
import * as curveUtil from 'zrender/src/core/curve';
import type SeriesData from '../../data/SeriesData';
import { LineDrawSeriesScope, LineDrawModelOption } from './LineDraw';
import Model from '../../model/Model';
import { ColorString } from '../../util/types';
export type ECSymbolOnEffectLine = ReturnType<typeof createSymbol> & {
__t: number
__lastT: number
__p1: number[]
__p2: number[]
__cp1: number[]
};
class EffectLine extends graphic.Group {
private _symbolType: string;
private _period: number;
private _loop: boolean;
private _roundTrip: boolean;
private _symbolScale: number[];
constructor(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
super();
this.add(this.createLine(lineData, idx, seriesScope));
this._updateEffectSymbol(lineData, idx);
}
createLine(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope): graphic.Group {
return new Line(lineData, idx, seriesScope);
}
private _updateEffectSymbol(lineData: SeriesData, idx: number) {
const itemModel = lineData.getItemModel<LineDrawModelOption>(idx);
const effectModel = itemModel.getModel('effect');
let size = effectModel.get('symbolSize');
const symbolType = effectModel.get('symbol');
if (!zrUtil.isArray(size)) {
size = [size, size];
}
const lineStyle = lineData.getItemVisual(idx, 'style');
const color = effectModel.get('color') || (lineStyle && lineStyle.stroke);
let symbol = this.childAt(1) as ECSymbolOnEffectLine;
if (this._symbolType !== symbolType) {
// Remove previous
this.remove(symbol);
symbol = createSymbol(
symbolType, -0.5, -0.5, 1, 1, color
) as ECSymbolOnEffectLine;
symbol.z2 = 100;
symbol.culling = true;
this.add(symbol);
}
// Symbol may be removed if loop is false
if (!symbol) {
return;
}
// Shadow color is same with color in default
symbol.setStyle('shadowColor', color as ColorString);
symbol.setStyle(effectModel.getItemStyle(['color']));
symbol.scaleX = size[0];
symbol.scaleY = size[1];
symbol.setColor(color);
this._symbolType = symbolType;
this._symbolScale = size;
this._updateEffectAnimation(lineData, effectModel, idx);
}
private _updateEffectAnimation(
lineData: SeriesData,
effectModel: Model<LineDrawModelOption['effect']>,
idx: number
) {
const symbol = this.childAt(1) as ECSymbolOnEffectLine;
if (!symbol) {
return;
}
const points = lineData.getItemLayout(idx);
let period = effectModel.get('period') * 1000;
const loop = effectModel.get('loop');
const roundTrip = effectModel.get('roundTrip');
const constantSpeed = effectModel.get('constantSpeed');
const delayExpr = zrUtil.retrieve(effectModel.get('delay'), function (idx) {
return idx / lineData.count() * period / 3;
});
// Ignore when updating
symbol.ignore = true;
this._updateAnimationPoints(symbol, points);
if (constantSpeed > 0) {
period = this._getLineLength(symbol) / constantSpeed * 1000;
}
if (period !== this._period || loop !== this._loop || roundTrip !== this._roundTrip) {
symbol.stopAnimation();
let delayNum: number;
if (zrUtil.isFunction(delayExpr)) {
delayNum = delayExpr(idx);
}
else {
delayNum = delayExpr;
}
if (symbol.__t > 0) {
delayNum = -period * symbol.__t;
}
this._animateSymbol(
symbol, period, delayNum, loop, roundTrip
);
}
this._period = period;
this._loop = loop;
this._roundTrip = roundTrip;
}
private _animateSymbol(
symbol: ECSymbolOnEffectLine, period: number, delayNum: number, loop: boolean, roundTrip: boolean) {
if (period > 0) {
symbol.__t = 0;
const self = this;
const animator = symbol.animate('', loop)
.when(roundTrip ? period * 2 : period, {
__t: roundTrip ? 2 : 1
})
.delay(delayNum)
.during(function () {
self._updateSymbolPosition(symbol);
});
if (!loop) {
animator.done(function () {
self.remove(symbol);
});
}
animator.start();
}
}
protected _getLineLength(symbol: ECSymbolOnEffectLine) {
// Not so accurate
return (vec2.dist(symbol.__p1, symbol.__cp1)
+ vec2.dist(symbol.__cp1, symbol.__p2));
}
protected _updateAnimationPoints(symbol: ECSymbolOnEffectLine, points: number[][]) {
symbol.__p1 = points[0];
symbol.__p2 = points[1];
symbol.__cp1 = points[2] || [
(points[0][0] + points[1][0]) / 2,
(points[0][1] + points[1][1]) / 2
];
}
updateData(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
(this.childAt(0) as Line).updateData(lineData, idx, seriesScope);
this._updateEffectSymbol(lineData, idx);
}
protected _updateSymbolPosition(symbol: ECSymbolOnEffectLine) {
const p1 = symbol.__p1;
const p2 = symbol.__p2;
const cp1 = symbol.__cp1;
const t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t;
const pos = [symbol.x, symbol.y];
const lastPos = pos.slice();
const quadraticAt = curveUtil.quadraticAt;
const quadraticDerivativeAt = curveUtil.quadraticDerivativeAt;
pos[0] = quadraticAt(p1[0], cp1[0], p2[0], t);
pos[1] = quadraticAt(p1[1], cp1[1], p2[1], t);
// Tangent
const tx = symbol.__t < 1 ? quadraticDerivativeAt(p1[0], cp1[0], p2[0], t)
: quadraticDerivativeAt(p2[0], cp1[0], p1[0], 1 - t);
const ty = symbol.__t < 1 ? quadraticDerivativeAt(p1[1], cp1[1], p2[1], t)
: quadraticDerivativeAt(p2[1], cp1[1], p1[1], 1 - t);
symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2;
// enable continuity trail for 'line', 'rect', 'roundRect' symbolType
if (this._symbolType === 'line' || this._symbolType === 'rect' || this._symbolType === 'roundRect') {
if (symbol.__lastT !== undefined && symbol.__lastT < symbol.__t) {
symbol.scaleY = vec2.dist(lastPos, pos) * 1.05;
// make sure the last segment render within endPoint
if (t === 1) {
pos[0] = lastPos[0] + (pos[0] - lastPos[0]) / 2;
pos[1] = lastPos[1] + (pos[1] - lastPos[1]) / 2;
}
}
else if (symbol.__lastT === 1) {
// After first loop, symbol.__t does NOT start with 0, so connect p1 to pos directly.
symbol.scaleY = 2 * vec2.dist(p1, pos);
}
else {
symbol.scaleY = this._symbolScale[1];
}
}
symbol.__lastT = symbol.__t;
symbol.ignore = false;
symbol.x = pos[0];
symbol.y = pos[1];
}
updateLayout(lineData: SeriesData, idx: number) {
(this.childAt(0) as Line).updateLayout(lineData, idx);
const effectModel = lineData.getItemModel<LineDrawModelOption>(idx).getModel('effect');
this._updateEffectAnimation(lineData, effectModel, idx);
}
}
export default EffectLine;
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦