【问题标题】:Destroy chart.js bar graph to redraw other graph in same <canvas>销毁 chart.js 条形图以重绘同一 <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 我调用这个函数,它使用相同的画布。

【问题讨论】:

标签: javascript canvas graph chart.js


【解决方案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" width="400" height="150">   </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" height="320px"></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" height="150"></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 没有帮助。
                            猜你喜欢
                            • 1970-01-01
                            • 2020-01-13
                            • 1970-01-01
                            • 1970-01-01
                            • 2017-09-02
                            • 2020-04-02
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多