Skip to content

Commit 5d8e7cd

Browse files
committed
fix(dataZoom): keep neighbor rows when filtering line series so partial segments and null gaps survive. close #21564, close #21565
When dataZoom uses the default filterMode 'filter', AxisProxy calls selectRange() on the series store, which physically drops every row whose value falls outside the window. For line / area series this corrupts the rendering in two ways: - A line segment that crosses the window boundary loses one of its endpoints, so the visible portion of the segment vanishes entirely (#21564). - A null data point that interrupts the line gets dropped if its own value sits outside the window, so the line view no longer sees the gap and connects across it as a ghost line (#21565). This is most visible when data is non-monotonic in the filter dimension and the null is the only signal that two adjacent rows belong to different segments. Replace selectRange with a boundary-aware filter for line subType only: keep row i when row i, row i-1 or row i+1 is in the window. A single Uint8Array pre-scan computes inWindow status; filterSelf then applies the keep predicate. Other series types and other filter modes are untouched. Adds unit coverage for the partial-segment case, the null-gap case, the bar-series no-op, the filterMode: 'none' no-op, and the all-in-window short-circuit. Adds a visual reproducer at test/line-dataZoom-boundary.html.
1 parent dc60bdd commit 5d8e7cd

3 files changed

Lines changed: 362 additions & 0 deletions

File tree

src/component/dataZoom/AxisProxy.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,13 @@ class AxisProxy {
450450
})
451451
);
452452
}
453+
else if (shouldKeepLineBoundary(seriesModel)) {
454+
// Line / area series need one neighboring data row on each side
455+
// of the in-window range so partial line segments at the
456+
// boundary are still drawn (#21564) and null gap markers that
457+
// fall outside the window still interrupt the line (#21565).
458+
keepLineBoundary(seriesData, dim, valueWindow);
459+
}
453460
else {
454461
const range: Dictionary<[number, number]> = {};
455462
range[dim] = valueWindow as [number, number];
@@ -499,4 +506,45 @@ class AxisProxy {
499506
}
500507
}
501508

509+
function shouldKeepLineBoundary(seriesModel: SeriesModel): boolean {
510+
return seriesModel.subType === 'line';
511+
}
512+
513+
function keepLineBoundary(
514+
seriesData: ReturnType<SeriesModel['getData']>,
515+
dim: string,
516+
valueWindow: number[]
517+
): void {
518+
const store = seriesData.getStore();
519+
const dimIdx = seriesData.getDimensionIndex(dim);
520+
const count = store.count();
521+
if (!count || dimIdx < 0) {
522+
return;
523+
}
524+
525+
const min = valueWindow[0];
526+
const max = valueWindow[1];
527+
const inWindow = new Uint8Array(count);
528+
let allIn = true;
529+
for (let i = 0; i < count; i++) {
530+
const v = store.get(dimIdx, i) as number;
531+
const isIn = ((v >= min && v <= max) || isNaN(v)) ? 1 : 0;
532+
inWindow[i] = isIn;
533+
if (!isIn) {
534+
allIn = false;
535+
}
536+
}
537+
if (allIn) {
538+
return;
539+
}
540+
541+
seriesData.filterSelf(function (idx: number) {
542+
return !!(
543+
inWindow[idx]
544+
|| (idx > 0 && inWindow[idx - 1])
545+
|| (idx < count - 1 && inWindow[idx + 1])
546+
);
547+
});
548+
}
549+
502550
export default AxisProxy;

test/line-dataZoom-boundary.html

Lines changed: 137 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/ut/spec/component/dataZoom/lineBoundary.test.ts

Lines changed: 177 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)