未验证 提交 76ae21d6 编写于 作者: W Wenli Zhang 提交者: GitHub

Merge pull request #14876 from apache/fix-legend-rotate

feat(legend): support legend.symbolRotate and inherit from series by default
......@@ -327,7 +327,7 @@ const NON_STYLE_VISUAL_PROPS = {
symbol: 1,
symbolSize: 1,
symbolKeepAspect: 1,
legendSymbol: 1,
legendIcon: 1,
visualMeta: 1,
liftZ: 1,
decal: 1
......
......@@ -41,7 +41,7 @@ import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
import type Polar from '../../coord/polar/Polar';
import {createSymbol, ECSymbol} from '../../util/symbol';
import {Group} from '../../util/graphic';
import {LegendSymbolParams} from '../../component/legend/LegendModel';
import {LegendIconParams} from '../../component/legend/LegendModel';
type LineDataValue = OptionDataValue | OptionDataValue[];
......@@ -210,7 +210,7 @@ class LineSeriesModel extends SeriesModel<LineSeriesOption> {
hoverLayerThreshold: Infinity
};
getLegendIcon(opt: LegendSymbolParams): ECSymbol | Group {
getLegendIcon(opt: LegendIconParams): ECSymbol | Group {
const group = new Group();
const line = createSymbol(
......@@ -226,6 +226,7 @@ class LineSeriesModel extends SeriesModel<LineSeriesOption> {
line.setStyle(opt.lineStyle);
const visualType = this.getData().getVisual('symbol');
const visualRotate = this.getData().getVisual('symbolRotate');
const symbolType = visualType === 'none' ? 'circle' : visualType;
// Symbol size is 80% when there is a line
......@@ -236,13 +237,18 @@ class LineSeriesModel extends SeriesModel<LineSeriesOption> {
(opt.itemHeight - size) / 2,
size,
size,
opt.itemStyle.fill,
opt.symbolKeepAspect
opt.itemStyle.fill
);
group.add(symbol);
symbol.setStyle(opt.itemStyle);
const symbolRotate = opt.iconRotate === 'inherit'
? visualRotate
: (opt.iconRotate || 0);
symbol.rotation = symbolRotate * Math.PI / 180;
symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);
if (symbolType.indexOf('empty') > -1) {
symbol.style.stroke = symbol.style.fill;
symbol.style.fill = '#fff';
......
......@@ -41,7 +41,7 @@ import Model from '../../model/Model';
import Geo from '../../coord/geo/Geo';
import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup';
import {createSymbol, ECSymbol} from '../../util/symbol';
import {LegendSymbolParams} from '../../component/legend/LegendModel';
import {LegendIconParams} from '../../component/legend/LegendModel';
import {Group} from '../../util/graphic';
export interface MapStateOption {
......@@ -227,28 +227,28 @@ class MapSeries extends SeriesModel<MapSeriesOption> {
this.option.center = center;
}
getLegendIcon(opt: LegendSymbolParams): ECSymbol | Group {
const symbolType = opt.symbolType || 'roundRect';
const symbol = createSymbol(
symbolType,
getLegendIcon(opt: LegendIconParams): ECSymbol | Group {
const iconType = opt.icon || 'roundRect';
const icon = createSymbol(
iconType,
0,
0,
opt.itemWidth,
opt.itemHeight,
opt.itemStyle.fill,
opt.symbolKeepAspect
opt.itemStyle.fill
);
symbol.setStyle(opt.itemStyle);
icon.setStyle(opt.itemStyle);
// Map do not use itemStyle.borderWidth as border width
symbol.style.stroke = 'none';
icon.style.stroke = 'none';
// No rotation because no series visual symbol for map
if (symbolType.indexOf('empty') > -1) {
symbol.style.stroke = symbol.style.fill;
symbol.style.fill = '#fff';
symbol.style.lineWidth = 2;
if (iconType.indexOf('empty') > -1) {
icon.style.stroke = icon.style.fill;
icon.style.fill = '#fff';
icon.style.lineWidth = 2;
}
return symbol;
return icon;
}
static defaultOption: MapSeriesOption = {
......
......@@ -66,7 +66,7 @@ export default function mapSymbolLayout(ecModel: GlobalModel) {
}
});
// Show label of those region not has legendSymbol(which is offset 0)
// Show label of those region not has legendIcon (which is offset 0)
const data = mapSeries.getData();
data.each(function (idx) {
const name = data.getName(idx);
......
......@@ -682,7 +682,7 @@ function resetLabelForRegion(
// In the following cases label will be drawn
// 1. In map series and data value is NaN
// 2. In geo component
// 3. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout
// 3. Region has no series legendIcon, which will be add a showLabel flag in mapSymbolLayout
if (
((isGeo || isDataNaN))
|| (itemLayout && itemLayout.showLabel)
......
......@@ -114,9 +114,7 @@ export interface LegendStyleOption {
textStyle?: LabelOption
symbolKeepAspect?: boolean
symbolSize?: number | 'auto' | 'inherit'
symbolRotate?: number | 'inherit'
}
interface DataItem extends LegendStyleOption {
......@@ -135,14 +133,14 @@ export interface LegendTooltipFormatterParams {
$vars: ['name']
}
export interface LegendSymbolParams {
export interface LegendIconParams {
itemWidth: number,
itemHeight: number,
/**
* symbolType is from legend.icon, legend.data.icon, or series visual
*/
symbolType: string,
symbolKeepAspect: boolean,
icon: string,
iconRotate: number | 'inherit',
itemStyle: PathStyleProps,
lineStyle: LineStyleProps
}
......@@ -455,7 +453,7 @@ class LegendModel<Ops extends LegendOption = LegendOption> extends ComponentMode
itemGap: 10,
itemWidth: 25,
itemHeight: 14,
symbolSize: 'auto',
symbolRotate: 'inherit',
inactiveColor: '#ccc',
inactiveBorderColor: '#ccc',
......
......@@ -32,7 +32,7 @@ import LegendModel, {
LegendLineStyleOption,
LegendOption,
LegendSelectorButtonOption,
LegendSymbolParams,
LegendIconParams,
LegendTooltipFormatterParams
} from './LegendModel';
import GlobalModel from '../../model/Global';
......@@ -207,7 +207,7 @@ class LegendView extends ComponentView {
if (seriesModel) {
const data = seriesModel.getData();
const lineVisualStyle = data.getVisual('legendLineStyle') || {};
const symbolType = data.getVisual('legendSymbol');
const legendIcon = data.getVisual('legendIcon');
/**
* `data.getVisual('style')` may be the color from the register
......@@ -215,12 +215,10 @@ class LegendView extends ComponentView {
*/
const style = data.getVisual('style');
data.getVisual('symbolSize');
const itemGroup = this._createItem(
seriesModel, name, dataIndex,
legendItemModel, legendModel, itemAlign,
lineVisualStyle, style, symbolType, selectMode
lineVisualStyle, style, legendIcon, selectMode
);
itemGroup.on('click', curry(dispatchSelectAction, name, null, api, excludeSeriesId))
......@@ -247,7 +245,7 @@ class LegendView extends ComponentView {
const idx = provider.indexOfName(name);
const style = provider.getItemVisual(idx, 'style') as PathStyleProps;
const symbolType = provider.getItemVisual(idx, 'legendSymbol');
const legendIcon = provider.getItemVisual(idx, 'legendIcon');
const colorArr = parse(style.fill as ColorString);
// Color may be set to transparent in visualMap when data is out of range.
......@@ -261,7 +259,7 @@ class LegendView extends ComponentView {
const itemGroup = this._createItem(
seriesModel, name, dataIndex,
legendItemModel, legendModel, itemAlign,
{}, style, symbolType, selectMode
{}, style, legendIcon, selectMode
);
// FIXME: consider different series has items with the same name.
......@@ -336,12 +334,12 @@ class LegendView extends ComponentView {
seriesModel: SeriesModel<SeriesOption & SymbolOptionMixin>,
name: string,
dataIndex: number,
itemModel: LegendModel['_data'][number],
legendItemModel: LegendModel['_data'][number],
legendModel: LegendModel,
itemAlign: LegendOption['align'],
lineVisualStyle: LineStyleProps,
itemVisualStyle: PathStyleProps,
symbolType: string,
legendIcon: string,
selectMode: LegendOption['selectedMode']
) {
const drawType = seriesModel.visualDrawType;
......@@ -349,14 +347,15 @@ class LegendView extends ComponentView {
const itemHeight = legendModel.get('itemHeight');
const isSelected = legendModel.isSelected(name);
const symbolKeepAspect = itemModel.get('symbolKeepAspect');
const legendIconType = itemModel.get('icon');
symbolType = legendIconType || symbolType || 'roundRect';
let iconRotate = legendItemModel.get('symbolRotate');
const legendIconType = legendItemModel.get('icon');
legendIcon = legendIconType || legendIcon || 'roundRect';
const legendLineStyle = legendModel.getModel('lineStyle');
const style = getLegendStyle(
symbolType,
itemModel,
legendIcon,
legendItemModel,
legendLineStyle,
lineVisualStyle,
itemVisualStyle,
......@@ -366,28 +365,34 @@ class LegendView extends ComponentView {
const itemGroup = new Group();
const textStyleModel = itemModel.getModel('textStyle');
const textStyleModel = legendItemModel.getModel('textStyle');
if (typeof seriesModel.getLegendIcon === 'function'
&& !legendIconType
&& (!legendIconType || legendIconType === 'inherit')
) {
// Series has specific way to define legend icon
itemGroup.add(seriesModel.getLegendIcon({
itemWidth,
itemHeight,
symbolType,
symbolKeepAspect,
icon: legendIcon,
iconRotate: iconRotate,
itemStyle: style.itemStyle,
lineStyle: style.lineStyle
}));
}
else {
// Use default legend icon policy for most series
const rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol')
? (iconRotate === 'inherit'
? seriesModel.getData().getVisual('symbolRotate')
: iconRotate
)
: 0; // No rotation for no icon
itemGroup.add(getDefaultLegendIcon({
itemWidth,
itemHeight,
symbolType,
symbolKeepAspect,
icon: legendIcon,
iconRotate: rotate,
itemStyle: style.itemStyle,
lineStyle: style.lineStyle
}));
......@@ -405,7 +410,7 @@ class LegendView extends ComponentView {
content = formatter(name);
}
const inactiveColor = itemModel.get('inactiveColor');
const inactiveColor = legendItemModel.get('inactiveColor');
itemGroup.add(new graphic.Text({
style: createTextStyle(textStyleModel, {
text: content,
......@@ -423,7 +428,7 @@ class LegendView extends ComponentView {
invisible: true
});
const tooltipModel = itemModel.getModel('tooltip') as Model<CommonTooltipOption<LegendTooltipFormatterParams>>;
const tooltipModel = legendItemModel.getModel('tooltip') as Model<CommonTooltipOption<LegendTooltipFormatterParams>>;
if (tooltipModel.get('show')) {
graphic.setTooltipConfig({
el: hitRect,
......@@ -532,7 +537,7 @@ class LegendView extends ComponentView {
}
function getLegendStyle(
symbolType: string,
iconType: string,
legendModel: LegendModel['_data'][number],
legendLineStyle: Model<LegendLineStyleOption>,
lineVisualStyle: LineStyleProps,
......@@ -569,11 +574,11 @@ function getLegendStyle(
case 'stroke':
/**
* symbol type with "emptyXXX" should use fill color
* icon type with "emptyXXX" should use fill color
* in visual style
*/
itemStyle.stroke = itemVisualStyle[
symbolType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke'
iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke'
];
break;
......@@ -632,7 +637,7 @@ function getLegendStyle(
* there is no border in series but border in legend, so we need to
* use border only when series has border if is set to be auto
*/
const visualHasBorder = itemStyle[symbolType.indexOf('empty') > -1 ? 'fill' : 'stroke'];
const visualHasBorder = itemStyle[iconType.indexOf('empty') > -1 ? 'fill' : 'stroke'];
itemStyle.lineWidth = borderWidth === 'auto'
? (itemVisualStyle.lineWidth > 0 && visualHasBorder ? 2 : 0)
: itemStyle.lineWidth;
......@@ -644,27 +649,29 @@ function getLegendStyle(
return { itemStyle, lineStyle };
}
function getDefaultLegendIcon(opt: LegendSymbolParams): ECSymbol {
const symboType = opt.symbolType || 'roundRect';
const symbol = createSymbol(
function getDefaultLegendIcon(opt: LegendIconParams): ECSymbol {
const symboType = opt.icon || 'roundRect';
const icon = createSymbol(
symboType,
0,
0,
opt.itemWidth,
opt.itemHeight,
opt.itemStyle.fill,
opt.symbolKeepAspect
opt.itemStyle.fill
);
symbol.setStyle(opt.itemStyle);
icon.setStyle(opt.itemStyle);
icon.rotation = (opt.iconRotate as number || 0) * Math.PI / 180;
icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);
if (symboType.indexOf('empty') > -1) {
symbol.style.stroke = symbol.style.fill;
symbol.style.fill = '#fff';
symbol.style.lineWidth = 2;
icon.style.stroke = icon.style.fill;
icon.style.fill = '#fff';
icon.style.lineWidth = 2;
}
return symbol;
return icon;
}
function dispatchSelectAction(
......
......@@ -34,10 +34,10 @@ export function install(registers: EChartsExtensionInstallRegisters) {
const data = seriesModel.getData();
// itemVisual symbol is for selected data
data.each(function (idx) {
data.setItemVisual(idx, 'legendSymbol', 'roundRect');
data.setItemVisual(idx, 'legendIcon', 'roundRect');
});
// visual is for unselected data
data.setVisual('legendSymbol', 'roundRect');
data.setVisual('legendIcon', 'roundRect');
}
});
}
......@@ -141,7 +141,7 @@ export interface DefaultDataVisual {
liftZ?: number
// For legend.
legendSymbol?: string
legendIcon?: string
legendLineStyle?: LineStyleProps
// visualMap will inject visualMeta data
......
......@@ -50,7 +50,7 @@ import { Source } from '../data/Source';
import { defaultSeriesFormatTooltip } from '../component/tooltip/seriesFormatTooltip';
import {ECSymbol} from '../util/symbol';
import {Group} from '../util/graphic';
import {LegendSymbolParams} from '../component/legend/LegendModel';
import {LegendIconParams} from '../component/legend/LegendModel';
const inner = modelUtil.makeInner<{
data: List
......@@ -96,7 +96,7 @@ interface SeriesModel {
/**
* Get legend icon symbol according to each series type
*/
getLegendIcon(opt: LegendSymbolParams): ECSymbol | Group;
getLegendIcon(opt: LegendIconParams): ECSymbol | Group;
/**
* See `component/brush/selector.js`
......@@ -175,7 +175,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
// Default symbol type.
defaultSymbol: string;
// Symbol provide to legend.
legendSymbol: string;
legendIcon: string;
// ---------------------------------------
// Props about data selection
......
......@@ -46,8 +46,8 @@ const seriesSymbolTask: StageHandler = {
) {
const data = seriesModel.getData();
if (seriesModel.legendSymbol) {
data.setVisual('legendSymbol', seriesModel.legendSymbol);
if (seriesModel.legendIcon) {
data.setVisual('legendIcon', seriesModel.legendIcon);
}
if (!seriesModel.hasSymbolVisual) {
......@@ -74,7 +74,7 @@ const seriesSymbolTask: StageHandler = {
const seriesSymbolOffset = !hasSymbolOffsetCallback ? symbolOffset : null;
data.setVisual({
legendSymbol: seriesModel.legendSymbol || seriesSymbol as string,
legendIcon: seriesModel.legendIcon || seriesSymbol as string,
// If seting callback functions on `symbol` or `symbolSize`, for simplicity and avoiding
// to bring trouble, we do not pick a reuslt from one of its calling on data item here,
// but just use the default value. Callback on `symbol` or `symbolSize` is convenient in
......
......@@ -75,7 +75,7 @@ under the License.
bottom: 10,
data: [{
name: 'Line D',
icon: 'rect'
icon: 'arrow'
}, {
name: 'Line E',
itemStyle: {
......@@ -100,7 +100,8 @@ under the License.
borderWidth: 5
},
symbolSize: 15,
symbol: 'emptyTriangle'
symbol: 'emptyTriangle',
symbolRotate: 15
}, {
data: getData(2),
type: 'line',
......@@ -201,8 +202,8 @@ under the License.
'Line A: the style of the line and items should be the same in those in legend; all colored in the first theme color',
'Line B: the style of the line and items should be the same in those in legend; all colored in red',
'Line C: the style of the line and items should be the same in those in legend; items colored in red and line colored in green',
'Line D: the style of the line should be the same in that in the legend; items colored orange in legend and blue in series',
'Line E: the style of the line should be the same in that in the legend; items colored pink in legend and green in series',
'Line D: orange arrow in legend and blue empty triangle in series',
'Line E: pink empty rectangale in legend and green empty circle in series',
'Line F: the colors in the legend should be blue'
],
option: option
......@@ -211,5 +212,68 @@ under the License.
</script>
<script>
require(['echarts'], function (echarts) {
var option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
legend: [{
data: [
'Line A',
'Line B',
{
name: 'Line C',
symbolRotate: -30,
icon: 'emptyTriangle'
},
'Bar D'
],
symbolRotate: 30,
itemWidth: 100
}],
series: [{
data: getData(0),
type: 'line',
name: 'Line A',
symbol: 'emptyRect'
}, {
data: getData(1),
type: 'line',
name: 'Line B',
symbol: 'emptyTriangle',
symbolRotate: 15,
symbolSize: 10,
symbolKeepAspect: true
}, {
data: getData(2),
type: 'line',
name: 'Line C',
symbol: 'emptyTriangle',
symbolKeepAspect: true
}, {
data: getData(0),
type: 'bar',
name: 'Bar D'
}],
animation: 0
};
var chart = testHelper.create(echarts, 'main2', {
title: [
'**Legend options should work**',
'Line A: the rect in the legend should be placed at the center with rotation of 30 degrees',
'Line B: the triangle in the legend should be placed at the center with rotation of 30 degrees',
'Line C: should not have rotate in legend',
'Bar D: should not have rotate in legend'
],
option: option
});
});
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册