Search code examples
javascripthtmldynamicchartsdestroy

Chartjs Line Chart flickers between old and new data when hovering


My problem is not making the chart, but when I input new values for the start freq and end freq, which are the first and last values of my x-axis array after calling getFreq(), the chart glitches when I hover, going back and forth between the old data and new data. Can anyone help me figure out how to delete the old data and make/update to a new chart where the chart doesn't flicker? My code is shown below.

let Conductivity = document.getElementById('Conductivity');
let StartFreq = document.getElementById('StartFreq');
let StopFreq = document.getElementById('StopFreq');
let a = document.getElementById('a');
let b = document.getElementById('b');
let unitInch = document.getElementById('unitInch');
let test = document.getElementById('test');
let diameter = document.getElementById('Diameter');
let ctx = document.getElementById('myChart').getContext('2d');


function generateLabels(){
    // To generate the xa xis intervals
    let xaxis = [];
    for (let i = 0; i <= 10; i++) {
        let valToAppend = Math.round((parseFloat(StartFreq.value) + (parseFloat(StopFreq.value)-parseFloat(StartFreq.value)) / 10 * i)*100)/100;
        if (valToAppend <= parseFloat(StopFreq.value)){
            xaxis.push(valToAppend)
        }

    }
    return xaxis
}
function getFreq(){
    let x = generateLabels();
    let freq = [];
    let start = x[0];
    freq.push(start);
    let end = x[x.length - 1];
    for (let i=0; i < 4 * (end-start);i++){
        let lastfreq = freq[freq.length - 1];
        freq.push(lastfreq + 0.25)
    }
    // let rklen = freq.length;

    return freq
}


function getRS(){
    let RS = [];
    let freq = getFreq();
    freq.forEach(element =>{
        let RStoAppend = Math.sqrt((2*Math.PI*(Math.pow(10,9)*element)*(4*Math.PI*Math.pow(10,-7)))/(2*(parseFloat(Conductivity.value)*Math.pow(10,7))))     ;
        RS.push(RStoAppend)
    });
    return RS

}
function getRK(){
    let RK = [];
    let freq = getFreq();
    freq.forEach(element => {
        let RKtoappend = (2*Math.PI*(element * Math.pow(10,9))) / (3* Math.pow(10,8));
        RK.push(RKtoappend)
    });

    return RK
}
function getRbeta(){
    let Rbeta = [];
    let RK = getRK();
    RK.forEach(element => {
        let Rbetatoappend = Math.sqrt(Math.pow(element,2) - Math.pow((Math.PI/(parseFloat(a.value)*25.4/1000)),2));
        Rbeta.push(Rbetatoappend);
    });


    return Rbeta;
}
function getRatte(){
    let Ratte = [];
    let RS = getRS();
    let RK = getRK();
    let Rbeta = getRbeta();
    for (let i = 0; i < RS.length ;i++){
        let Rattetoappend = RS[i]*(2*(25.4/1000*parseFloat(b.value))*Math.pow(Math.PI,2)+Math.pow((parseFloat(a.value)*25.4/1000),3)*Math.pow(RK[i],2))/(Math.pow((parseFloat(a.value)*25.4/1000),3)*(25.4/1000*parseFloat(b.value))*Rbeta[i]*RK[i]*377)/(1000/25.4);
        Ratte.push(Rattetoappend);
    }
    // test.innerHTML = '<td id="test">' + Ratte + '<td>';

    return Ratte
}
function getRTE10(){
    let RTE10 = [];
    let Ratte = getRatte();
    Ratte.forEach(element => {
        if (isNaN(-20*Math.log10(Math.exp(-element)) * parseFloat(unitInch.value))) {
            RTE10.push(0)
        }
        else {
            RTE10.push(-20 * Math.log10(Math.exp(-element)) * parseFloat(unitInch.value))
        }
    });


    return RTE10
}
//////////////////////// For CTE11/////////////
function getk(){
    let k = [];
    let freq = getFreq();
    freq.forEach(element => {
        k.push(2*Math.PI*element*Math.pow(10,9)/(3*Math.pow(10,8)))
    });
    return k
}
function getbeta(){
    let beta = [];
    let k = getk();
    k.forEach(element => {
        beta.push(Math.sqrt(Math.pow(element,2)-Math.pow((1.8412/(parseFloat(diameter.value)/2*25.4/1000)),2)))
    });
    return beta
}
function getTE11_1(){
    let TE11_1 = [];
    let k = getk();
    let rs = getRS();
    let beta = getbeta();
    for (let i = 0; i < rs.length ;i++){
        TE11_1.push(rs[i]*(Math.pow((1.8412/(parseFloat(diameter.value)/2*25.4/1000)),2)+Math.pow(k[i],2)/(Math.pow(1.8414,2)-1))/((parseFloat(diameter.value)/2*25.4/1000)*k[i]*beta[i]*377)/(1000/25.4));
    }
    return TE11_1
}

function getCTE11(){
    let CTE11 = [];
    let TE11_1 = getTE11_1();
    TE11_1.forEach(element => {
        if (isNaN(-20 * Math.log10(Math.exp(-element)) * parseFloat(unitInch.value))) {
            CTE11.push(0)
        }
        else {
            CTE11.push(-20 * Math.log10(Math.exp(-element)) * parseFloat(unitInch.value))
        }
    });
   // test.innerHTML = '<td id="test">' + CTE11 + '<td>';
    return CTE11
}

function getTM01(){
    let TM01 = [];
    let rs = getRS();
    let freq = getFreq();
    for (let i = 0; i < rs.length ;i++){
        TM01.push(rs[i]/((parseFloat(diameter.value)/2 *25.4/1000)*377*Math.sqrt(1-Math.pow(((2.4049/(2*Math.PI*parseFloat(diameter.value)/2 *25.4/1000)*0.3)*Math.pow(10,9)/(freq[i]*Math.pow(10,9))),2)))/(1000/25.4));
    }
    return TM01

}

function getCTM01(){
    let CTM01 = [];
    let TM01 = getTM01();

    TM01.forEach(element => {
        if (isNaN(-20 * Math.log10(Math.exp(-element)) * parseFloat(unitInch.value))) {
            CTM01.push(0)
        }
        else {
            CTM01.push(-20 * Math.log10(Math.exp(-element)) * parseFloat(unitInch.value))
        }
    });
    return CTM01
}


function getAt2(){
    let at2 = [];
    let freq = getFreq();
    freq.forEach(element =>{
       at2.push(Math.pow(((3.8318/(2*Math.PI*parseFloat(diameter.value)/2 *25.4/1000)*0.3)*Math.pow(10,9)/(element*Math.pow(10,9))),2)+1/(Math.pow(3.8318,2)-1))
    });

    return at2
}
function getAt1(){
    let at1 = [];
    let freq = getFreq();
    let rs = getRS();
    for (let i = 0; i < rs.length ;i++){
        at1.push(rs[i]/(parseFloat(diameter.value)/2 *25.4/1000*377*Math.sqrt(1-Math.pow(((3.8318/(2*Math.PI*parseFloat(diameter.value)/2 *25.4/1000)*0.3)*Math.pow(10,9)/(freq[i]*Math.pow(10,9))),2)))/(1000/25.4));
    }
    return at1
}

function getCTE01(){
    let CTE01 = [];
    let at1 = getAt1();
    let at2 = getAt2();
    for(let i = 0;i < at1.length; i++){
        if (isNaN((-20*Math.log10(Math.exp(-(at1[i]*at2[i])))*parseFloat(unitInch.value)))) {
            CTE01.push(0)
        }
        else {
            CTE01.push(-20 * Math.log10(Math.exp(-(at1[i] * at2[i]))) * parseFloat(unitInch.value))
        }
        }
    return CTE01
}

function getdata(){
    let data =[];
    let xaxis = getFreq();
    let RTE10 = getRTE10();
    let CTE11 = getCTE11();
    let CTM01 = getCTM01();
    let CTE01 = getCTE01();
    data.push(xaxis,RTE10,CTE11,CTM01,CTE01);
    return data
}


function draw_chart(data) {
    let chart = new Chart(ctx, {

        // The type of chart we want to create
        type: 'line',

        // The data for our dataset
        data: {
            labels: data[0],
            datasets: [{
                label: 'R-TE10',
                data: data[1],
                pointStyle : 'line',
                backgroundColor: 'transparent',
                borderColor: 'blue',
                pointRadius: '0'
            },{
                label: 'C-TE11',
                data: data[2],
                pointStyle : 'line',
                backgroundColor: 'transparent',
                borderColor: 'orange',
                pointRadius: '0'
            },{
                label: 'C-TM01',
                data: data[3],
                pointStyle : 'line',
                backgroundColor: 'transparent',
                borderColor: 'green',
                pointRadius: '0'
            },{
                label: 'C-TE01',
                data: data[4],
                pointStyle : 'line',
                backgroundColor: 'transparent',
                borderColor: 'red',
                pointRadius: '0'
            }]
        },

        // Configuration options go here
        options: {
            responsive : true,
            scales: {
                xAxes :[{
                    ticks: {
                        min: StartFreq,
                        max: StopFreq,
                        suggestedMin: StartFreq,
                        suggestedMax: StopFreq,
                        stepSize: 0.25,
                    }
                }],

                yAxes: [{
                    ticks: {
                        min:0,
                        max:10,
                        suggestedMin: 0,
                        suggestedMax: 10,
                        maxTicksLimit: 11,
                        stepSize : 1,
                        beginAtZero: true,
                    }
                }]
            }

        }
    });
}



function generateChart() {
   // removeData(chart);
    let data = getdata();
    draw_chart(data);
}

HTML

<table class="inner">
        <tr class="inner">
          <td class="inner"> Attenuation (air) /</td>
          <td class="inner"> <input class="numInput" type="number" id="unitInch"/></td>
          <td class="inner">(in inches)</td>

        </tr>
        <tr class="inner">
          <td class="inner" colspan="2">Rectangular WG</td>
          <td class="inner">Circular WG</td>
          <td class="inner">Material</td>
        </tr>
        <tr class="inner">
          <td class="inner">a (inch)</td>
          <td class="inner">b (inch)</td>
          <td class="inner">Diameter (inch)</td>
          <td class="inner">Conductivity ( x 10<sup>7</sup> S/m)</td>
          <td class="inner">Start Freq (GHz)</td>
          <td class="inner">Stop Freq (GHz)</td>
        </tr>
        <tr class="inner">
          <td class="inner"><input class="numInput" type="number" id='a'/></td>
          <td class="inner"><input class="numInput" type="number" id="b"/></td>
          <td class="inner"><input class="numInput" type="number" id="Diameter"/></td>
          <td class="inner"><input class="numInput" type="number" id="Conductivity"/></td>
          <td class="inner"><input class="numInput" type="number" id="StartFreq"/></td>
          <td class="inner"><input class="numInput" type="number" id="StopFreq"/></td>

        </tr>
      </table>

  <button onclick="generateChart()"><strong>Generate Chart</strong></button>
<div id="chartsize">
      <canvas id="myChart"></canvas>
</div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.js"></script>
      <script src="js/chart.js"></script>

**By the way, my chart works completely fine the first time I load the page, then when I update, the chart flickers like crazy when hovering. I added in a lot more code, so that you guys can recreate the chart and figure it out. Please ignore the bad style, I am new to Web Development... **


Solution

  • This seems to be a somewhat common issue with Chart.js. The typical suggested solutions are to either destroy the chart instance, or remove the canvas element and create a new one whenever the chart is updated.

    For the first possible solution, you can try calling destroy() on your chart object. For example, if your chart object is chart, you could call chart.destroy();. From the documentation:

    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. This must be called before the canvas is reused for a new chart.

    However, this doesn't seem to always work. Removing and creating the canvas element seemed to work when I tested it with your code. You could use a function like below and call it whenever you click the "Generate Chart" button:

    function resetCanvas (){
      $("canvas").remove();
      $("#chartsize").append('<canvas id="myChart"><canvas>');
      canvas = document.querySelector("#myChart");
      ctx = canvas.getContext("2d");
    };
    

    Note that you might need to set the height and width of your chart container. I'm using jQuery for this function for simplicity. But you could reproduce it using vanilla JS if needed.

    See the fiddle here for an example of this using your code: https://jsfiddle.net/fL1rd3wp/1/