echarts funnelLayout 源码
echarts funnelLayout 代码
文件路径:/src/chart/funnel/funnelLayout.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 layout from '../../util/layout';
import {parsePercent, linearMap} from '../../util/number';
import FunnelSeriesModel, { FunnelSeriesOption, FunnelDataItemOption } from './FunnelSeries';
import ExtensionAPI from '../../core/ExtensionAPI';
import SeriesData from '../../data/SeriesData';
import GlobalModel from '../../model/Global';
import { isFunction } from 'zrender/src/core/util';
function getViewRect(seriesModel: FunnelSeriesModel, api: ExtensionAPI) {
return layout.getLayoutRect(
seriesModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
}
);
}
function getSortedIndices(data: SeriesData, sort: FunnelSeriesOption['sort']) {
const valueDim = data.mapDimension('value');
const valueArr = data.mapArray(valueDim, function (val: number) {
return val;
});
const indices: number[] = [];
const isAscending = sort === 'ascending';
for (let i = 0, len = data.count(); i < len; i++) {
indices[i] = i;
}
// Add custom sortable function & none sortable opetion by "options.sort"
if (isFunction(sort)) {
indices.sort(sort as any);
}
else if (sort !== 'none') {
indices.sort(function (a, b) {
return isAscending
? valueArr[a] - valueArr[b]
: valueArr[b] - valueArr[a];
});
}
return indices;
}
function labelLayout(data: SeriesData) {
const seriesModel = data.hostModel;
const orient = seriesModel.get('orient');
data.each(function (idx) {
const itemModel = data.getItemModel<FunnelDataItemOption>(idx);
const labelModel = itemModel.getModel('label');
let labelPosition = labelModel.get('position');
const labelLineModel = itemModel.getModel('labelLine');
const layout = data.getItemLayout(idx);
const points = layout.points;
const isLabelInside = labelPosition === 'inner'
|| labelPosition === 'inside' || labelPosition === 'center'
|| labelPosition === 'insideLeft' || labelPosition === 'insideRight';
let textAlign;
let textX;
let textY;
let linePoints;
if (isLabelInside) {
if (labelPosition === 'insideLeft') {
textX = (points[0][0] + points[3][0]) / 2 + 5;
textY = (points[0][1] + points[3][1]) / 2;
textAlign = 'left';
}
else if (labelPosition === 'insideRight') {
textX = (points[1][0] + points[2][0]) / 2 - 5;
textY = (points[1][1] + points[2][1]) / 2;
textAlign = 'right';
}
else {
textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
textAlign = 'center';
}
linePoints = [
[textX, textY], [textX, textY]
];
}
else {
let x1;
let y1;
let x2;
let y2;
const labelLineLen = labelLineModel.get('length');
if (__DEV__) {
if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition as string) > -1) {
labelPosition = 'left';
console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.');
}
if (orient === 'horizontal' && ['left', 'right'].indexOf(labelPosition as string) > -1) {
labelPosition = 'bottom';
console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.');
}
}
if (labelPosition === 'left') {
// Left side
x1 = (points[3][0] + points[0][0]) / 2;
y1 = (points[3][1] + points[0][1]) / 2;
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
}
else if (labelPosition === 'right') {
// Right side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'left';
}
else if (labelPosition === 'top') {
// Top side
x1 = (points[3][0] + points[0][0]) / 2;
y1 = (points[3][1] + points[0][1]) / 2;
y2 = y1 - labelLineLen;
textY = y2 - 5;
textAlign = 'center';
}
else if (labelPosition === 'bottom') {
// Bottom side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
}
else if (labelPosition === 'rightTop') {
// RightTop side
x1 = orient === 'horizontal' ? points[3][0] : points[1][0];
y1 = orient === 'horizontal' ? points[3][1] : points[1][1];
if (orient === 'horizontal') {
y2 = y1 - labelLineLen;
textY = y2 - 5;
textAlign = 'center';
}
else {
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'top';
}
}
else if (labelPosition === 'rightBottom') {
// RightBottom side
x1 = points[2][0];
y1 = points[2][1];
if (orient === 'horizontal') {
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
}
else {
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'bottom';
}
}
else if (labelPosition === 'leftTop') {
// LeftTop side
x1 = points[0][0];
y1 = orient === 'horizontal' ? points[0][1] : points[1][1];
if (orient === 'horizontal') {
y2 = y1 - labelLineLen;
textY = y2 - 5;
textAlign = 'center';
}
else {
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
}
}
else if (labelPosition === 'leftBottom') {
// LeftBottom side
x1 = orient === 'horizontal' ? points[1][0] : points[3][0];
y1 = orient === 'horizontal' ? points[1][1] : points[2][1];
if (orient === 'horizontal') {
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
}
else {
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
}
}
else {
// Right side or Bottom side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
if (orient === 'horizontal') {
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
}
else {
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'left';
}
}
if (orient === 'horizontal') {
x2 = x1;
textX = x2;
}
else {
y2 = y1;
textY = y2;
}
linePoints = [[x1, y1], [x2, y2]];
}
layout.label = {
linePoints: linePoints,
x: textX,
y: textY,
verticalAlign: 'middle',
textAlign: textAlign,
inside: isLabelInside
};
});
}
export default function funnelLayout(ecModel: GlobalModel, api: ExtensionAPI) {
ecModel.eachSeriesByType('funnel', function (seriesModel: FunnelSeriesModel) {
const data = seriesModel.getData();
const valueDim = data.mapDimension('value');
const sort = seriesModel.get('sort');
const viewRect = getViewRect(seriesModel, api);
const orient = seriesModel.get('orient');
const viewWidth = viewRect.width;
const viewHeight = viewRect.height;
let indices = getSortedIndices(data, sort);
let x = viewRect.x;
let y = viewRect.y;
const sizeExtent = orient === 'horizontal' ? [
parsePercent(seriesModel.get('minSize'), viewHeight),
parsePercent(seriesModel.get('maxSize'), viewHeight)
] : [
parsePercent(seriesModel.get('minSize'), viewWidth),
parsePercent(seriesModel.get('maxSize'), viewWidth)
];
const dataExtent = data.getDataExtent(valueDim);
let min = seriesModel.get('min');
let max = seriesModel.get('max');
if (min == null) {
min = Math.min(dataExtent[0], 0);
}
if (max == null) {
max = dataExtent[1];
}
const funnelAlign = seriesModel.get('funnelAlign');
let gap = seriesModel.get('gap');
const viewSize = orient === 'horizontal' ? viewWidth : viewHeight;
let itemSize = (viewSize - gap * (data.count() - 1)) / data.count();
const getLinePoints = function (idx: number, offset: number) {
// End point index is data.count() and we assign it 0
if (orient === 'horizontal') {
const val = data.get(valueDim, idx) as number || 0;
const itemHeight = linearMap(val, [min, max], sizeExtent, true);
let y0;
switch (funnelAlign) {
case 'top':
y0 = y;
break;
case 'center':
y0 = y + (viewHeight - itemHeight) / 2;
break;
case 'bottom':
y0 = y + (viewHeight - itemHeight);
break;
}
return [
[offset, y0],
[offset, y0 + itemHeight]
];
}
const val = data.get(valueDim, idx) as number || 0;
const itemWidth = linearMap(val, [min, max], sizeExtent, true);
let x0;
switch (funnelAlign) {
case 'left':
x0 = x;
break;
case 'center':
x0 = x + (viewWidth - itemWidth) / 2;
break;
case 'right':
x0 = x + viewWidth - itemWidth;
break;
}
return [
[x0, offset],
[x0 + itemWidth, offset]
];
};
if (sort === 'ascending') {
// From bottom to top
itemSize = -itemSize;
gap = -gap;
if (orient === 'horizontal') {
x += viewWidth;
}
else {
y += viewHeight;
}
indices = indices.reverse();
}
for (let i = 0; i < indices.length; i++) {
const idx = indices[i];
const nextIdx = indices[i + 1];
const itemModel = data.getItemModel<FunnelDataItemOption>(idx);
if (orient === 'horizontal') {
let width = itemModel.get(['itemStyle', 'width']);
if (width == null) {
width = itemSize;
}
else {
width = parsePercent(width, viewWidth);
if (sort === 'ascending') {
width = -width;
}
}
const start = getLinePoints(idx, x);
const end = getLinePoints(nextIdx, x + width);
x += width + gap;
data.setItemLayout(idx, {
points: start.concat(end.slice().reverse())
});
}
else {
let height = itemModel.get(['itemStyle', 'height']);
if (height == null) {
height = itemSize;
}
else {
height = parsePercent(height, viewHeight);
if (sort === 'ascending') {
height = -height;
}
}
const start = getLinePoints(idx, y);
const end = getLinePoints(nextIdx, y + height);
y += height + gap;
data.setItemLayout(idx, {
points: start.concat(end.slice().reverse())
});
}
}
labelLayout(data);
});
}
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦