简体   繁体   English

如何从画布中清除图表以便无法触发悬停事件?

[英]How to clear a chart from a canvas so that hover events cannot be triggered?

I'm using Chartjs to display a Line Chart and this works fine:我正在使用 Chartjs 来显示折线图,这很好用:

// get line chart canvas
var targetCanvas = document.getElementById('chartCanvas').getContext('2d');

// draw line chart
var chart = new Chart(targetCanvas).Line(chartData);

But the problem occurs when I try to change the data for the Chart.但是当我尝试更改图表的数据时会出现问题。 I update the graph by creating a new instance of a Chart with the new data points, and thus reinitializing the canvas.我通过使用新数据点创建图表的新实例来更新图形,从而重新初始化画布。

This works fine.这工作正常。 However, when I hover over the new chart, if I happen to go over specific locations corresponding to points displayed on the old chart, the hover/label is still triggered and suddenly the old chart is visible.但是,当我将鼠标悬停在新图表上时,如果我碰巧经过与旧图表上显示的点相对应的特定位置,仍然会触发悬停/标签,并且突然可以看到旧图表。 It remains visible while my mouse is at this location and disappears when move off that point.当我的鼠标在这个位置时它仍然可见,当离开那个点时它会消失。 I don't want the old chart to display.我不想显示旧图表。 I want to remove it completely.我想彻底删除它。

I've tried to clear both the canvas and the existing chart before loading the new one.在加载新图表之前,我尝试清除画布和现有图表。 Like:喜欢:

targetCanvas.clearRect(0,0, targetCanvas.canvas.width, targetCanvas.canvas.height);

and

chart.clear();

But none of these have worked so far.但到目前为止,这些都没有奏效。 Any ideas about how I can stop this from happening?关于如何阻止这种情况发生的任何想法?

I had huge problems with this我在这方面遇到了很大的问题

First I tried .clear() then I tried .destroy() and I tried setting my chart reference to null首先我尝试了.clear()然后我尝试了.destroy()并尝试将我的图表引用设置为 null

What finally fixed the issue for me: deleting the <canvas> element and then reappending a new <canvas> to the parent container最终为我解决的问题是:删除<canvas>元素,然后将新的<canvas>重新附加到父容器


My specific code (obviously there's a million ways to do this):我的具体代码(显然有一百万种方法可以做到这一点):

var resetCanvas = function(){
  $('#results-graph').remove(); // this is my <canvas> element
  $('#graph-container').append('<canvas id="results-graph"><canvas>');
  canvas = document.querySelector('#results-graph');
  ctx = canvas.getContext('2d');
  ctx.canvas.width = $('#graph').width(); // resize to parent width
  ctx.canvas.height = $('#graph').height(); // resize to parent height
  var x = canvas.width/2;
  var y = canvas.height/2;
  ctx.font = '10pt Verdana';
  ctx.textAlign = 'center';
  ctx.fillText('This text is centered on the canvas', x, y);
};

I have faced the same problem few hours ago.几个小时前我遇到了同样的问题。

The ".clear()" method actually clears the canvas, but (evidently) it leaves the object alive and reactive. “.clear()”方法实际上清除了画布,但(显然)它使对象保持活跃和反应。

Reading carefully the official documentation , in the "Advanced usage" section, I have noticed the method ".destroy()", described as follows:仔细阅读官方文档,在“高级用法”部分,我注意到了“.destroy()”方法,描述如下:

"Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js." “使用它来销毁创建的任何图表实例。这将清除存储到 Chart.js 中图表对象的任何引用,以及 Chart.js 附加的任何关联事件侦听器。”

It actually does what it claims and it has worked fine for me, I suggest you to give it a try.它实际上做到了它声称的那样并且对我来说效果很好,我建议您尝试一下。

var myPieChart=null;

function drawChart(objChart,data){
    if(myPieChart!=null){
        myPieChart.destroy();
    }
    // Get the context of the canvas element we want to select
    var ctx = objChart.getContext("2d");
    myPieChart = new Chart(ctx).Pie(data, {animateScale: true});
}

This is the only thing that worked for me:这是唯一对我有用的东西:

document.getElementById("chartContainer").innerHTML = '&nbsp;';
document.getElementById("chartContainer").innerHTML = '<canvas id="myCanvas"></canvas>';
var ctx = document.getElementById("myCanvas").getContext("2d");

I had the same problem here... I tried to use destroy() and clear() method, but without success.我在这里遇到了同样的问题......我尝试使用 destroy() 和 clear() 方法,但没有成功。

I resolved it the next way:我用下一个方法解决了它:

HTML: HTML:

<div id="pieChartContent">
    <canvas id="pieChart" width="300" height="300"></canvas>
</div>

Javascript: Javascript:

var pieChartContent = document.getElementById('pieChartContent');
pieChartContent.innerHTML = '&nbsp;';
$('#pieChartContent').append('<canvas id="pieChart" width="300" height="300"><canvas>');

ctx = $("#pieChart").get(0).getContext("2d");        
var myPieChart = new Chart(ctx).Pie(data, options);

It works perfect to me... I hope that It helps.它对我来说很完美......我希望它有所帮助。

We can update the chart data in Chart.js V2.0 as follows:我们可以在 Chart.js V2.0 中更新图表数据如下:

var myChart = new Chart(ctx, data);
myChart.config.data = new_data;
myChart.update();

This worked very well for me这对我来说非常有效

    var ctx = $("#mycanvas");
     var LineGraph = new Chart(ctx, {
        type: 'line',
        data: chartdata});
        LineGraph.destroy();

Use .destroy this to destroy any chart instances that are created.使用.destroy this 来销毁创建的任何图表实例。 This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js.这将清除 Chart.js 中存储到图表对象的所有引用,以及 Chart.js 附加的任何关联事件侦听器。 This must be called before the canvas is reused for a new chart.这必须在画布重新用于新图表之前调用。

Simple edit for 2020: 2020 年的简单编辑:

This worked for me.这对我有用。 Change the chart to global by making it window owned (Change the declaration from var myChart to window myChart )通过使其拥有窗口来将图表更改为全局(将声明从var myChart更改为window myChart

Check whether the chart variable is already initialized as Chart, if so, destroy it and create a new one, even you can create another one on the same name.检查chart变量是否已经初始化为Chart,如果是,销毁它并创建一个新的,即使你可以创建另一个同名的。 Below is the code:下面是代码:

if(window.myChart instanceof Chart)
{
    window.myChart.destroy();
}
var ctx = document.getElementById('myChart').getContext("2d");

Hope it works!希望它有效!

Complementing Adam's Answer补充亚当的答案

With Vanilla JS:使用香草 JS:

 document.getElementById("results-graph").remove(); //canvas div = document.querySelector("#graph-container"); //canvas parent element div.insertAdjacentHTML("afterbegin", "<canvas id='results-graph'></canvas>"); //adding the canvas again

It's best to use Chart.js specific functionalities to initially check for the existing chart instance and then perform destroy or clear in order to reuse the same canvas element for rendering another chart, instead of handlding HTML elements from within JS.最好使用 Chart.js 特定功能来初步检查现有图表实例,然后执行销毁或清除,以便重用相同的画布元素来呈现另一个图表,而不是从 JS 内部处理 HTML 元素。

ChartJs's getChart(key) - finds the chart instance from the given key. ChartJs 的getChart(key) - 从给定的键中找到图表实例。

  • If the key is a string, it is interpreted as the ID of the Canvas element for the Chart.如果键是字符串,则将其解释为图表的 Canvas 元素的 ID。
  • The key can also be a CanvasRenderingContext2D or an HTMLDOMElement.键也可以是 CanvasRenderingContext2D 或 HTMLDOMElement。

Note: This will return undefined if no Chart is found.注意:如果没有找到图表,这将返回undefined If the instance of the chart is found, it signifies that the chart must have previously been created.如果找到图表的实例,则表示该图表必须先前已创建。

 // JS - Destroy exiting Chart Instance to reuse <canvas> element let chartStatus = Chart.getChart("myChart"); // <canvas> id if (chartStatus != undefined) { chartStatus.destroy(); //(or) // chartStatus.clear(); } //-- 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>

This approach would save you from remove - create - append a Canvas element into DIV from inside JS.这种方法将使您免于从 JS 内部删除 - 创建 - 将 Canvas 元素附加到 DIV 中。

Using CanvasJS, this works for me clearing chart and everything else, might work for you as well, granting you set your canvas/chart up fully before each processing elsewhere:使用 CanvasJS,这适用于我清除图表和其他所有内容,也可能适用于您,允许您在其他地方的每次处理之前完全设置画布/图表:

var myDiv= document.getElementById("my_chart_container{0}";
myDiv.innerHTML = "";

I couldn't get .destroy() to work either so this is what I'm doing.我也无法让 .destroy() 工作,所以这就是我正在做的。 The chart_parent div is where I want the canvas to show up. chart_parent div 是我希望画布出现的地方。 I need the canvas to resize each time, so this answer is an extension of the above one.我每次都需要调整画布大小,所以这个答案是上述答案的扩展。

HTML: HTML:

<div class="main_section" > <div id="chart_parent"></div> <div id="legend"></div> </div>

JQuery:查询:

  $('#chart').remove(); // this is my <canvas> element
  $('#chart_parent').append('<label for = "chart">Total<br /><canvas class="chart" id="chart" width='+$('#chart_parent').width()+'><canvas></label>');

When you create one new chart.js canvas, this generate one new iframe hidden, you need delete the canvas and the olds iframes.当您创建一个新的 chart.js 画布时,这会生成一个新的隐藏的 iframe,您需要删除画布和旧的 iframe。

$('#canvasChart').remove(); 
$('iframe.chartjs-hidden-iframe').remove(); 
$('#graph-container').append('<canvas id="canvasChart"><canvas>'); 
var ctx = document.getElementById("canvasChart"); 
var myChart = new Chart(ctx, { blablabla });

reference: https://github.com/zebus3d/javascript/blob/master/chartJS_filtering_with_checkboxs.html参考: https : //github.com/zebus3d/javascript/blob/master/chartJS_filtering_with_checkboxs.html

This worked for me.这对我有用。 Add a call to clearChart, at the top oF your updateChart()在 updateChart() 的顶部添加对 clearChart 的调用

`function clearChart() {
    event.preventDefault();
    var parent = document.getElementById('parent-canvas');
    var child = document.getElementById('myChart');          
    parent.removeChild(child);            
    parent.innerHTML ='<canvas id="myChart" width="350" height="99" ></canvas>';             
    return;
}`

If you are using chart.js in an Angular project with Typescript, the you can try the following;如果您在带有 Typescript 的 Angular 项目中使用 chart.js,您可以尝试以下操作;

Import the library:
    import { Chart } from 'chart.js';

In your Component Class declare the variable and define a method:

  chart: Chart;

  drawGraph(): void {
    if (this.chart) {
      this.chart.destroy();
    }

    this.chart = new Chart('myChart', {
       .........
    });
  }


In HTML Template:
<canvas id="myChart"></canvas>

What we did is, before initialization of new chart, remove/destroy the previews Chart instance, if exist already, then create a new chart, for example我们所做的是,在初始化新图表之前,移除/销毁预览图表实例,如果已经存在,则创建一个新图表,例如

if(myGraf != undefined)
    myGraf.destroy();
    myGraf= new Chart(document.getElementById("CanvasID"),
    { 
      ...
    }

Hope this helps.希望这可以帮助。

First put chart in some variable then history it next time before init首先将图表放在某个变量中,然后在下次初始化之前将其历史化

#Check if myChart object exist then distort it #检查myChart对象是否存在然后扭曲它

    if($scope.myChart) {
      $scope.myChart.destroy();
    }

    $scope.myChart  = new Chart(targetCanvas

You should save the chart as a variable.您应该将图表保存为变量。 On global scope, if its pure javascript, or as a class property, if its Angular.在全局范围内,如果它是纯 javascript,或者作为类属性,如果它是 Angular。

Then you'll be able to use this reference to call destroy().然后你就可以使用这个引用来调用destroy()。

Pure Javascript:纯Javascript:

var chart;

function startChart() {
    // Code for chart initialization
    chart = new Chart(...); // Replace ... with your chart parameters
}

function destroyChart() {
    chart.destroy();
}

Angular:角度:

export class MyComponent {
    chart;

    constructor() {
        // Your constructor code goes here
    }

    ngOnInit() {
        // Probably you'll start your chart here

        // Code for chart initialization
        this.chart = new Chart(...); // Replace ... with your chart parameters
    }

    destroyChart() {
        this.chart.destroy();
    }
}

For me this worked:对我来说这有效:

 var in_canvas = document.getElementById('chart_holder'); //remove canvas if present while (in_canvas.hasChildNodes()) { in_canvas.removeChild(in_canvas.lastChild); } //insert canvas var newDiv = document.createElement('canvas'); in_canvas.appendChild(newDiv); newDiv.id = "myChart";

Chart.js has a bug: Chart.controller(instance) registers any new chart in a global property Chart.instances[] and deletes it from this property on .destroy() . Chart.js 有一个错误: Chart.controller(instance)在全局属性Chart.instances[]注册任何新图表,并在.destroy()上从该属性中删除它。

But at chart creation Chart.js also writes ._meta property to dataset variable:但是在图表创建时 Chart.js 还将._meta属性写入数据集变量:

var meta = dataset._meta[me.id];
if (!meta) {
   meta = dataset._meta[me.id] = {
       type: null,
       data: [],
       dataset: null,
       controller: null,
       hidden: null,     // See isDatasetVisible() comment
       xAxisID: null,
       yAxisID: null
   };

and it doesn't delete this property on destroy() .并且它不会在destroy()上删除此属性。

If you use your old dataset object without removing ._meta property , Chart.js will add new dataset to ._meta without deletion previous data.如果您使用旧的DataSet对象不去除._meta property ,chart.js之将新的数据集添加到._meta不删除以前的数据。 Thus, at each chart's re-initialization your dataset object accumulates all previous data.因此,在每个图表重新初始化时,您的数据集对象会累积所有以前的数据。

In order to avoid this, destroy dataset object after calling Chart.destroy() .为了避免这种情况,在调用Chart.destroy()之后销毁数据集对象。

Since destroy kind of destroys "everything", a cheap and simple solution when all you really want is to just "reset the data".由于销毁会破坏“一切”,因此当您真正想要的只是“重置数据”时,一个廉价而简单的解决方案。 Resetting your datasets to an empty array will work perfectly fine as well.将数据集重置为空数组也可以正常工作。 So, if you have a dataset with labels, and an axis on each side:因此,如果您有一个带有标签的数据集,并且每边都有一个轴:

window.myLine2.data.labels = [];
window.myLine2.data.datasets[0].data = [];
window.myLine2.data.datasets[1].data = [];

After this, you can simply call:在此之后,您可以简单地调用:

window.myLine2.data.labels.push(x);
window.myLine2.data.datasets[0].data.push(y);

or, depending whether you're using a 2d dataset:或者,取决于您是否使用二维数据集:

window.myLine2.data.datasets[0].data.push({ x: x, y: y});

It'll be a lot more lightweight than completely destroying your whole chart/dataset, and rebuilding everything.它比完全销毁整个图表/数据集并重建所有内容要轻得多。

for those who like me use a function to create several graphics and want to update them a block too, only the function .destroy() worked for me, I would have liked to make an .update(), which seems cleaner but ... here is a code snippet that may help.对于那些喜欢我的人使用一个函数来创建多个图形并也想将它们更新为一个块,只有函数 .destroy() 对我有用,我本来希望制作一个 .update(),它看起来更干净但...... . 这是一个可能有帮助的代码片段。

var SNS_Chart = {};

// IF LABELS IS EMPTY (after update my datas)
if( labels.length != 0 ){

      if( Object.entries(SNS_Chart).length != 0 ){

            array_items_datas.forEach(function(for_item, k_arr){
                SNS_Chart[''+for_item+''].destroy();
            });

       }

       // LOOP OVER ARRAY_ITEMS
       array_items_datas.forEach(function(for_item, k_arr){

             // chart
             OPTIONS.title.text = array_str[k_arr];
             var elem = document.getElementById(for_item);
             SNS_Chart[''+for_item+''] = new Chart(elem, {
                 type: 'doughnut',
                 data: {
                     labels: labels[''+for_item+''],
                     datasets: [{
                        // label: '',
                        backgroundColor: [
                            '#5b9aa0',
                            '#c6bcb6',
                            '#eeac99',
                            '#a79e84',
                            '#dbceb0',
                            '#8ca3a3',
                            '#82b74b',
                            '#454140',
                            '#c1502e',
                            '#bd5734'
                        ],
                        borderColor: '#757575',
                        borderWidth : 2,
                        // hoverBackgroundColor : '#616161',
                        data: datas[''+for_item+''],
                     }]
                 },
                 options: OPTIONS

             });
             // chart
       });
       // END LOOP ARRAY_ITEMS

  }
 // END IF LABELS IS EMPTY ...

just declare let doughnut = null before creating your chart在创建图表之前声明let doughnut = null

const doughnutDriverStatsChartCanvas = $('#dougnautChartDriverStats').get(0).getContext('2d')
const doughnutOptionsDriverStats = {
    maintainAspectRatio: false,
    responsive: true,
}
let doughnut = null
doughnut = new Chart(doughnutDriverStatsChartCanvas, {
    type: 'doughnut',
    data: doughnutChartDriverStats,
    options: doughnutOptionsDriverStats
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM