echarts LargeLineDraw 源码
echarts LargeLineDraw 代码
文件路径:/src/chart/helper/LargeLineDraw.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.
*/
// TODO Batch by color
import * as graphic from '../../util/graphic';
import * as lineContain from 'zrender/src/contain/line';
import * as quadraticContain from 'zrender/src/contain/quadratic';
import { PathProps } from 'zrender/src/graphic/Path';
import SeriesData from '../../data/SeriesData';
import { StageHandlerProgressParams, LineStyleOption, ColorString } from '../../util/types';
import Model from '../../model/Model';
import { getECData } from '../../util/innerStore';
import Element from 'zrender/src/Element';
class LargeLinesPathShape {
polyline = false;
curveness = 0;
segs: ArrayLike<number> = [];
}
interface LargeLinesPathProps extends PathProps {
shape?: Partial<LargeLinesPathShape>
}
interface LargeLinesCommonOption {
polyline?: boolean
lineStyle?: LineStyleOption & {
curveness?: number
}
}
/**
* Data which can support large lines.
*/
type LargeLinesData = SeriesData<Model<LargeLinesCommonOption> & {
seriesIndex?: number
}>;
class LargeLinesPath extends graphic.Path {
shape: LargeLinesPathShape;
__startIndex: number;
private _off: number = 0;
hoverDataIdx: number = -1;
notClear: boolean;
constructor(opts?: LargeLinesPathProps) {
super(opts);
}
reset() {
this.notClear = false;
this._off = 0;
}
getDefaultStyle() {
return {
stroke: '#000',
fill: null as ColorString
};
}
getDefaultShape() {
return new LargeLinesPathShape();
}
buildPath(ctx: CanvasRenderingContext2D, shape: LargeLinesPathShape) {
const segs = shape.segs;
const curveness = shape.curveness;
let i;
if (shape.polyline) {
for (i = this._off; i < segs.length;) {
const count = segs[i++];
if (count > 0) {
ctx.moveTo(segs[i++], segs[i++]);
for (let k = 1; k < count; k++) {
ctx.lineTo(segs[i++], segs[i++]);
}
}
}
}
else {
for (i = this._off; i < segs.length;) {
const x0 = segs[i++];
const y0 = segs[i++];
const x1 = segs[i++];
const y1 = segs[i++];
ctx.moveTo(x0, y0);
if (curveness > 0) {
const x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;
const y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;
ctx.quadraticCurveTo(x2, y2, x1, y1);
}
else {
ctx.lineTo(x1, y1);
}
}
}
if (this.incremental) {
this._off = i;
this.notClear = true;
}
}
findDataIndex(x: number, y: number) {
const shape = this.shape;
const segs = shape.segs;
const curveness = shape.curveness;
const lineWidth = this.style.lineWidth;
if (shape.polyline) {
let dataIndex = 0;
for (let i = 0; i < segs.length;) {
const count = segs[i++];
if (count > 0) {
const x0 = segs[i++];
const y0 = segs[i++];
for (let k = 1; k < count; k++) {
const x1 = segs[i++];
const y1 = segs[i++];
if (lineContain.containStroke(x0, y0, x1, y1, lineWidth, x, y)) {
return dataIndex;
}
}
}
dataIndex++;
}
}
else {
let dataIndex = 0;
for (let i = 0; i < segs.length;) {
const x0 = segs[i++];
const y0 = segs[i++];
const x1 = segs[i++];
const y1 = segs[i++];
if (curveness > 0) {
const x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;
const y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;
if (quadraticContain.containStroke(
x0, y0, x2, y2, x1, y1, lineWidth, x, y
)) {
return dataIndex;
}
}
else {
if (lineContain.containStroke(
x0, y0, x1, y1, lineWidth, x, y
)) {
return dataIndex;
}
}
dataIndex++;
}
}
return -1;
}
contain(x: number, y: number): boolean {
const localPos = this.transformCoordToLocal(x, y);
const rect = this.getBoundingRect();
x = localPos[0];
y = localPos[1];
if (rect.contain(x, y)) {
// Cache found data index.
const dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);
return dataIdx >= 0;
}
this.hoverDataIdx = -1;
return false;
}
getBoundingRect() {
// Ignore stroke for large symbol draw.
let rect = this._rect;
if (!rect) {
const shape = this.shape;
const points = shape.segs;
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (let i = 0; i < points.length;) {
const x = points[i++];
const y = points[i++];
minX = Math.min(x, minX);
maxX = Math.max(x, maxX);
minY = Math.min(y, minY);
maxY = Math.max(y, maxY);
}
rect = this._rect = new graphic.BoundingRect(minX, minY, maxX, maxY);
}
return rect;
}
}
class LargeLineDraw {
group = new graphic.Group();
private _newAdded: LargeLinesPath[];
/**
* Update symbols draw by new data
*/
updateData(data: LargeLinesData) {
this._clear();
const lineEl = this._create();
lineEl.setShape({
segs: data.getLayout('linesPoints')
});
this._setCommon(lineEl, data);
};
/**
* @override
*/
incrementalPrepareUpdate(data: LargeLinesData) {
this.group.removeAll();
this._clear();
};
/**
* @override
*/
incrementalUpdate(taskParams: StageHandlerProgressParams, data: LargeLinesData) {
const lastAdded = this._newAdded[0];
const linePoints = data.getLayout('linesPoints');
const oldSegs = lastAdded && lastAdded.shape.segs;
// Merging the exists. Each element has 1e4 points.
// Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)
if (oldSegs && oldSegs.length < 2e4) {
const oldLen = oldSegs.length;
const newSegs = new Float32Array(oldLen + linePoints.length);
// Concat two array
newSegs.set(oldSegs);
newSegs.set(linePoints, oldLen);
lastAdded.setShape({
segs: newSegs
});
}
else {
// Clear
this._newAdded = [];
const lineEl = this._create();
lineEl.incremental = true;
lineEl.setShape({
segs: linePoints
});
this._setCommon(lineEl, data);
lineEl.__startIndex = taskParams.start;
}
}
/**
* @override
*/
remove() {
this._clear();
}
eachRendered(cb: (el: Element) => boolean | void) {
this._newAdded[0] && cb(this._newAdded[0]);
}
private _create() {
const lineEl = new LargeLinesPath({
cursor: 'default',
ignoreCoarsePointer: true
});
this._newAdded.push(lineEl);
this.group.add(lineEl);
return lineEl;
}
private _setCommon(lineEl: LargeLinesPath, data: LargeLinesData, isIncremental?: boolean) {
const hostModel = data.hostModel;
lineEl.setShape({
polyline: hostModel.get('polyline'),
curveness: hostModel.get(['lineStyle', 'curveness'])
});
lineEl.useStyle(
hostModel.getModel('lineStyle').getLineStyle()
);
lineEl.style.strokeNoScale = true;
const style = data.getVisual('style');
if (style && style.stroke) {
lineEl.setStyle('stroke', style.stroke);
}
lineEl.setStyle('fill', null);
const ecData = getECData(lineEl);
// Enable tooltip
// PENDING May have performance issue when path is extremely large
ecData.seriesIndex = hostModel.seriesIndex;
lineEl.on('mousemove', function (e) {
ecData.dataIndex = null;
const dataIndex = lineEl.hoverDataIdx;
if (dataIndex > 0) {
// Provide dataIndex for tooltip
ecData.dataIndex = dataIndex + lineEl.__startIndex;
}
});
};
private _clear() {
this._newAdded = [];
this.group.removeAll();
};
}
export default LargeLineDraw;
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦