Skip to content

Commit dbfaf6a

Browse files
committed
fix&feature:
(1) feature: Add option boxplot.clip (2) feature: Enable boxplot and candlestick containShape for "value"/"time"/"log" axis, enable proper shadow axisPointer. (candlestick-case.html, boxplot-multi.html) (3) fix: Fix candlestick ends shapes can not be displayed on "value"/"time" axis. (candlestick-case.html) (4) fix: candlestick shape width is inappropriate when datazoom filterMode is 'none'/'empty'. (candlestick-case.html) (5) fix: In polar coordinate system, support "value"/"time" axis as angle/radius axis, enable propere bandWidth for bar series, enable proper shadow axisPointer. (bar-polar-multi-series-radial.html, bar-polar-multi-series.html)
1 parent 7a8d38b commit dbfaf6a

64 files changed

Lines changed: 2522 additions & 1051 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/BarView.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ function getClipArea(coord: CoordSysOfBar, data: SeriesData) {
9999
// When boundaryGap is false in category axis, bar may exceed the grid.
100100
// We should not clip this part.
101101
// See test/bar2.html
102+
// PENDING: The effect is not preferable, but we preserve it for backward compatibility.
102103
if(baseAxis.type==='category'&&!baseAxis.onBand){
103104
constexpandWidth=data.getLayout('bandWidth');
104105
if(baseAxis.isHorizontal()){

‎src/chart/boxplot/BoxplotSeries.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface BoxplotSeriesOption
6565
coordinateSystem?: 'cartesian2d'
6666

6767
layout?: LayoutOrient
68+
clip?: boolean;
6869
/**
6970
* [min, max] can be percent of band width.
7071
*/
@@ -73,9 +74,11 @@ export interface BoxplotSeriesOption
7374
data?: (BoxplotDataValue|BoxplotDataItemOption)[]
7475
}
7576

77+
exportconstSERIES_TYPE_BOXPLOT='boxplot';
78+
7679
classBoxplotSeriesModelextendsSeriesModel<BoxplotSeriesOption>{
7780

78-
staticreadonlytype='series.boxplot';
81+
staticreadonlytype='series.'+SERIES_TYPE_BOXPLOT;
7982
readonlytype=BoxplotSeriesModel.type;
8083

8184
staticreadonlydependencies=['xAxis','yAxis','grid'];
@@ -109,6 +112,7 @@ class BoxplotSeriesModel extends SeriesModel<BoxplotSeriesOption> {
109112
legendHoverLink: true,
110113

111114
layout: null,
115+
clip: true,
112116
boxWidth: [7,50],
113117

114118
itemStyle: {

‎src/chart/boxplot/BoxplotView.ts‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,27 @@
1717
* under the License.
1818
*/
1919

20-
import*aszrUtilfrom'zrender/src/core/util';
2120
importChartViewfrom'../../view/Chart';
2221
import*asgraphicfrom'../../util/graphic';
2322
import{setStatesStylesFromModel,toggleHoverEmphasis}from'../../util/states';
2423
importPath,{PathProps}from'zrender/src/graphic/Path';
25-
importBoxplotSeriesModel,{BoxplotDataItemOption}from'./BoxplotSeries';
24+
importBoxplotSeriesModel,{SERIES_TYPE_BOXPLOT,BoxplotDataItemOption}from'./BoxplotSeries';
2625
importGlobalModelfrom'../../model/Global';
2726
importExtensionAPIfrom'../../core/ExtensionAPI';
2827
importSeriesDatafrom'../../data/SeriesData';
2928
import{BoxplotItemLayout}from'./boxplotLayout';
3029
import{saveOldStyle}from'../../animation/basicTransition';
30+
import{resolveNormalBoxClipping}from'../helper/whiskerBoxCommon';
31+
import{
32+
createClipPath,SHAPE_CLIP_KIND_FULLY_CLIPPED,SHAPE_CLIP_KIND_NOT_CLIPPED,
33+
SHAPE_CLIP_KIND_PARTIALLY_CLIPPED,
34+
updateClipPath
35+
}from'../helper/createClipPathFromCoordSys';
36+
import{map}from'zrender/src/core/util';
37+
3138

3239
classBoxplotViewextendsChartView{
33-
statictype='boxplot';
40+
statictype=SERIES_TYPE_BOXPLOT;
3441
type=BoxplotView.type;
3542

3643
private_data: SeriesData;
@@ -47,12 +54,29 @@ class BoxplotView extends ChartView {
4754
}
4855

4956
constconstDim=seriesModel.getWhiskerBoxesLayout()==='horizontal' ? 1 : 0;
57+
constneedClip=seriesModel.get('clip',true);
58+
constcoordSys=seriesModel.coordinateSystem;
59+
constclipArea=coordSys.getArea&&coordSys.getArea();
60+
constclipPath=needClip&&createClipPath(coordSys,false,seriesModel);
5061

5162
data.diff(oldData)
5263
.add(function(newIdx){
5364
if(data.hasValue(newIdx)){
5465
constitemLayout=data.getItemLayout(newIdx)asBoxplotItemLayout;
66+
67+
constclipKind=needClip
68+
? resolveNormalBoxClipping(clipArea,itemLayout) : SHAPE_CLIP_KIND_NOT_CLIPPED;
69+
if(clipKind===SHAPE_CLIP_KIND_FULLY_CLIPPED){
70+
return;
71+
}
72+
5573
constsymbolEl=createNormalBox(itemLayout,data,newIdx,constDim,true);
74+
// One axis tick can corresponds to a group of box items (from different series),
75+
// so it may be visually misleading when a group of items are partially outside
76+
// but no clipping is applied.
77+
// Consider performance of zr Element['clipPath'], only set to partially clipped elements.
78+
updateClipPath(clipKind===SHAPE_CLIP_KIND_PARTIALLY_CLIPPED,symbolEl,clipPath);
79+
5680
data.setItemGraphicEl(newIdx,symbolEl);
5781
group.add(symbolEl);
5882
}
@@ -67,6 +91,14 @@ class BoxplotView extends ChartView {
6791
}
6892

6993
constitemLayout=data.getItemLayout(newIdx)asBoxplotItemLayout;
94+
95+
constclipKind=needClip
96+
? resolveNormalBoxClipping(clipArea,itemLayout) : SHAPE_CLIP_KIND_NOT_CLIPPED;
97+
if(clipKind===SHAPE_CLIP_KIND_FULLY_CLIPPED){
98+
group.remove(symbolEl);
99+
return;
100+
}
101+
70102
if(!symbolEl){
71103
symbolEl=createNormalBox(itemLayout,data,newIdx,constDim);
72104
}
@@ -75,6 +107,9 @@ class BoxplotView extends ChartView {
75107
updateNormalBoxData(itemLayout,symbolEl,data,newIdx);
76108
}
77109

110+
// See `updateClipPath` in `add`.
111+
updateClipPath(clipKind===SHAPE_CLIP_KIND_PARTIALLY_CLIPPED,symbolEl,clipPath);
112+
78113
group.add(symbolEl);
79114

80115
data.setItemGraphicEl(newIdx,symbolEl);
@@ -192,7 +227,7 @@ function updateNormalBoxData(
192227
}
193228

194229
functiontransInit(points: number[][],dim: number,itemLayout: BoxplotItemLayout){
195-
returnzrUtil.map(points,function(point){
230+
returnmap(points,function(point){
196231
point=point.slice();
197232
point[dim]=itemLayout.initBaseline;
198233
returnpoint;

‎src/chart/boxplot/boxplotLayout.ts‎

Lines changed: 60 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -17,103 +17,69 @@
1717
* under the License.
1818
*/
1919

20-
import*aszrUtilfrom'zrender/src/core/util';
20+
import{isArray}from'zrender/src/core/util';
2121
import{parsePercent}from'../../util/number';
2222
importtypeGlobalModelfrom'../../model/Global';
23-
importBoxplotSeriesModelfrom'./BoxplotSeries';
24-
importAxis2Dfrom'../../coord/cartesian/Axis2D';
25-
26-
consteach=zrUtil.each;
27-
28-
interfaceGroupItem{
29-
seriesModels: BoxplotSeriesModel[]
30-
axis: Axis2D
31-
boxOffsetList: number[]
32-
boxWidthList: number[]
33-
}
23+
importBoxplotSeriesModel,{SERIES_TYPE_BOXPLOT}from'./BoxplotSeries';
24+
import{
25+
eachCollectedAxis,eachCollectedSeries,getCollectedSeriesLength,
26+
requireAxisStatistics
27+
}from'../../coord/axisStatistics';
28+
import{makeCallOnlyOnce}from'../../util/model';
29+
import{EChartsExtensionInstallRegisters}from'../../extension';
30+
importAxisfrom'../../coord/Axis';
31+
import{registerAxisContainShapeHandler}from'../../coord/scaleRawExtentInfo';
32+
import{calcBandWidth}from'../../coord/axisBand';
33+
import{
34+
makeAxisStatKey,
35+
createSimpleAxisStatClient,createBandWidthBasedAxisContainShapeHandler
36+
}from'../helper/axisSnippets';
37+
38+
39+
constcallOnlyOnce=makeCallOnlyOnce();
3440

3541
exportinterfaceBoxplotItemLayout{
3642
ends: number[][]
3743
initBaseline: number
3844
}
3945

40-
exportdefaultfunctionboxplotLayout(ecModel: GlobalModel){
41-
42-
constgroupResult=groupSeriesByAxis(ecModel);
43-
44-
each(groupResult,function(groupItem){
45-
constseriesModels=groupItem.seriesModels;
46-
47-
if(!seriesModels.length){
46+
exportfunctionboxplotLayout(ecModel: GlobalModel){
47+
constaxisStatKey=makeAxisStatKey(SERIES_TYPE_BOXPLOT);
48+
eachCollectedAxis(ecModel,axisStatKey,function(axis){
49+
constseriesCount=getCollectedSeriesLength(axis,axisStatKey);
50+
if(!seriesCount){
4851
return;
4952
}
50-
51-
calculateBase(groupItem);
52-
53-
each(seriesModels,function(seriesModel,idx){
53+
constbaseResult=calculateBase(axis,seriesCount);
54+
eachCollectedSeries(axis,axisStatKey,function(seriesModel: BoxplotSeriesModel,idx){
5455
layoutSingleSeries(
5556
seriesModel,
56-
groupItem.boxOffsetList[idx],
57-
groupItem.boxWidthList[idx]
57+
baseResult.boxOffsetList[idx],
58+
baseResult.boxWidthList[idx]
5859
);
5960
});
6061
});
6162
}
6263

63-
/**
64-
* Group series by axis.
65-
*/
66-
functiongroupSeriesByAxis(ecModel: GlobalModel){
67-
constresult: GroupItem[]=[];
68-
constaxisList: Axis2D[]=[];
69-
70-
ecModel.eachSeriesByType('boxplot',function(seriesModel: BoxplotSeriesModel){
71-
constbaseAxis=seriesModel.getBaseAxis();
72-
letidx=zrUtil.indexOf(axisList,baseAxis);
73-
74-
if(idx<0){
75-
idx=axisList.length;
76-
axisList[idx]=baseAxis;
77-
result[idx]={
78-
axis: baseAxis,
79-
seriesModels: []
80-
}asGroupItem;
81-
}
82-
83-
result[idx].seriesModels.push(seriesModel);
84-
});
85-
86-
returnresult;
87-
}
88-
8964
/**
9065
* Calculate offset and box width for each series.
9166
*/
92-
functioncalculateBase(groupItem: GroupItem){
93-
constbaseAxis=groupItem.axis;
94-
constseriesModels=groupItem.seriesModels;
95-
constseriesCount=seriesModels.length;
96-
97-
constboxWidthList: number[]=groupItem.boxWidthList=[];
98-
constboxOffsetList: number[]=groupItem.boxOffsetList=[];
67+
functioncalculateBase(baseAxis: Axis,seriesCount: number): {
68+
boxOffsetList: number[];
69+
boxWidthList: number[];
70+
}{
71+
constboxWidthList: number[]=[];
72+
constboxOffsetList: number[]=[];
9973
constboundList: number[][]=[];
10074

101-
letbandWidth: number;
102-
if(baseAxis.type==='category'){
103-
bandWidth=baseAxis.getBandWidth();
104-
}
105-
else{
106-
letmaxDataCount=0;
107-
each(seriesModels,function(seriesModel){
108-
maxDataCount=Math.max(maxDataCount,seriesModel.getData().count());
109-
});
110-
constextent=baseAxis.getExtent();
111-
bandWidth=Math.abs(extent[1]-extent[0])/maxDataCount;
112-
}
75+
constbandWidth=calcBandWidth(
76+
baseAxis,
77+
{fromStat: {key: makeAxisStatKey(SERIES_TYPE_BOXPLOT)},min: 1},
78+
).w;
11379

114-
each(seriesModels,function(seriesModel){
80+
eachCollectedSeries(baseAxis,makeAxisStatKey(SERIES_TYPE_BOXPLOT),function(seriesModel: BoxplotSeriesModel){
11581
letboxWidthBound=seriesModel.get('boxWidth');
116-
if(!zrUtil.isArray(boxWidthBound)){
82+
if(!isArray(boxWidthBound)){
11783
boxWidthBound=[boxWidthBound,boxWidthBound];
11884
}
11985
boundList.push([
@@ -127,14 +93,19 @@ function calculateBase(groupItem: GroupItem) {
12793
constboxWidth=(availableWidth-boxGap*(seriesCount-1))/seriesCount;
12894
letbase=boxWidth/2-availableWidth/2;
12995

130-
each(seriesModels,function(seriesModel,idx){
96+
eachCollectedSeries(baseAxis,makeAxisStatKey(SERIES_TYPE_BOXPLOT),function(seriesModel,idx){
13197
boxOffsetList.push(base);
13298
base+=boxGap+boxWidth;
13399

134100
boxWidthList.push(
135101
Math.min(Math.max(boxWidth,boundList[idx][0]),boundList[idx][1])
136102
);
137103
});
104+
105+
return{
106+
boxOffsetList,
107+
boxWidthList,
108+
};
138109
}
139110

140111
/**
@@ -212,3 +183,18 @@ function layoutSingleSeries(seriesModel: BoxplotSeriesModel, offset: number, box
212183
ends.push(from,to);
213184
}
214185
}
186+
187+
exportfunctionregisterBoxplotAxisHandlers(registers: EChartsExtensionInstallRegisters){
188+
callOnlyOnce(registers,function(){
189+
constaxisStatKey=makeAxisStatKey(SERIES_TYPE_BOXPLOT);
190+
requireAxisStatistics(
191+
registers,
192+
axisStatKey,
193+
createSimpleAxisStatClient(SERIES_TYPE_BOXPLOT)
194+
);
195+
registerAxisContainShapeHandler(
196+
axisStatKey,
197+
createBandWidthBasedAxisContainShapeHandler(axisStatKey)
198+
);
199+
});
200+
}

‎src/chart/boxplot/install.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
import{EChartsExtensionInstallRegisters}from'../../extension';
2121
importBoxplotSeriesModelfrom'./BoxplotSeries';
2222
importBoxplotViewfrom'./BoxplotView';
23-
importboxplotLayoutfrom'./boxplotLayout';
23+
import{boxplotLayout,registerBoxplotAxisHandlers}from'./boxplotLayout';
2424
import{boxplotTransform}from'./boxplotTransform';
2525

2626
exportfunctioninstall(registers: EChartsExtensionInstallRegisters){
2727
registers.registerSeriesModel(BoxplotSeriesModel);
2828
registers.registerChartView(BoxplotView);
2929
registers.registerLayout(boxplotLayout);
3030
registers.registerTransform(boxplotTransform);
31+
32+
registerBoxplotAxisHandlers(registers);
3133
}

‎src/chart/candlestick/CandlestickSeries.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,11 @@ export interface CandlestickSeriesOption
8282
data?: (CandlestickDataValue|CandlestickDataItemOption)[]
8383
}
8484

85+
exportconstSERIES_TYPE_CANDLESTICK='candlestick';
86+
8587
classCandlestickSeriesModelextendsSeriesModel<CandlestickSeriesOption>{
8688

87-
staticreadonlytype='series.candlestick';
89+
staticreadonlytype='series.'+SERIES_TYPE_CANDLESTICK;
8890
readonlytype=CandlestickSeriesModel.type;
8991

9092
staticreadonlydependencies=['xAxis','yAxis','grid'];

0 commit comments

Comments
 (0)