You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
12 KiB
344 lines
12 KiB
1 month ago
|
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* 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.js';
|
||
|
import { parsePercent, linearMap } from '../../util/number.js';
|
||
|
import { isFunction } from 'zrender/lib/core/util.js';
|
||
|
function getViewRect(seriesModel, api) {
|
||
|
return layout.getLayoutRect(seriesModel.getBoxLayoutParams(), {
|
||
|
width: api.getWidth(),
|
||
|
height: api.getHeight()
|
||
|
});
|
||
|
}
|
||
|
function getSortedIndices(data, sort) {
|
||
|
var valueDim = data.mapDimension('value');
|
||
|
var valueArr = data.mapArray(valueDim, function (val) {
|
||
|
return val;
|
||
|
});
|
||
|
var indices = [];
|
||
|
var isAscending = sort === 'ascending';
|
||
|
for (var 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);
|
||
|
} else if (sort !== 'none') {
|
||
|
indices.sort(function (a, b) {
|
||
|
return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
|
||
|
});
|
||
|
}
|
||
|
return indices;
|
||
|
}
|
||
|
function labelLayout(data) {
|
||
|
var seriesModel = data.hostModel;
|
||
|
var orient = seriesModel.get('orient');
|
||
|
data.each(function (idx) {
|
||
|
var itemModel = data.getItemModel(idx);
|
||
|
var labelModel = itemModel.getModel('label');
|
||
|
var labelPosition = labelModel.get('position');
|
||
|
var labelLineModel = itemModel.getModel('labelLine');
|
||
|
var layout = data.getItemLayout(idx);
|
||
|
var points = layout.points;
|
||
|
var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';
|
||
|
var textAlign;
|
||
|
var textX;
|
||
|
var textY;
|
||
|
var 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 {
|
||
|
var x1 = void 0;
|
||
|
var y1 = void 0;
|
||
|
var x2 = void 0;
|
||
|
var y2 = void 0;
|
||
|
var labelLineLen = labelLineModel.get('length');
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition) > -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) > -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, api) {
|
||
|
ecModel.eachSeriesByType('funnel', function (seriesModel) {
|
||
|
var data = seriesModel.getData();
|
||
|
var valueDim = data.mapDimension('value');
|
||
|
var sort = seriesModel.get('sort');
|
||
|
var viewRect = getViewRect(seriesModel, api);
|
||
|
var orient = seriesModel.get('orient');
|
||
|
var viewWidth = viewRect.width;
|
||
|
var viewHeight = viewRect.height;
|
||
|
var indices = getSortedIndices(data, sort);
|
||
|
var x = viewRect.x;
|
||
|
var y = viewRect.y;
|
||
|
var sizeExtent = orient === 'horizontal' ? [parsePercent(seriesModel.get('minSize'), viewHeight), parsePercent(seriesModel.get('maxSize'), viewHeight)] : [parsePercent(seriesModel.get('minSize'), viewWidth), parsePercent(seriesModel.get('maxSize'), viewWidth)];
|
||
|
var dataExtent = data.getDataExtent(valueDim);
|
||
|
var min = seriesModel.get('min');
|
||
|
var max = seriesModel.get('max');
|
||
|
if (min == null) {
|
||
|
min = Math.min(dataExtent[0], 0);
|
||
|
}
|
||
|
if (max == null) {
|
||
|
max = dataExtent[1];
|
||
|
}
|
||
|
var funnelAlign = seriesModel.get('funnelAlign');
|
||
|
var gap = seriesModel.get('gap');
|
||
|
var viewSize = orient === 'horizontal' ? viewWidth : viewHeight;
|
||
|
var itemSize = (viewSize - gap * (data.count() - 1)) / data.count();
|
||
|
var getLinePoints = function (idx, offset) {
|
||
|
// End point index is data.count() and we assign it 0
|
||
|
if (orient === 'horizontal') {
|
||
|
var val_1 = data.get(valueDim, idx) || 0;
|
||
|
var itemHeight = linearMap(val_1, [min, max], sizeExtent, true);
|
||
|
var y0 = void 0;
|
||
|
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]];
|
||
|
}
|
||
|
var val = data.get(valueDim, idx) || 0;
|
||
|
var itemWidth = linearMap(val, [min, max], sizeExtent, true);
|
||
|
var 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 (var i = 0; i < indices.length; i++) {
|
||
|
var idx = indices[i];
|
||
|
var nextIdx = indices[i + 1];
|
||
|
var itemModel = data.getItemModel(idx);
|
||
|
if (orient === 'horizontal') {
|
||
|
var width = itemModel.get(['itemStyle', 'width']);
|
||
|
if (width == null) {
|
||
|
width = itemSize;
|
||
|
} else {
|
||
|
width = parsePercent(width, viewWidth);
|
||
|
if (sort === 'ascending') {
|
||
|
width = -width;
|
||
|
}
|
||
|
}
|
||
|
var start = getLinePoints(idx, x);
|
||
|
var end = getLinePoints(nextIdx, x + width);
|
||
|
x += width + gap;
|
||
|
data.setItemLayout(idx, {
|
||
|
points: start.concat(end.slice().reverse())
|
||
|
});
|
||
|
} else {
|
||
|
var height = itemModel.get(['itemStyle', 'height']);
|
||
|
if (height == null) {
|
||
|
height = itemSize;
|
||
|
} else {
|
||
|
height = parsePercent(height, viewHeight);
|
||
|
if (sort === 'ascending') {
|
||
|
height = -height;
|
||
|
}
|
||
|
}
|
||
|
var start = getLinePoints(idx, y);
|
||
|
var end = getLinePoints(nextIdx, y + height);
|
||
|
y += height + gap;
|
||
|
data.setItemLayout(idx, {
|
||
|
points: start.concat(end.slice().reverse())
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
labelLayout(data);
|
||
|
});
|
||
|
}
|