This is an automated email from the ASF dual-hosted git repository.

susiwen8 pushed a commit to branch codex/issue-21290-heatmap-empty
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 2e2c81c8d06718556bbb1eaea908a2392201599e
Author: susiwen8 <[email protected]>
AuthorDate: Thu Apr 9 00:02:22 2026 +0800

    fix(heatmap): render empty cells consistently
---
 src/chart/heatmap/HeatmapView.ts    | 32 +++++++++++----
 test/ut/spec/series/heatmap.test.ts | 82 +++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 8 deletions(-)

diff --git a/src/chart/heatmap/HeatmapView.ts b/src/chart/heatmap/HeatmapView.ts
index 4e21c179e..78a5b943f 100644
--- a/src/chart/heatmap/HeatmapView.ts
+++ b/src/chart/heatmap/HeatmapView.ts
@@ -21,6 +21,7 @@ import * as graphic from '../../util/graphic';
 import { toggleHoverEmphasis } from '../../util/states';
 import HeatmapLayer from './HeatmapLayer';
 import * as zrUtil from 'zrender/src/core/util';
+import tokens from '../../visual/tokens';
 import ChartView from '../../view/Chart';
 import HeatmapSeriesModel, { HeatmapDataItemOption } from './HeatmapSeries';
 import type GlobalModel from '../../model/Global';
@@ -29,7 +30,7 @@ import type VisualMapModel from 
'../../component/visualMap/VisualMapModel';
 import type PiecewiseModel from '../../component/visualMap/PiecewiseModel';
 import type ContinuousModel from '../../component/visualMap/ContinuousModel';
 import { CoordinateSystem, isCoordinateSystemType } from 
'../../coord/CoordinateSystem';
-import { StageHandlerProgressParams, Dictionary, OptionDataValue } from 
'../../util/types';
+import { StageHandlerProgressParams, Dictionary, OptionDataValue, ZRColor } 
from '../../util/types';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import type Calendar from '../../coord/calendar/Calendar';
 import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
@@ -130,11 +131,12 @@ class HeatmapView extends ChartView {
         this.group.removeAll();
 
         const coordSys = seriesModel.coordinateSystem;
+        const emptyCellFill: ZRColor = ecModel.get('backgroundColor') || 
tokens.color.neutral00;
         if (coordSys.type === 'cartesian2d'
             || coordSys.type === 'calendar'
             || coordSys.type === 'matrix'
         ) {
-            this._renderOnGridLike(seriesModel, api, 0, 
seriesModel.getData().count());
+            this._renderOnGridLike(seriesModel, api, emptyCellFill, 0, 
seriesModel.getData().count());
         }
         else if (isGeoCoordSys(coordSys)) {
             this._renderOnGeo(
@@ -154,6 +156,7 @@ class HeatmapView extends ChartView {
         api: ExtensionAPI
     ) {
         const coordSys = seriesModel.coordinateSystem;
+        const emptyCellFill: ZRColor = ecModel.get('backgroundColor') || 
tokens.color.neutral00;
         if (coordSys) {
             // geo does not support incremental rendering?
             if (isGeoCoordSys(coordSys)) {
@@ -161,7 +164,7 @@ class HeatmapView extends ChartView {
             }
             else {
                 this._progressiveEls = [];
-                this._renderOnGridLike(seriesModel, api, params.start, 
params.end, true);
+                this._renderOnGridLike(seriesModel, api, emptyCellFill, 
params.start, params.end, true);
             }
         }
     }
@@ -173,6 +176,7 @@ class HeatmapView extends ChartView {
     _renderOnGridLike(
         seriesModel: HeatmapSeriesModel,
         api: ExtensionAPI,
+        emptyCellFill: ZRColor,
         start: number,
         end: number,
         incremental?: boolean
@@ -236,10 +240,11 @@ class HeatmapView extends ChartView {
             if (isCartesian2d) {
                 const dataDimX = data.get(dataDims[0], idx);
                 const dataDimY = data.get(dataDims[1], idx);
+                const value = data.get(dataDims[2], idx) as number;
 
-                // Ignore empty data and out of extent data
-                if (isNaN(data.get(dataDims[2], idx) as number)
-                    || isNaN(dataDimX as number)
+                // Ignore out of extent data. Preserve empty cells so splitArea
+                // does not show through inconsistently behind them.
+                if (isNaN(dataDimX as number)
                     || isNaN(dataDimY as number)
                     || dataDimX < xAxisExtent[0]
                     || dataDimX > xAxisExtent[1]
@@ -253,6 +258,7 @@ class HeatmapView extends ChartView {
                     dataDimX,
                     dataDimY
                 ]);
+                const emptyCell = isNaN(value);
 
                 rect = new graphic.Rect({
                     shape: {
@@ -261,10 +267,15 @@ class HeatmapView extends ChartView {
                         width,
                         height
                     },
-                    style
+                    style: emptyCell
+                        ? zrUtil.extend(zrUtil.extend({}, style), {
+                            fill: emptyCellFill
+                        })
+                        : style
                 });
             }
             else if (isMatrix) {
+                const value = data.get(dataDims[2], idx) as number;
                 const shape = coordSys.dataToLayout([
                     data.get(dataDims[0], idx),
                     data.get(dataDims[1], idx)
@@ -272,10 +283,15 @@ class HeatmapView extends ChartView {
                 if (zrUtil.eqNaN(shape.x)) {
                     continue;
                 }
+                const emptyCell = isNaN(value);
                 rect = new graphic.Rect({
                     z2: 1,
                     shape,
-                    style,
+                    style: emptyCell
+                        ? zrUtil.extend(zrUtil.extend({}, style), {
+                            fill: emptyCellFill
+                        })
+                        : style,
                 });
             }
             else { // Calendar
diff --git a/test/ut/spec/series/heatmap.test.ts 
b/test/ut/spec/series/heatmap.test.ts
new file mode 100644
index 000000000..2c87802ec
--- /dev/null
+++ b/test/ut/spec/series/heatmap.test.ts
@@ -0,0 +1,82 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import { EChartsType } from '@/src/echarts';
+import { createChart, getGraphicElements } from '../../core/utHelper';
+
+describe('heatmap', function () {
+    let chart: EChartsType;
+
+    beforeEach(function () {
+        chart = createChart({
+            width: 240,
+            height: 160
+        });
+    });
+
+    afterEach(function () {
+        chart.dispose();
+    });
+
+    it('should render empty cells so splitArea does not leak through', 
function () {
+        chart.setOption({
+            backgroundColor: '#fff',
+            animation: false,
+            visualMap: {
+                min: 0,
+                max: 10,
+                show: false
+            },
+            grid: {
+                left: 20,
+                right: 20,
+                top: 20,
+                bottom: 20
+            },
+            xAxis: {
+                type: 'category',
+                data: ['a', 'b', 'c'],
+                splitArea: {
+                    show: true
+                }
+            },
+            yAxis: {
+                type: 'category',
+                data: ['row'],
+                splitArea: {
+                    show: true
+                }
+            },
+            series: [{
+                type: 'heatmap',
+                data: [
+                    [0, 0, 8],
+                    [1, 0, ''],
+                    [2, 0, '']
+                ]
+            }]
+        }, true);
+
+        const rects = getGraphicElements(chart, 'series')
+            .filter(el => el.type === 'rect');
+
+        expect(rects).toHaveLength(3);
+        expect(rects.filter(rect => (rect as any).style.fill === 
'#fff')).toHaveLength(2);
+    });
+});


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to