Skip to content

Commit 6a4c1b1

Browse files
authored
fix: zIndex order for areas, lines and points (#290)
This commit change the rendering order of the line series and area series. For lines we render: for each series the line and each data point, instead of rendering first all the lines and then all the points. For areas, depending if the area is stacked or not: - if stacked, we render fist all the areas, than all the lines and than all the points (this will avoid having a lines partially rendered below the above area) - if non stacked, we render groups of areas, line, points. so each group id rendered on a different layer. This PR also remove completely the animation from lines and areas as its currently disabled in the library. We will reintegrate the animation in a future release. fix #287
1 parent 305a3bc commit 6a4c1b1

File tree

5 files changed

+189
-165
lines changed

5 files changed

+189
-165
lines changed

.playground/playgroud.tsx

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import {
55
Chart,
66
getAxisId,
77
getSpecId,
8-
LineSeries,
98
niceTimeFormatter,
109
Position,
1110
ScaleType,
1211
Settings,
1312
mergeWithDefaultTheme,
13+
AreaSeries,
1414
} from '../src';
1515
import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana';
1616

@@ -21,10 +21,6 @@ export class Playground extends React.Component {
2121
renderChart(legendPosition: Position) {
2222
const theme = mergeWithDefaultTheme({
2323
lineSeriesStyle: {
24-
// area: {
25-
// fill: 'green',
26-
// opacity:0.2
27-
// },
2824
line: {
2925
stroke: 'violet',
3026
strokeWidth: 4,
@@ -49,28 +45,64 @@ export class Playground extends React.Component {
4945
tickFormat={niceTimeFormatter([1555819200000, 1555905600000])}
5046
/>
5147
<Axis id={getAxisId('count')} title="count" position={Position.Left} tickFormat={(d) => d.toFixed(2)} />
52-
<LineSeries
53-
id={getSpecId('dataset A with long title')}
48+
49+
<AreaSeries
50+
id={getSpecId('dataset B')}
5451
xScaleType={ScaleType.Time}
5552
yScaleType={ScaleType.Linear}
56-
data={KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 15)}
53+
data={KIBANA_METRICS.metrics.kibana_os_load[1].data.slice(0, 15)}
5754
xAccessor={0}
58-
lineSeriesStyle={{
55+
yAccessors={[1]}
56+
stackAccessors={[0]}
57+
areaSeriesStyle={{
5958
line: {
60-
stroke: 'red',
61-
opacity: 1,
59+
// opacity:1,
60+
strokeWidth: 10,
61+
},
62+
point: {
63+
visible: true,
64+
strokeWidth: 3,
65+
radius: 10,
6266
},
6367
}}
64-
yAccessors={[1]}
6568
/>
66-
<LineSeries
67-
id={getSpecId('dataset B')}
69+
<AreaSeries
70+
id={getSpecId('dataset C')}
6871
xScaleType={ScaleType.Time}
6972
yScaleType={ScaleType.Linear}
7073
data={KIBANA_METRICS.metrics.kibana_os_load[1].data.slice(0, 15)}
7174
xAccessor={0}
7275
yAccessors={[1]}
7376
stackAccessors={[0]}
77+
areaSeriesStyle={{
78+
line: {
79+
// opacity:1,
80+
strokeWidth: 10,
81+
},
82+
point: {
83+
visible: true,
84+
strokeWidth: 3,
85+
radius: 10,
86+
},
87+
}}
88+
/>
89+
<AreaSeries
90+
id={getSpecId('dataset A with long title')}
91+
xScaleType={ScaleType.Time}
92+
yScaleType={ScaleType.Linear}
93+
data={KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 15)}
94+
xAccessor={0}
95+
areaSeriesStyle={{
96+
point: {
97+
visible: true,
98+
strokeWidth: 3,
99+
radius: 10,
100+
},
101+
line: {
102+
strokeWidth: 10,
103+
},
104+
}}
105+
yAccessors={[1]}
74106
/>
75107
</Chart>
76108
</div>

src/chart_types/xy_chart/rendering/rendering.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export interface AreaGeometry {
9292
seriesAreaStyle: AreaStyle;
9393
seriesAreaLineStyle: LineStyle;
9494
seriesPointStyle: PointStyle;
95+
isStacked: boolean;
9596
}
9697

9798
export function isPointGeometry(ig: IndexedGeometry): ig is PointGeometry {
@@ -411,6 +412,7 @@ export function renderArea(
411412
seriesKey: any[],
412413
xScaleOffset: number,
413414
seriesStyle: AreaSeriesStyle,
415+
isStacked: boolean = false,
414416
): {
415417
areaGeometry: AreaGeometry;
416418
indexedGeometries: Map<any, IndexedGeometry[]>;
@@ -469,6 +471,7 @@ export function renderArea(
469471
seriesAreaStyle: seriesStyle.area,
470472
seriesAreaLineStyle: seriesStyle.line,
471473
seriesPointStyle: seriesStyle.point,
474+
isStacked,
472475
};
473476
return {
474477
areaGeometry,
@@ -527,3 +530,7 @@ export function isPointOnGeometry(
527530
const { width, height } = indexedGeometry;
528531
return yCoordinate >= y && yCoordinate <= y + height && xCoordinate >= x && xCoordinate <= x + width;
529532
}
533+
534+
export function getGeometryIdKey(geometryId: GeometryId, prefix?: string, postfix?: string) {
535+
return `${prefix || ''}spec:${geometryId.specId}_${geometryId.seriesKey.join('::-::')}${postfix || ''}`;
536+
}

src/chart_types/xy_chart/store/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ export function renderGeometries(
503503
ds.key,
504504
xScaleOffset,
505505
areaSeriesStyle,
506+
isStacked,
506507
);
507508
areaGeometriesIndex = mergeGeometriesIndexes(areaGeometriesIndex, renderedAreas.indexedGeometries);
508509
areas.push(renderedAreas.areaGeometry);
Lines changed: 101 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { Group as KonvaGroup } from 'konva';
22
import React from 'react';
33
import { Circle, Group, Path } from 'react-konva';
4-
import { animated, Spring } from 'react-spring/renderprops-konva.cjs';
54
import { LegendItem } from '../../chart_types/xy_chart/legend/legend';
6-
import { AreaGeometry, getGeometryStyle, PointGeometry } from '../../chart_types/xy_chart/rendering/rendering';
5+
import {
6+
AreaGeometry,
7+
getGeometryStyle,
8+
PointGeometry,
9+
getGeometryIdKey,
10+
GeometryId,
11+
} from '../../chart_types/xy_chart/rendering/rendering';
712
import { SharedGeometryStyle } from '../../utils/themes/theme';
813
import {
914
buildAreaRenderProps,
@@ -38,110 +43,118 @@ export class AreaGeometries extends React.PureComponent<AreaGeometriesDataProps,
3843
return (
3944
<Group ref={this.barSeriesRef} key={'bar_series'}>
4045
{this.renderAreaGeoms()}
41-
{this.renderAreaLines()}
42-
{this.renderAreaPoints()}
4346
</Group>
4447
);
4548
}
46-
private renderAreaPoints = (): JSX.Element[] => {
47-
const { areas } = this.props;
48-
return areas.reduce(
49-
(acc, glyph, i) => {
50-
const { points, seriesPointStyle, color } = glyph;
51-
52-
if (!seriesPointStyle.visible) {
53-
return acc;
49+
private renderAreaGeoms = (): JSX.Element[] => {
50+
const { sharedStyle, highlightedLegendItem } = this.props;
51+
const areas = this.props.areas.reduce<{
52+
stacked: AreaGeometry[];
53+
nonStacked: AreaGeometry[];
54+
}>(
55+
(acc, area) => {
56+
if (area.isStacked) {
57+
acc.stacked.push(area);
58+
} else {
59+
acc.nonStacked.push(area);
5460
}
55-
56-
const pointStyleProps = buildPointStyleProps(color, seriesPointStyle);
57-
58-
return [...acc, ...this.renderPoints(points, i, pointStyleProps)];
61+
return acc;
5962
},
60-
[] as JSX.Element[],
63+
64+
{ stacked: [], nonStacked: [] },
6165
);
66+
67+
return [
68+
...this.renderStackedAreas(areas.stacked, sharedStyle, highlightedLegendItem),
69+
...this.renderNonStackedAreas(areas.nonStacked, sharedStyle, highlightedLegendItem),
70+
];
6271
};
63-
private renderPoints = (
64-
areaPoints: PointGeometry[],
65-
areaIndex: number,
66-
pointStyleProps: PointStyleProps,
72+
renderStackedAreas = (
73+
areas: AreaGeometry[],
74+
sharedStyle: SharedGeometryStyle,
75+
highlightedLegendItem: LegendItem | null,
6776
): JSX.Element[] => {
68-
const areaPointElements: JSX.Element[] = [];
69-
70-
areaPoints.forEach((areaPoint, pointIndex) => {
71-
const { x, y, transform } = areaPoint;
72-
const key = `area-point-${areaIndex}-${pointIndex}`;
73-
74-
if (this.props.animated) {
75-
areaPointElements.push(
76-
<Group key={`area-point-group-${areaIndex}-${pointIndex}`} x={transform.x}>
77-
<Spring native from={{ y }} to={{ y }}>
78-
{() => {
79-
const pointProps = buildPointRenderProps(x, y, pointStyleProps);
80-
return <animated.Circle {...pointProps} key={key} />;
81-
}}
82-
</Spring>
83-
</Group>,
84-
);
85-
} else {
86-
const pointProps = buildPointRenderProps(transform.x + x, y, pointStyleProps);
87-
areaPointElements.push(<Circle {...pointProps} key={key} />);
77+
const elements: JSX.Element[] = [];
78+
areas.forEach((glyph) => {
79+
const { seriesAreaStyle } = glyph;
80+
if (seriesAreaStyle.visible) {
81+
elements.push(this.renderArea(glyph, sharedStyle, highlightedLegendItem));
8882
}
8983
});
90-
return areaPointElements;
91-
};
92-
93-
private renderAreaGeoms = (): JSX.Element[] => {
94-
const { areas, sharedStyle } = this.props;
95-
const areasToRender: JSX.Element[] = [];
96-
9784
areas.forEach((glyph, i) => {
98-
const { area, color, transform, geometryId, seriesAreaStyle } = glyph;
99-
if (!seriesAreaStyle.visible) {
100-
return;
85+
const { seriesAreaLineStyle } = glyph;
86+
if (seriesAreaLineStyle.visible) {
87+
elements.push(...this.renderAreaLines(glyph, i, sharedStyle, highlightedLegendItem));
10188
}
102-
const customOpacity = seriesAreaStyle ? seriesAreaStyle.opacity : undefined;
103-
const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle, customOpacity);
104-
const key = `area-${i}`;
105-
if (this.props.animated) {
106-
areasToRender.push(
107-
<Group key={`area-group-${i}`} x={transform.x}>
108-
<Spring native from={{ area }} to={{ area }}>
109-
{(props: { area: string }) => {
110-
const areaProps = buildAreaRenderProps(0, props.area, color, seriesAreaStyle, geometryStyle);
111-
return <animated.Path {...areaProps} key={key} />;
112-
}}
113-
</Spring>
114-
</Group>,
115-
);
116-
} else {
117-
const areaProps = buildAreaRenderProps(transform.x, area, color, seriesAreaStyle, geometryStyle);
118-
areasToRender.push(<Path {...areaProps} key={key} />);
89+
});
90+
areas.forEach((glyph, i) => {
91+
const { seriesPointStyle } = glyph;
92+
if (seriesPointStyle.visible) {
93+
const pointStyleProps = buildPointStyleProps(glyph.color, seriesPointStyle);
94+
elements.push(...this.renderPoints(glyph.points, i, pointStyleProps, glyph.geometryId));
11995
}
12096
});
121-
return areasToRender;
97+
return elements;
12298
};
123-
private renderAreaLines = (): JSX.Element[] => {
124-
const { areas, sharedStyle } = this.props;
125-
const linesToRender: JSX.Element[] = [];
126-
areas.forEach((glyph, areaIndex) => {
127-
const { lines, color, geometryId, transform, seriesAreaLineStyle } = glyph;
128-
if (!seriesAreaLineStyle.visible) {
129-
return;
99+
renderNonStackedAreas = (
100+
areas: AreaGeometry[],
101+
sharedStyle: SharedGeometryStyle,
102+
highlightedLegendItem: LegendItem | null,
103+
): JSX.Element[] => {
104+
return areas.reduce<JSX.Element[]>((acc, glyph, i) => {
105+
const { seriesAreaLineStyle, seriesAreaStyle, seriesPointStyle } = glyph;
106+
if (seriesAreaStyle.visible) {
107+
acc.push(this.renderArea(glyph, sharedStyle, highlightedLegendItem));
108+
}
109+
if (seriesAreaLineStyle.visible) {
110+
acc.push(...this.renderAreaLines(glyph, i, sharedStyle, highlightedLegendItem));
111+
}
112+
if (seriesPointStyle.visible) {
113+
const pointStyleProps = buildPointStyleProps(glyph.color, seriesPointStyle);
114+
acc.push(...this.renderPoints(glyph.points, i, pointStyleProps, glyph.geometryId));
130115
}
116+
return acc;
117+
}, []);
118+
};
119+
private renderArea = (
120+
glyph: AreaGeometry,
121+
sharedStyle: SharedGeometryStyle,
122+
highlightedLegendItem: LegendItem | null,
123+
): JSX.Element => {
124+
const { area, color, transform, geometryId, seriesAreaStyle } = glyph;
125+
const customOpacity = seriesAreaStyle ? seriesAreaStyle.opacity : undefined;
126+
const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle, customOpacity);
127+
const key = getGeometryIdKey(geometryId, 'area-');
128+
const areaProps = buildAreaRenderProps(transform.x, area, color, seriesAreaStyle, geometryStyle);
129+
return <Path {...areaProps} key={key} />;
130+
};
131+
private renderAreaLines = (
132+
glyph: AreaGeometry,
133+
areaIndex: number,
134+
sharedStyle: SharedGeometryStyle,
135+
highlightedLegendItem: LegendItem | null,
136+
): JSX.Element[] => {
137+
const { lines, color, geometryId, transform, seriesAreaLineStyle } = glyph;
138+
const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle, seriesAreaLineStyle.opacity);
131139

132-
const geometryStyle = getGeometryStyle(
133-
geometryId,
134-
this.props.highlightedLegendItem,
135-
sharedStyle,
136-
seriesAreaLineStyle.opacity,
137-
);
140+
return lines.map((linePath, lineIndex) => {
141+
const key = getGeometryIdKey(geometryId, `area-line-${areaIndex}-${lineIndex}`);
142+
const lineProps = buildLineRenderProps(transform.x, linePath, color, seriesAreaLineStyle, geometryStyle);
143+
return <Path {...lineProps} key={key} />;
144+
});
145+
};
138146

139-
lines.forEach((linePath, lineIndex) => {
140-
const key = `area-${areaIndex}-line-${lineIndex}`;
141-
const lineProps = buildLineRenderProps(transform.x, linePath, color, seriesAreaLineStyle, geometryStyle);
142-
linesToRender.push(<Path {...lineProps} key={key} />);
143-
});
147+
private renderPoints = (
148+
areaPoints: PointGeometry[],
149+
areaIndex: number,
150+
pointStyleProps: PointStyleProps,
151+
geometryId: GeometryId,
152+
): JSX.Element[] => {
153+
return areaPoints.map((areaPoint, pointIndex) => {
154+
const { x, y, transform } = areaPoint;
155+
const key = getGeometryIdKey(geometryId, `area-point-${areaIndex}-${pointIndex}-`);
156+
const pointProps = buildPointRenderProps(transform.x + x, y, pointStyleProps);
157+
return <Circle {...pointProps} key={key} />;
144158
});
145-
return linesToRender;
146159
};
147160
}

0 commit comments

Comments
 (0)