销毁 chart.js 条形图以重绘同一 <canvas> 中的其他图表

Posted

技术标签:

【中文标题】销毁 chart.js 条形图以重绘同一 <canvas> 中的其他图表【英文标题】:Destroy chart.js bar graph to redraw other graph in same <canvas> 【发布时间】:2017-02-24 16:13:58 【问题描述】:

我正在使用 Chart.js 库来绘制 条形图,它工作正常,但现在我想销毁 条形图 strong> 并在同一个 canvas 中制作 折线图。我试过这两种方法来清除画布:

var grapharea = document.getElementById("barChart").getContext("2d");

grapharea.destroy();

var myNewChart = new Chart(grapharea,  type: 'radar', data: barData, options: barOptions );

第二种方式:

var grapharea = document.getElementById("barChart").getContext("2d");

grapharea.clear();

var myNewChart = new Chart(grapharea,  type: 'radar', data: barData, options: barOptions );

我说的对吗? OnButtonClick 我调用这个函数,它使用相同的画布。

【问题讨论】:

重复问题:***.com/questions/24815851/… 【参考方案1】:

从...更改新图表变量

var yourChart= new Chart(ctx1).Line(barChartData1, animation: false);

window.yourChart= new Chart(ctx1).Line(barChartData1, animation: false);

然后

if(window.yourChart!= null)

 window.yourChart.destroy();


var doc1 = document.getElementById("stockPrice");
var ctx1 = doc1.getContext("2d");
window.yourChart = new Chart(ctx1).Bar(barChartData1, animation: false);

【讨论】:

【参考方案2】:

对于给定的画布 ID,这是我在创建新图表之前销毁 ChartJS 图表的策略。这有点蛮力,但可以完成工作。

我创建了一个对象 chartsByCanvasId,它跟踪从画布 id 到关联 ChartJS 对象的映射,我将在创建新对象之前检查该对象是否存在要销毁的任何现有 ChartJS 对象。

看这里:


// Helper object and functions

const chartsByCanvasId = ;

const destroyChartIfNecessary = (canvasId) => 
    if (chartsByCanvasId[canvasId]) 
        chartsByCanvasId[canvasId].destroy();
    


const registerNewChart = (canvasId, chart) => 
    chartsByCanvasId[canvasId] = chart;


那么,如果图表存在,这就是你如何销毁图表

destroyChartIfNecessary(canvasId);
const myChart = new Chart(ctx, config);
registerNewChart(canvasId, myChart);

请注意,创建图表后,我们会立即将其“注册”到registerNewChart()。这个注册步骤很重要,因为这就是destroyChartIfNecessary() 知道给定画布 id 已经存在 ChartJS 对象的方式。

这个策略的好处在于,即使您的页面上有很多图表,它也能工作,因为它通过画布 ID 跟踪 ChartJS 对象。

【讨论】:

【参考方案3】:

ChartJs 的getChart(key) - 如果图表已经创建,则从给定键中查找图表实例。

如果键是字符串,则将其解释为图表的 Canvas 元素的 ID。 键也可以是 CanvasRenderingContext2D 或 htmlDOMElement。

注意:如果没有找到图表,这将返回 undefined。如果找到图表的实例,则表明该图表必须是先前创建的。

// JS - Destroy exiting Chart Instance to reuse <canvas> element
let chartStatus = Chart.getChart("myChart"); // <canvas> id
if (chartStatus != undefined) 
  chartStatus.destroy();

//-- End of chart destroy   

var chartCanvas = $('#myChart'); //<canvas> id
chartInstance = new Chart(chartCanvas, 
  type: 'line',
  data: data
);
<!-- HTML -Line Graph - Chart.js -->
<div class="container-fluid" id="chartContainer">
  <canvas id="myChart"  >   </canvas>
</div>

这种方法将使您免于从 JS 内部删除 - 创建 - 将 Canvas 元素附加到 DIV 中。

【讨论】:

这会对未定义的元素进行 fab 检查。真正符合 OOP!【参考方案4】:

我不知道我花了多少时间来处理这个问题。

假设您的 html 包含该内容

<div id="soner" class="card-body customerDonutChart">
<canvas id="customerDonutChart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas> 
</div>

注意解决问题所必需的&lt;div id="soner" 部分。

function myChartJsCaller()

   document.getElementById("soner").innerHTML = '<canvas id="customerDonutChart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas>';

  // here is important, your ctx = blabla must be below after changing innerHTML
  let ctx = document.getElementById(selector);
  .
  .

【讨论】:

【参考方案5】:

如果您在一页上有很多图表,那么构建数据结构来保存现有图表的列表会很复杂。在 chart.js 3.5.0 中更容易测试图表画布是否已被使用。不需要单独的数据结构:

// Chart may previously have been shown or not, so the chart may need creating or updating. 
// Vue messes with the DOM, so you can't just "update" the chart as per the docs.
var canv = this.$refs['canvas'];
const oldChart = Chart.getChart(canv);
if (typeof oldChart !== 'undefined') 
    oldChart.destroy();

new Chart(canv.getContext('2d'), this.config);

【讨论】:

【参考方案6】:

这适用于我的角度

removeHTML() 

    let chart = <HTMLCanvasElement>document.getElementById("myChart"); 
    let chart2 = <HTMLCanvasElement>document.getElementById("myChart2"); 

    if(chart)
       chart.remove()
    

    if(chart2)
      chart2.remove()
    





ngOnDestroy(): void 

    this.removeHTML();

【讨论】:

尝试根据上下文回答。【参考方案7】:

对我来说,它似乎与核心 javascript 一起工作如下(假设图表 min js 已经加载):

const data = 
    labels : graphDataLabels,
        datasets: [
            label: 'Sentiment Intensity Distribution',
            data: dataValues, //[300, 50, 100],
            backgroundColor: [
                "#0D5265",
                "#32DAC8",
                "#FF8300"
            ],
            hoverOffset: 4
        ]
    ;
    const config = 
        type: 'pie',
        data: data
    ;
    var ctx = document.getElementById('pieChart').getContext('2d');
    if(ctx.pieChart)
        pieChart = null;
    else
        pieChart = new Chart(ctx, config);
    

【讨论】:

【参考方案8】:

我设法找到了一个与 destroy 方法一起使用的解决方案,并允许重复使用画布而不删除和重新创建它,同时它消耗的资源更少。

首先,将var chart声明为全局并创建一个布尔值来检查js是否加载

var chart;
var graphScriptLoaded = false;

下一部分很好,因为它只在需要图表时加载js,节省了加载页面的时间,同时它可以让你了解它是否是第一次执行与否

//load graph just when needed and destry existing istances
if (!Boolean(graphScriptLoaded)) 
  loadScript('https://cdn.jsdelivr.net/npm/chart.js@2.8.0', function()
    graphScriptLoaded = true;
    chart=graphs_init(i_arr, spent_arr);
  );
 else 
  chart.destroy();
  chart=graphs_init(i_arr, spent_arr);

然后,在创建图表的函数中,只需返回图表 var

var chart = new Chart(ctx, 
[.....]
);
return chart;

函数“loadscript”是自定义的,基于这个答案: How do I include a JavaScript file in another JavaScript file?

这里是:

    function loadScript(url, callback)

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState)  //IE
        script.onreadystatechange = function()
          if (script.readyState == "loaded" || script.readyState == "complete")
                script.onreadystatechange = null;
                callback();
          
        ;
     else   //Others
        script.onload = function()
          callback();
        ;
    

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);

它就像一个魅力。

【讨论】:

【参考方案9】:

我遇到了同样的问题,我已经删除了 canvas 元素并重新创建了 canvas 元素,然后再次延迟渲染。

var element = document.getElementById("canvasId");

element.parentNode.removeChild(element);

var canv =  document.createElement("canvas");

canv.setAttribute("id","canvasId");

canv.style.height = "20vw"; // give height and width as per the requirement

canv.style.width = "20vw"; 

setTimeout(()=>
 var grapharea = document.getElementById("canvasId").getContext("2d");
,500)

【讨论】:

【参考方案10】:

这将解决图表在多个 ajax 调用中多次更新时变慢的问题:

只需在启动图表之前添加此代码:

    $('.chartjs-size-monitor').each(function()
      $(this).remove();
    )
    var grapharea = document.getElementById("barChart").getContext("2d");

【讨论】:

【参考方案11】:

创建一个全局对象:

window['chart-' + chartId] = new Chart(...);

通过重绘访问和销毁进程:

if ( window['chart-' + chartId] != undefined ) 
    window['chart-' + chartId].destroy();

【讨论】:

【参考方案12】:

2020 年的简单编辑:

这对我有用。通过使其拥有窗口将图表更改为全局(将声明从 var myChart 更改为 window myChart

检查图表变量是否已经初始化为Chart,如果是,则销毁它并创建一个新的,即使你可以创建另一个同名的变量。下面是代码:

if(window.myChart instanceof Chart)

    window.myChart.destroy();

var ctx = document.getElementById('myChart').getContext("2d");

希望它有效!

【讨论】:

这必须是唯一正确的选项。你不应该在没有先检查有什么东西要破坏的情况下打电话给destroy()。此外,明确检查 instanceof Chart 是正确的。我之前一直在检查“未定义”,它在某些情况下可能会导致问题。【参考方案13】:

对于 ChartJS v2.x,您可以使用 update() 更新图表数据,而无需显式销毁和创建画布。

var chart_ctx = document.getElementById("chart").getContext("2d");

var chart = new Chart(chart_ctx, 
    type: "pie",
    data: ,
    options: 
);

$.ajax(
    ...
).done(function (response) 
    chart.data = response;
    chart.update();
);

【讨论】:

如果变量已被覆盖或不再使用,是否有办法检索图表实例? @RahulHindocha 如果您无权访问您的变量,您可以通过查看Chart.instances 对象重新访问实例。从这里你可以做Chart.instances[key].destroy()Chart.instances[key].update()【参考方案14】:

我总是只使用 1 个图表/页面。 Destroy() 解决了问题。

 if (
        window.myLine !== undefined
        &&
        window.myLine !== null
    ) 
        window.myLine.destroy();
    

    window.myLine = new Chart(graphCanvasCtx, config);

【讨论】:

【参考方案15】:

你可以测试一下

 $('#canvas').replaceWith($('<canvas id="canvas" ></canvas>'));

;)

【讨论】:

【参考方案16】:

也许有更好的方法,但没有适合我的答案。

document.querySelector("#chartReport").innerHTML = '<canvas id="myChart"></canvas>';

我的 HTML 部分是

<div class="col-md-6 col-md-offset-3">
     <div id="chartReport">
         <canvas id="myChart"></canvas>
     </div>
</div>

【讨论】:

你的解决方案对我有用,而不是其他人建议使用 jquery 删除和附加 非常感谢!我尝试了很多答案,但只有这个答案可以完美地工作,没有故障数据 你是个传奇人物。 最快、最好、最简单的解决方法 简单的解决方法,谢谢??【参考方案17】:

为了解决这个问题,我使用了 jQuery 的 add()remove() 方法来清除画布。我正在删除组件,在再次绘制它之前,我使用 jQuery 的 append() 方法再次使用相同的 id 附加画布。

redraw()

 $("#myChart").remove();// removing previous canvas element
 //change the data values or add new values for new graph
 $("#chart_box").after("<canvas id='myChart'></canvas>");
 // again adding a new canvas element with same id
 generateGraph();// calling the main graph generating function 


【讨论】:

【参考方案18】:

我现在使用的是 Chart.js 2.7.2。在我的应用程序中,我正在创建多个图表,并且需要一种方法来访问它们以正确“替换”它们的数据并修复悬停时显示的“旧图表”。我试过的答案都没有正确。

这是一种使用一个或多个图表来管理此问题的方法:

在全局中存储图表

var charts=[]; // global

创建图表功能

function createChart(id, type, labels, data)

    // for multiple datasets
    var datasets=[];
    data.forEach(function(set) 
        datasets.push(
            label: set.label,
            data: set.data
        );
    );  

    var config = 
        type: type,
        data: 
            labels: labels,
            datasets: datasets
        
    ;

    if(typeof charts[id] == "undefined") // see if passed id exists
       
        // doesn't, so create it
        charts[id]= new (function()
            this.ctx=$(id); // canvas el
            this.chart=new Chart(this.ctx, config);
        )();     
        console.log('created chart '+charts[id].chart.canvas.id);     
    
    else
    
        charts[id].chart.destroy(); // "destroy" the "old chart"
        charts[id].chart=new Chart(charts[id].ctx, config); // create the chart with same id and el
        console.log('replaced chart '+charts[id].chart.canvas.id);        
    
    // just to see all instances
    Chart.helpers.each(Chart.instances, function(instance)
        console.log('found instance '+instance.chart.canvas.id)
    )


对于您的每个画布元素,例如:

<canvas id="thiscanvasid"></canvas>

使用函数创建/替换图表

createChart('#thiscanvasid', 'bar', json.labels, json.datasets);

【讨论】:

【参考方案19】:

每次图表调用后删除画布,这对我有用

$("canvas#chartreport").remove();
$("div.chartreport").append('<canvas id="chartreport" class="animated fadeIn" ></canvas>');
var ctx = document.getElementById("chartreport").getContext("2d");
chartreport= new Chart(ctx,  .... );

【讨论】:

这是个好主意,在 vanilla js 中 document.getElementById( "sector-chart" ).remove(); let canvas = document.createElement('canvas'); canvas.setAttribute('id','chart'); canvas.setAttribute('width','300'); canvas.setAttribute('height','100'); document.querySelector('#chart-container').appendChild(canvas); 这非常有效,特别是如果你有你的 JS 文件来在不同的文件上绘制图形。【参考方案20】:

为了能够在同一画布上绘制另一个图表,正确的使用方法是.destroy()。您必须在先前创建的图表对象上调用它。您也可以对两个图表使用相同的变量。

var grapharea = document.getElementById("barChart").getContext("2d");

var myChart = new Chart(grapharea,  type: 'bar', data: barData, options: barOptions );

myChart.destroy();

myChart = new Chart(grapharea,  type: 'radar', data: barData, options: barOptions );

直接来自docs (under Prototype Methods):

.destroy()

使用它来销毁任何已创建的图表实例。这将清除存储在 Chart.js 中的图表对象的所有引用,以及 Chart.js 附加的任何关联事件侦听器。这必须在画布重新用于新图表之前调用。

// Example from the docs
var myLineChart = new Chart(ctx, config);
// Destroys a specific chart instance
myLineChart.destroy();

它明确指出必须先调用此方法,然后才能将画布重新用于新图表。

.clear() 稍后在同一部分中也提到了“将清除图表画布。在动画帧之间广泛用于内部,但您可能会发现它很有用。”调用此方法后,图表将保持活动状态,因此如果您想将画布重用于全新的图表,则无需调用此方法。

说实话,在像你这样的情况下,我经常使用容器 div 来包装我的 canvas,并且每当我需要创建新图表时,我都会在其中放置一个新的 canvas 元素div。然后我将这个新创建的canvas 用于新图表。如果您遇到奇怪的行为,可能与图表在当前图表之前占据画布有关,请记住这种方法。

【讨论】:

谢谢,但是如何知道一个图表是存在还是被破坏了呢? typeof myChart === undefined 有效吗? 回复 João Pimentel Ferreira - 将图表声明为变量,然后编写一个条件来检查它是否存在。 if (myChart) myChart.destroy(); 这种方法对我不起作用。 destroy 方法什么也没做。 伙计,非常感谢。你节省了我的时间。我使用了使用 div 的解决方法,因为调用 destroy 没有帮助。

以上是关于销毁 chart.js 条形图以重绘同一 <canvas> 中的其他图表的主要内容,如果未能解决你的问题,请参考以下文章

Chart.js:条形图点击事件

分组条形图,在 chart.js 中

删除 chart.js 条形图中的删除线行为

如何在 Chart.js 条形图中添加左/右填充?

如何在chart.js中为条形图绘制基线

如何使 Chart.js 条形图保持原位