fix(grid): keep canvas backing store at observed pixel density under reduced UI scale

This commit is contained in:
TangT authored and GitHub committed 2026-08-06 14:09:21 +08:00
1 parent c398a07573
commit 122b606bd7
3 files changed
+25 -7

No files matched your search

@@ -97,6 +97,17 @@ describe("drawCanvasDataGrid with frozen columns", () => {
});
});
it.each([1, 1.125, 1.1875, 1.2345])("keeps the exact observed density when the nominal ratio is %s", (pixelRatio) => {
expect(
resolveCanvasBackingStoreMetrics({
width: 800,
height: 400,
pixelRatio,
devicePixelSize: { cssWidth: 800, cssHeight: 400, pixelWidth: 1000, pixelHeight: 500 },
}),
).toEqual({ pixelWidth: 1000, pixelHeight: 500, scaleX: 1.25, scaleY: 1.25, measured: true });
});
it("ignores a stale observed size after the CSS viewport changes", () => {
expect(
resolveCanvasBackingStoreMetrics({
@@ -108,17 +119,21 @@ describe("drawCanvasDataGrid with frozen columns", () => {
).toEqual({ pixelWidth: 1125, pixelHeight: 500, scaleX: 1.25, scaleY: 1.25, measured: false });
});
it("caps the observed backing store at the configured pixel ratio", () => {
it("caps the observed backing store only at the absolute safety limit", () => {
expect(
resolveCanvasBackingStoreMetrics({
width: 801,
height: 399,
pixelRatio: 4,
pixelRatio: 1.125,
devicePixelSize: { cssWidth: 801, cssHeight: 399, pixelWidth: 4005, pixelHeight: 1995 },
}),
).toEqual({ pixelWidth: 3204, pixelHeight: 1596, scaleX: 4, scaleY: 4, measured: true });
});
it("caps a fallback ratio at the absolute safety limit", () => {
expect(resolveCanvasBackingStoreMetrics({ width: 801, height: 399, pixelRatio: 5 })).toEqual({ pixelWidth: 3204, pixelHeight: 1596, scaleX: 4, scaleY: 4, measured: false });
});
it("draws without errors when frozenColumnCount is 0", () => {
const canvas = createMockCanvas();
const options = createBaseOptions({ canvas, frozenColumnCount: 0 });
@@ -3,6 +3,7 @@ import type { RowStatus } from "@/lib/dataGrid/gridRowStatus";
import { DATA_GRID_DARK_SEARCH_COLORS, resolveDataGridPaintTheme, type DataGridPaintTheme } from "@/lib/dataGrid/dataGridPaintTheme";
export const CANVAS_DATA_GRID_ROW_HEIGHT = 26;
export const MAX_CANVAS_DATA_GRID_PIXEL_RATIO = 4;
export interface CanvasDevicePixelSize {
cssWidth: number;
@@ -111,13 +112,15 @@ export interface CanvasBackingStoreMetrics {
export function resolveCanvasBackingStoreMetrics(options: { width: number; height: number; pixelRatio: number; devicePixelSize?: CanvasDevicePixelSize | null }): CanvasBackingStoreMetrics {
const width = Math.max(1, options.width);
const height = Math.max(1, options.height);
const fallbackRatio = Math.max(1, options.pixelRatio);
const fallbackRatio = Math.min(MAX_CANVAS_DATA_GRID_PIXEL_RATIO, Math.max(1, options.pixelRatio));
const measured = options.devicePixelSize;
const measurementMatches = !!measured && Math.abs(measured.cssWidth - width) <= 0.5 && Math.abs(measured.cssHeight - height) <= 0.5 && measured.pixelWidth > 0 && measured.pixelHeight > 0;
const fallbackPixelWidth = Math.max(1, Math.ceil(width * fallbackRatio));
const fallbackPixelHeight = Math.max(1, Math.ceil(height * fallbackRatio));
const pixelWidth = measurementMatches ? Math.min(measured.pixelWidth, fallbackPixelWidth) : fallbackPixelWidth;
const pixelHeight = measurementMatches ? Math.min(measured.pixelHeight, fallbackPixelHeight) : fallbackPixelHeight;
const maxPixelWidth = Math.max(1, Math.ceil(width * MAX_CANVAS_DATA_GRID_PIXEL_RATIO));
const maxPixelHeight = Math.max(1, Math.ceil(height * MAX_CANVAS_DATA_GRID_PIXEL_RATIO));
const pixelWidth = measurementMatches ? Math.min(measured.pixelWidth, maxPixelWidth) : fallbackPixelWidth;
const pixelHeight = measurementMatches ? Math.min(measured.pixelHeight, maxPixelHeight) : fallbackPixelHeight;
return {
pixelWidth,
pixelHeight,