Skip to content

Commit 8de2b64

Browse files
committed
feature&fix(axis):
(1) feature: Enable uniform bandWidth calculation in numeric axis (e.g., for tooltip shadow); it previously only applicable to category axis, but buggy in numeric axis with bar series. And enable the clip of tooltip shadow. (2) refactor: Introduce a dedicated workflow phase for series aggregation and data statistics computation on a single axis, allowting the results to be reused across multiple features. (3) fix: Fix duplicate ticks in TimeScale and customValues, which cause jitter of splitArea. (4) fix: Fix category showMin/MaxLabel handling when step > 1 and showMin/MaxLabel: false (5) chore: Tweak bad effects introduced by the previous implementation of SCALE_EXTENT_KIND_MAPPING. (6) chore: Clean some code.
1 parent b16d96c commit 8de2b64

41 files changed

Lines changed: 1073 additions & 522 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎src/chart/bar/install.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import{EChartsExtensionInstallRegisters}from'../../extension';
2121
import*aszrUtilfrom'zrender/src/core/util';
22-
import{layout,createProgressiveLayout,registerBarGridAxisContainShapeHandler}from'../../layout/barGrid';
22+
import{layout,createProgressiveLayout,registerBarGridAxisHandlers}from'../../layout/barGrid';
2323
importdataSamplefrom'../../processor/dataSample';
2424

2525
importBarSeriesfrom'./BarSeries';
@@ -67,5 +67,5 @@ export function install(registers: EChartsExtensionInstallRegisters) {
6767
);
6868
});
6969

70-
registerBarGridAxisContainShapeHandler(registers);
70+
registerBarGridAxisHandlers(registers);
7171
}

‎src/chart/bar/installPictorialBar.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import{EChartsExtensionInstallRegisters}from'../../extension';
2121
importPictorialBarViewfrom'./PictorialBarView';
2222
importPictorialBarSeriesModelfrom'./PictorialBarSeries';
23-
import{createProgressiveLayout,layout,registerBarGridAxisContainShapeHandler}from'../../layout/barGrid';
23+
import{createProgressiveLayout,layout,registerBarGridAxisHandlers}from'../../layout/barGrid';
2424
import{curry}from'zrender/src/core/util';
2525

2626
exportfunctioninstall(registers: EChartsExtensionInstallRegisters){
@@ -31,5 +31,5 @@ export function install(registers: EChartsExtensionInstallRegisters) {
3131
// Do layout after other overall layout, which can prepare some information.
3232
registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT,createProgressiveLayout('pictorialBar'));
3333

34-
registerBarGridAxisContainShapeHandler(registers);
34+
registerBarGridAxisHandlers(registers);
3535
}

‎src/chart/line/LineView.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ function getIsIgnoreFunc(
399399

400400
zrUtil.each(categoryAxis.getViewLabels(),function(labelItem){
401401
constordinalNumber=(categoryAxis.scaleasOrdinalScale)
402-
.getRawOrdinalNumber(labelItem.tickValue);
402+
.getRawOrdinalNumber(labelItem.tick.value);
403403
labelMap[ordinalNumber]=1;
404404
});
405405

‎src/chart/sankey/sankeyLayout.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { GraphNode, GraphEdge } from '../../data/Graph';
2525
import{LayoutOrient}from'../../util/types';
2626
importGlobalModelfrom'../../model/Global';
2727
import{createBoxLayoutReference,getLayoutRect}from'../../util/layout';
28+
import{asc}from'../../util/number';
2829

2930
exportdefaultfunctionsankeyLayout(ecModel: GlobalModel,api: ExtensionAPI){
3031

@@ -290,9 +291,7 @@ function prepareNodesByBreadth(nodes: GraphNode[], orient: LayoutOrient) {
290291
constgroupResult=groupData(nodes,function(node){
291292
returnnode.getLayout()[keyAttr]asnumber;
292293
});
293-
groupResult.keys.sort(function(a,b){
294-
returna-b;
295-
});
294+
asc(groupResult.keys);
296295
zrUtil.each(groupResult.keys,function(key){
297296
nodesByBreadth.push(groupResult.buckets.get(key));
298297
});

‎src/component/axis/AngleAxisView.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ class AngleAxisView extends AxisView {
101101
labelItem=zrUtil.clone(labelItem);
102102
constscale=angleAxis.scale;
103103
consttickValue=scale.type==='ordinal'
104-
? (scaleasOrdinalScale).getRawOrdinalNumber(labelItem.tickValue)
105-
: labelItem.tickValue;
104+
? (scaleasOrdinalScale).getRawOrdinalNumber(labelItem.tick.value)
105+
: labelItem.tick.value;
106106
labelItem.coord=angleAxis.dataToCoord(tickValue);
107107
returnlabelItem;
108108
});
@@ -250,7 +250,7 @@ const angelAxisElementsBuilders: Record<typeof elementList[number], AngleAxisEle
250250
// Use length of ticksAngles because it may remove the last tick to avoid overlapping
251251
zrUtil.each(labels,function(labelItem,idx){
252252
letlabelModel=commonLabelModel;
253-
consttickValue=labelItem.tickValue;
253+
consttickValue=labelItem.tick.value;
254254

255255
constr=radiusExtent[getRadiusIdx(polar)];
256256
constp=polar.coordToPoint([r+labelMargin,labelItem.coord]);

‎src/component/axis/AxisBuilder.ts‎

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ import {
4444
DimensionName,
4545
}from'../../util/types';
4646
import{
47-
AxisBaseOption,AxisBaseOptionCommon,AxisLabelBaseOptionNuance
47+
AxisBaseOption,AxisBaseOptionCommon,AxisLabelBaseOptionNuance,
48+
AxisShowMinMaxLabelOption,
4849
}from'../../coord/axisCommonTypes';
4950
importtypeElementfrom'zrender/src/Element';
5051
import{PathProps,PathStyleProps}from'zrender/src/graphic/Path';
@@ -70,9 +71,11 @@ import BoundingRect from 'zrender/src/core/BoundingRect';
7071
importPointfrom'zrender/src/core/Point';
7172
import{copyTransform}from'zrender/src/core/Transformable';
7273
import{
74+
AxisLabelInfoDetermined,
7375
AxisLabelsComputingContext,AxisTickLabelComputingKind,createAxisLabelsComputingContext
7476
}from'../../coord/axisTickLabelBuilder';
7577
import{AxisTickCoord}from'../../coord/Axis';
78+
import{isTimeScale}from'../../scale/helper';
7679

7780

7881
constPI=Math.PI;
@@ -112,14 +115,13 @@ type AxisLabelText = graphic.Text & {
112115
}&ECElement;
113116

114117
exportconstgetLabelInner=makeInner<{
115-
break: VisualAxisBreak;
116-
tickValue: number;
118+
labelInfo: AxisLabelInfoDetermined;// Never be null/undefined.
117119
layoutRotation: number;
118120
},graphic.Text>();
119121

120122
constgetTickInner=makeInner<{
121-
onBand: AxisTickCoord['onBand']
122-
tickValue: AxisTickCoord['tickValue']
123+
onBand: AxisTickCoord['onBand'];
124+
tickValue: AxisTickCoord['tickValue'];
123125
},graphic.Line>();
124126

125127

@@ -1061,7 +1063,10 @@ function fixMinMaxLabelShow(
10611063
labelLayoutList: LabelLayoutData[],
10621064
optionHideOverlap: AxisBaseOption['axisLabel']['hideOverlap']
10631065
){
1064-
if(shouldShowAllLabels(axisModel.axis)){
1066+
constaxis=axisModel.axis;
1067+
constcustomValuesOption=axisModel.get(['axisLabel','customValues']);
1068+
1069+
if(shouldShowAllLabels(axis)){
10651070
return;
10661071
}
10671072

@@ -1070,7 +1075,7 @@ function fixMinMaxLabelShow(
10701075
// Assert no ignore in labels.
10711076

10721077
functiondeal(
1073-
showMinMaxLabel: boolean,
1078+
showMinMaxLabelOption: AxisShowMinMaxLabelOption,
10741079
outmostLabelIdx: number,
10751080
innerLabelIdx: number,
10761081
){
@@ -1079,8 +1084,18 @@ function fixMinMaxLabelShow(
10791084
if(!outmostLabelLayout||!innerLabelLayout){
10801085
return;
10811086
}
1087+
if(showMinMaxLabelOption==null){
1088+
if(!optionHideOverlap&&customValuesOption){
1089+
// In this case, users are unlikely to expect labels to be hidden.
1090+
return;
1091+
}
1092+
if(isTimeScale(axis.scale)&&getLabelInner(outmostLabelLayout.label).labelInfo.tick.notNice){
1093+
// TimeScale does not expand extent to "nice", so eliminate labels that are not nice.
1094+
ignoreEl(outmostLabelLayout.label);
1095+
}
1096+
}
10821097

1083-
if(showMinMaxLabel===false||outmostLabelLayout.suggestIgnore){
1098+
if(showMinMaxLabelOption===false||outmostLabelLayout.suggestIgnore){
10841099
ignoreEl(outmostLabelLayout.label);
10851100
return;
10861101
}
@@ -1107,7 +1122,7 @@ function fixMinMaxLabelShow(
11071122
innerLabelLayout=newLabelLayoutWithGeometry({marginForce},innerLabelLayout);
11081123
}
11091124
if(labelIntersect(outmostLabelLayout,innerLabelLayout,null,{touchThreshold})){
1110-
if(showMinMaxLabel){
1125+
if(showMinMaxLabelOption){
11111126
ignoreEl(innerLabelLayout.label);
11121127
}
11131128
else{
@@ -1119,11 +1134,11 @@ function fixMinMaxLabelShow(
11191134
// If min or max are user set, we need to check
11201135
// If the tick on min(max) are overlap on their neighbour tick
11211136
// If they are overlapped, we need to hide the min(max) tick label
1122-
constshowMinLabel=axisModel.get(['axisLabel','showMinLabel']);
1123-
constshowMaxLabel=axisModel.get(['axisLabel','showMaxLabel']);
1137+
constshowMinLabelOption=axisModel.get(['axisLabel','showMinLabel']);
1138+
constshowMaxLabelOption=axisModel.get(['axisLabel','showMaxLabel']);
11241139
constlabelsLen=labelLayoutList.length;
1125-
deal(showMinLabel,0,1);
1126-
deal(showMaxLabel,labelsLen-1,labelsLen-2);
1140+
deal(showMinLabelOption,0,1);
1141+
deal(showMaxLabelOption,labelsLen-1,labelsLen-2);
11271142
}
11281143

11291144
// PENDING: Is it necessary to display a tick while the corresponding label is ignored?
@@ -1146,7 +1161,7 @@ function syncLabelIgnoreToMajorTicks(
11461161
constlabelInner=getLabelInner(labelLayout.label);
11471162
if(tickInner.tickValue!=null
11481163
&&!tickInner.onBand
1149-
&&tickInner.tickValue===labelInner.tickValue
1164+
&&tickInner.tickValue===labelInner.labelInfo.tick.value
11501165
){
11511166
ignoreEl(tickEl);
11521167
return;
@@ -1355,9 +1370,11 @@ function buildAxisLabel(
13551370
letz2Max=-Infinity;
13561371

13571372
each(labels,function(labelItem,index){
1373+
constlabelItemTick=labelItem.tick;
1374+
constlabelItemTickValue=labelItemTick.value;
13581375
consttickValue=axis.scale.type==='ordinal'
1359-
? (axis.scaleasOrdinalScale).getRawOrdinalNumber(labelItem.tickValue)
1360-
: labelItem.tickValue;
1376+
? (axis.scaleasOrdinalScale).getRawOrdinalNumber(labelItemTickValue)
1377+
: labelItemTickValue;
13611378
constformattedLabel=labelItem.formattedLabel;
13621379
constrawLabel=labelItem.rawLabel;
13631380

@@ -1396,7 +1413,7 @@ function buildAxisLabel(
13961413
itemLabelModel.getShallow('verticalAlignMaxLabel',true),
13971414
verticalAlign
13981415
);
1399-
constz2=10+(labelItem.time?.level||0);
1416+
constz2=10+(labelItemTick.time?.level||0);
14001417
z2Min=Math.min(z2Min,z2);
14011418
z2Max=Math.max(z2Max,z2);
14021419

@@ -1443,8 +1460,7 @@ function buildAxisLabel(
14431460
textEl.anid='label_'+tickValue;
14441461

14451462
constinner=getLabelInner(textEl);
1446-
inner.break=labelItem.break;
1447-
inner.tickValue=tickValue;
1463+
inner.labelInfo=labelItem;
14481464
inner.layoutRotation=labelLayout.rotation;
14491465

14501466
graphic.setTooltipConfig({
@@ -1464,11 +1480,13 @@ function buildAxisLabel(
14641480
eventData.targetType='axisLabel';
14651481
eventData.value=rawLabel;
14661482
eventData.tickIndex=index;
1467-
if(labelItem.break){
1483+
constlabelItemTickBreak=labelItem.tick.break;
1484+
constlabelItemTickBreakParsedBreak=labelItemTickBreak.parsedBreak;
1485+
if(labelItemTickBreak){
14681486
eventData.break={
14691487
// type: labelItem.break.type,
1470-
start: labelItem.break.parsedBreak.vmin,
1471-
end: labelItem.break.parsedBreak.vmax,
1488+
start: labelItemTickBreakParsedBreak.vmin,
1489+
end: labelItemTickBreakParsedBreak.vmax,
14721490
};
14731491
}
14741492
if(axis.type==='category'){
@@ -1477,8 +1495,8 @@ function buildAxisLabel(
14771495

14781496
getECData(textEl).eventData=eventData;
14791497

1480-
if(labelItem.break){
1481-
addBreakEventHandler(axisModel,api,textEl,labelItem.break);
1498+
if(labelItemTickBreak){
1499+
addBreakEventHandler(axisModel,api,textEl,labelItemTickBreak);
14821500
}
14831501
}
14841502

@@ -1488,7 +1506,7 @@ function buildAxisLabel(
14881506

14891507
constlabelLayoutList=map(labelEls,label=>({
14901508
label,
1491-
priority: getLabelInner(label).break
1509+
priority: getLabelInner(label).labelInfo.tick.break
14921510
? label.z2+(z2Max-z2Min+1)// Make break labels be highest priority.
14931511
: label.z2,
14941512
defaultAttr: {
@@ -1537,7 +1555,7 @@ function updateAxisLabelChangableProps(
15371555
labelEl.ignore=false;
15381556

15391557
copyTransform(_tmpLayoutEl,_tmpLayoutElReset);
1540-
_tmpLayoutEl.x=axisModel.axis.dataToCoord(inner.tickValue);
1558+
_tmpLayoutEl.x=axisModel.axis.dataToCoord(inner.labelInfo.tick.value);
15411559
_tmpLayoutEl.y=cfg.labelOffset+cfg.labelDirection*labelMargin;
15421560
_tmpLayoutEl.rotation=inner.layoutRotation;
15431561

@@ -1590,7 +1608,7 @@ function adjustBreakLabels(
15901608
}
15911609
constbreakLabelIndexPairs=scaleBreakHelper.retrieveAxisBreakPairs(
15921610
labelLayoutList,
1593-
layoutInfo=>layoutInfo&&getLabelInner(layoutInfo.label).break,
1611+
layoutInfo=>layoutInfo&&getLabelInner(layoutInfo.label).labelInfo.tick.break,
15941612
true
15951613
);
15961614
constmoveOverlap=axisModel.get(['breakLabelLayout','moveOverlap'],true);

‎src/component/axis/axisSplitHelper.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type CartesianAxisView from './CartesianAxisView';
2626
importtypeSingleAxisModelfrom'../../coord/single/AxisModel';
2727
importtypeCartesianAxisModelfrom'../../coord/cartesian/AxisModel';
2828
importAxisViewfrom'./AxisView';
29+
importtype{AxisBaseModel}from'../../coord/AxisBaseModel';
2930

3031
constinner=makeInner<{
3132
// Hash map of color index
@@ -35,7 +36,7 @@ const inner = makeInner<{
3536
exportfunctionrectCoordAxisBuildSplitArea(
3637
axisView: SingleAxisView|CartesianAxisView,
3738
axisGroup: graphic.Group,
38-
axisModel: SingleAxisModel|CartesianAxisModel,
39+
axisModel: (SingleAxisModel|CartesianAxisModel)&AxisBaseModel,
3940
gridModel: GridModel|SingleAxisModel
4041
){
4142
constaxis=axisModel.axis;
@@ -44,8 +45,7 @@ export function rectCoordAxisBuildSplitArea(
4445
return;
4546
}
4647

47-
// TODO: TYPE
48-
constsplitAreaModel=(axisModelasCartesianAxisModel).getModel('splitArea');
48+
constsplitAreaModel=axisModel.getModel('splitArea');
4949
constareaStyleModel=splitAreaModel.getModel('areaStyle');
5050
letareaColors=areaStyleModel.get('color');
5151

@@ -107,7 +107,6 @@ export function rectCoordAxisBuildSplitArea(
107107

108108
consttickValue=ticksCoords[i-1].tickValue;
109109
tickValue!=null&&newSplitAreaColors.set(tickValue,colorIndex);
110-
111110
axisGroup.add(newgraphic.Rect({
112111
anid: tickValue!=null ? 'area_'+tickValue : null,
113112
shape: {

‎src/component/axisPointer/CartesianAxisPointer.ts‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import Grid from '../../coord/cartesian/Grid';
2727
importAxis2Dfrom'../../coord/cartesian/Axis2D';
2828
import{PathProps}from'zrender/src/graphic/Path';
2929
importModelfrom'../../model/Model';
30+
import{isNullableNumberFinite,mathMax,mathMin}from'../../util/number';
3031

3132
// Not use top level axisPointer model
3233
typeAxisPointerModel=Model<CommonAxisPointerOption>;
@@ -105,8 +106,8 @@ class CartesianAxisPointer extends BaseAxisPointer {
105106

106107
constcurrPosition=[transform.x,transform.y];
107108
currPosition[dimIndex]+=delta[dimIndex];
108-
currPosition[dimIndex]=Math.min(axisExtent[1],currPosition[dimIndex]);
109-
currPosition[dimIndex]=Math.max(axisExtent[0],currPosition[dimIndex]);
109+
currPosition[dimIndex]=mathMin(axisExtent[1],currPosition[dimIndex]);
110+
currPosition[dimIndex]=mathMax(axisExtent[0],currPosition[dimIndex]);
110111

111112
constcursorOtherValue=(otherExtent[1]+otherExtent[0])/2;
112113
constcursorPoint=[cursorOtherValue,cursorOtherValue];
@@ -156,13 +157,18 @@ const pointerShapeBuilder = {
156157
},
157158

158159
shadow: function(axis: Axis2D,pixelValue: number,otherExtent: number[]): PathProps&{type: 'Rect'}{
159-
constbandWidth=Math.max(1,axis.getBandWidth());
160-
constspan=otherExtent[1]-otherExtent[0];
160+
letbandWidth=axis.getBandWidth();
161+
constthisExtent=axis.getGlobalExtent();
162+
bandWidth=isNullableNumberFinite(bandWidth)
163+
? mathMax(1,bandWidth) : 1;
164+
constotherSpan=otherExtent[1]-otherExtent[0];
165+
constthisX=mathMax(thisExtent[0],pixelValue-bandWidth/2);
166+
constthisW=mathMin(thisX+bandWidth,thisExtent[1])-thisX;
161167
return{
162168
type: 'Rect',
163169
shape: viewHelper.makeRectShape(
164-
[pixelValue-bandWidth/2,otherExtent[0]],
165-
[bandWidth,span],
170+
[thisX,otherExtent[0]],
171+
[thisW,otherSpan],
166172
getAxisDimIndex(axis)
167173
)
168174
};

‎src/component/axisPointer/axisTrigger.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ function showTooltip(
368368
axisType: axisModel.type,
369369
axisId: axisModel.id,
370370
value: valueasnumber,
371-
// Caustion: viewHelper.getValueLabel is actually on "view stage", which
371+
// Caution: viewHelper.getValueLabel is actually on "view stage", which
372372
// depends that all models have been updated. So it should not be performed
373373
// here. Considering axisPointerModel used here is volatile, which is hard
374374
// to be retrieve in TooltipView, we prepare parameters here.

‎src/component/brush/preprocessor.ts‎

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { ECUnitOption, Dictionary } from '../../util/types';
2323
import{BrushOption,BrushToolboxIconType}from'./BrushModel';
2424
import{ToolboxOption}from'../toolbox/ToolboxModel';
2525
import{ToolboxBrushFeatureOption}from'../toolbox/feature/Brush';
26-
import{normalizeToArray}from'../../util/model';
26+
import{normalizeToArray,removeDuplicates}from'../../util/model';
2727

2828
constDEFAULT_TOOLBOX_BTNS: BrushToolboxIconType[]=['rect','polygon','keep','clear'];
2929

@@ -61,20 +61,9 @@ export default function brushPreprocessor(option: ECUnitOption, isNew: boolean):
6161

6262
brushTypes.push.apply(brushTypes,brushComponentSpecifiedBtns);
6363

64-
removeDuplicate(brushTypes);
64+
removeDuplicates(brushTypes,item=>item+'',null);
6565

6666
if(isNew&&!brushTypes.length){
6767
brushTypes.push.apply(brushTypes,DEFAULT_TOOLBOX_BTNS);
6868
}
6969
}
70-
71-
functionremoveDuplicate(arr: string[]): void{
72-
constmap={}asDictionary<number>;
73-
zrUtil.each(arr,function(val){
74-
map[val]=1;
75-
});
76-
arr.length=0;
77-
zrUtil.each(map,function(flag,val){
78-
arr.push(val);
79-
});
80-
}

0 commit comments

Comments
 (0)