Angular下拉选择更新Chart.js X轴值报Canvas复用错误解决方案
问题根因
报错来自Chart.js的实例绑定限制:你每次调用filtereTokenPrice都会在同一个<canvas>元素上初始化全新的Chart实例,首次执行时canvas无绑定实例所以可以正常渲染,后续切换选项时前一次创建的实例仍占用canvas资源,没有被销毁,就会抛出「Canvas is already in use」的错误。
解决方法
两种实现都可以解决问题,优先选第一种复用实例的方案,性能更好。
方案1:复用Chart实例(推荐)
不要重复初始化实例,首次创建实例后,后续切换选项只更新数据再触发重绘即可。
- 先在组件类的顶层声明chart实例属性,不要在方法内部直接声明变量:
// 组件类属性区新增 chart: Chart | null = null; - 修改
filtereTokenPrice方法逻辑,区分首次初始化和后续更新场景:async filtereTokenPrice(){ // 原map是同步方法,不需要加await this.usd = this.TokenUsdPrice.map(item=>item.usdPrice); const chartLabels = this.service.getdates(); const chartDatasets = [{ data: this.usd, borderWidth: 1, fill: false }]; if (!this.chart) { // 实例不存在,首次初始化 this.chart = new Chart('canvas', { type: 'line', data: { labels: chartLabels, datasets: chartDatasets } }); } else { // 实例已存在,替换数据后重绘 this.chart.data.labels = chartLabels; this.chart.data.datasets = chartDatasets; this.chart.update(); } } - 新增组件销毁生命周期逻辑,避免内存泄漏:
// 类实现OnDestroy接口 ngOnDestroy(): void { if (this.chart) { this.chart.destroy(); } }
方案2:每次新建前销毁旧实例
如果不需要复用实例,每次创建新实例前手动销毁已存在的旧实例即可:
- 同样先在组件类顶层声明chart属性:
chart: Chart | null = null; - 修改
filtereTokenPrice逻辑:async filtereTokenPrice(){ this.usd = this.TokenUsdPrice.map(item=>item.usdPrice); // 存在旧实例先销毁 if (this.chart) { this.chart.destroy(); } // 再创建新实例 this.chart = new Chart('canvas',{ type:'line', data:{ labels:this.service.getdates(), datasets:[ { data:this.usd, borderWidth:1, fill:false } ] } }) }
额外优化点
- 不推荐在
mat-option上绑定(click)事件触发数据查询,这种方式可能出现点击事件触发时表单值还没更新的问题,建议直接监听表单控件的valueChanges事件触发逻辑:
模板中直接移除// 在ngOnInit生命周期中添加订阅 ngOnInit(): void { this.selectedValue.valueChanges.subscribe(() => { this.TokenPrice(); }); }mat-option上的(click)="TokenPrice()"绑定即可。 - 你原需求描述是「动态价格展示在X轴,固定日期展示在Y轴」,当前配置中
labels(默认对应X轴)放的是日期数据,data放的是价格,和需求正好相反,如果轴显示不符合预期,可以在Chart的options配置中修改轴映射规则。
内容的提问来源于stack exchange,提问作者Hemanth Girimath
相关产品推荐
相关产品推荐

