Skip to content

Commit

Permalink
Make part of the chart rendering async for large datasets (#24260)
Browse files Browse the repository at this point in the history
  • Loading branch information
MindFreeze authored Feb 18, 2025
1 parent cdd17ee commit c52217c
Showing 1 changed file with 34 additions and 7 deletions.
41 changes: 34 additions & 7 deletions src/components/chart/ha-chart-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { DataZoomComponentOption } from "echarts/components";
import type { EChartsType } from "echarts/core";
import type {
ECElementEvent,
SetOptionOpts,
XAXisOption,
YAXisOption,
} from "echarts/types/dist/shared";
Expand All @@ -25,6 +24,7 @@ import type { HomeAssistant } from "../../types";
import { isMac } from "../../util/is_mac";
import "../ha-icon-button";
import { formatTimeLabel } from "./axis-label";
import { ensureArray } from "../../common/array/ensure-array";

export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;

Expand Down Expand Up @@ -68,12 +68,16 @@ export class HaChartBase extends LitElement {

private _listeners: (() => void)[] = [];

private _originalZrFlush?: () => void;

public disconnectedCallback() {
super.disconnectedCallback();
while (this._listeners.length) {
this._listeners.pop()!();
}
this.chart?.dispose();
this.chart = undefined;
this._originalZrFlush = undefined;
}

public connectedCallback() {
Expand All @@ -86,7 +90,7 @@ export class HaChartBase extends LitElement {
listenMediaQuery("(prefers-reduced-motion)", (matches) => {
if (this._reducedMotion !== matches) {
this._reducedMotion = matches;
this.chart?.setOption({ animation: !this._reducedMotion });
this._setChartOptions({ animation: !this._reducedMotion });
}
})
);
Expand All @@ -96,7 +100,7 @@ export class HaChartBase extends LitElement {
if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) {
this._modifierPressed = true;
if (!this.options?.dataZoom) {
this.chart?.setOption({ dataZoom: this._getDataZoomConfig() });
this._setChartOptions({ dataZoom: this._getDataZoomConfig() });
}
}
};
Expand All @@ -105,7 +109,7 @@ export class HaChartBase extends LitElement {
if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) {
this._modifierPressed = false;
if (!this.options?.dataZoom) {
this.chart?.setOption({ dataZoom: this._getDataZoomConfig() });
this._setChartOptions({ dataZoom: this._getDataZoomConfig() });
}
}
};
Expand All @@ -131,18 +135,16 @@ export class HaChartBase extends LitElement {
return;
}
let chartOptions: ECOption = {};
const chartUpdateParams: SetOptionOpts = { lazyUpdate: true };
if (changedProps.has("data")) {
chartOptions.series = this.data;
chartUpdateParams.replaceMerge = ["series"];
}
if (changedProps.has("options")) {
chartOptions = { ...chartOptions, ...this._createOptions() };
} else if (this._isTouchDevice && changedProps.has("_isZoomed")) {
chartOptions.dataZoom = this._getDataZoomConfig();
}
if (Object.keys(chartOptions).length > 0) {
this.chart.setOption(chartOptions, chartUpdateParams);
this._setChartOptions(chartOptions);
}
}

Expand Down Expand Up @@ -509,6 +511,31 @@ export class HaChartBase extends LitElement {
return Math.max(this.clientWidth / 2, 200);
}

private _setChartOptions(options: ECOption) {
if (!this.chart) {
return;
}
if (!this._originalZrFlush) {
const dataSize = ensureArray(this.data).reduce(
(acc, series) => acc + (series.data as any[]).length,
0
);
if (dataSize > 10000) {
// for large datasets zr.flush takes 30-40% of the render time
// so we delay it a bit to avoid blocking the main thread
const zr = this.chart.getZr();
this._originalZrFlush = zr.flush.bind(zr);
zr.flush = () => {
setTimeout(() => {
this._originalZrFlush?.();
}, 10);
};
}
}
const replaceMerge = options.series ? ["series"] : [];
this.chart.setOption(options, { replaceMerge });
}

private _handleZoomReset() {
this.chart?.dispatchAction({ type: "dataZoom", start: 0, end: 100 });
}
Expand Down

0 comments on commit c52217c

Please sign in to comment.