Chart.js图表更新函数失效,点击更新提示Canvas已被占用
Chart.js 更新时出现“canvas is already in use”错误的解决方案
你的问题核心在于全局变量作用域混乱和数据引用复用导致的图表实例管理错误:
render函数中,新创建的图表实例被赋值给局部chartUI参数,而非全局变量,导致全局变量始终指向已销毁的旧实例,下次操作引发canvas冲突。- 全局的
labelArr和dataArr被多函数复用,未在每次更新时创建新数组,造成数据引用混乱,图表更新逻辑失效。 destroy函数参数与全局变量同名,易引发逻辑混淆,且销毁后未置空全局chartUI,残留无效引用。
修复后的完整代码
// 移除全局数据数组,改为按需创建局部数组 let data = { labels: [], datasets: [ { data: [], label: "# of Amount", borderWidth: 1, borderColor: "#01a001", backgroundColor: "#01a001", }, ], }; let config = { type: "line", data, options: { scales: { y: { beginAtZero: false, }, }, }, }; let chartUI = null; // 全局图表实例初始化为null function initChart(currentAccount) { const labelArr = []; const dataArr = []; currentAccount.transactions.forEach((t) => { const dateReady = currentUser.dateCreator(t.date); labelArr.push(dateReady); dataArr.push(t.amount); }); data.labels = labelArr; data.datasets[0].data = dataArr; // 首次初始化图表,避免重复创建 if (!chartUI) { chartUI = new Chart(document.getElementById("chart"), config); } else { chartUI.update(); } } function destroy() { if (chartUI) { chartUI.destroy(); chartUI = null; // 销毁后置空,确保canvas释放 } } function render(currentAccount) { destroy(); // 先彻底销毁旧实例 const labelArr = []; const dataArr = []; currentAccount.transactions.forEach((t) => { const dateReady = currentUser.dateCreator(t.date); labelArr.push(dateReady); dataArr.push(t.amount); }); data.labels = labelArr; data.datasets[0].data = dataArr; // 创建新实例并赋值给全局变量 chartUI = new Chart(document.getElementById("chart"), config); }
对应的导出函数修改
export const initSettings = function (currentAccount) { initChart(currentAccount); }; export const updateUI = function (currentAccount) { render(currentAccount); // render内部已包含destroy逻辑,无需单独调用 };
关键修复说明
- 移除全局数据数组:每次操作创建局部数组,避免不同更新之间的数据污染,确保
data对象始终引用最新数据集。 - 全局实例严格管理:
chartUI初始化为null,销毁后置空,确保canvas在创建新实例前处于未占用状态。 - 简化更新流程:
render内部先执行销毁逻辑,再创建新实例并绑定到全局变量,彻底解决局部变量覆盖问题。 - 消除参数混淆:
destroy和render直接操作全局实例,不再依赖传入参数,减少作用域混乱风险。
内容的提问来源于stack exchange,提问作者tomaszM-dev
相关产品推荐
相关产品推荐

