Home forums Report Bugs Weird Chart Behaviour in IOS Browser on mobile?

Tagged: ,

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #28166

    We can only reproduce this in ios browsers on mobile devices

    I have included a link to a repl.it a simple example demonstrating the problem at the bottom of this post.

    (make sure you click the open in new tab popout thing to get just the webpage in your iPad or iPhone)

    there are 12 charts (without much data), when you click the add datapoint button, it will render fine for several clicks(between 2-10… probably never more than 20 before the error occurs), then it will stop rendering traces, and eventually stop rendering the entire chart.
    even when the chart is not rendering you can still see the hover popups (and sometimes the chart title)

    sometimes click to update the last point, or add a new point will cause traces to come back in some charts (and disappear in others)

    (you can also reproduce just by refreshing the page several times)

    (to make it occur faster you can increase the NUM_CHARTS variable in the javascript(if you have a newish iPad PRO it takes more clicks and charts to reproduce))

    simple example @ https://repl.it/repls/SpicyUnfinishedCryptos
    this example is based off https://canvasjs.com/docs/charts/basics-of-creating-html5-chart/updating-chart-options/

    SUCESSFULLY REPRODUCED on iPhone X and iPhone XS and iPad 5th GEN, (it took about 20 charts to reproduce on Gen2 iPad PRO)

    #28191

    @joran,

    Earlier WebKit allowed 448MB of canvas buffer memory whereas now they have dropped it to 224MB. Because of this, there was some memory issue with CanvasJS Charts in the latest iOS. However, we had optimized memory consumption in CanvasJS v2.3 – please check out this release blog for more info.

    Also, we will further optimize it in future releases.

    —-
    Manoj Mohan
    Team CanvasJS

    #28214

    Thanks for getting back to me… Im not sure how to check the “canvas buffer memory” , however the (linked example) site only reports about 17mb of “VM memory” use-age in chrome developer tools … can you tell me how to monitor the buffer memory? or inspect it?

    also i found this that might be of interest https://stackoverflow.com/a/52586606/541038 that claims the memory of canvas is not freed on reload or destroy, so you can work around it by setting the width and height of the canvas element(s?) to zero … Im not sure if i can hook into your overlay canvas or _prerenderCanvas or whatever to do this on my end (maybe i can im going to try tomorrow)

    #28227

    OK so some further information I found today (I used the safari dev tools panel which includes a canvas tab)

    • The initial time a chart is created and rendered it creates 5 canvas elements that are X mb big (where X is something like width x height x 3)
    • Every subsequent time that chart.render gets called a new canvas element of X mb is created, is created as far as I can tell none of the earlier ones get garbage collected
    • chart.destroy();chart = null had did not release the earlier canvases memory
    • I can mostly mitigate it by setting [HTMLCanvasElement].height = 0; [HTMLCanvasElement].width = 0; [HTMLCanvasElement].style.width = 0; [HTMLCanvasElement].style.height = 0 before calling destroy
    • I was able to find the following 4 canvases chart.ctx.canvas, chart.overlayCanvas, chart.legend.ghostCtx.canvas, chart._preRenderCtx.canvas
    • I was unable to find the 5th canvas element, so one canvas stuck around at X mb … the rest were quite small (~64bytes) after setting width and height to zero

    here is the code i used to clean up before calling chart.destroy;chart = null

    
    const ele = document.getElementById("chartContainer");
    const c = chartsInstances[chartId]
    c.legend.ghostCtx.canvas.style.height = 0;
    c.legend.ghostCtx.canvas.height = 1;
    c.legend.ghostCtx.canvas.style.width = 0;
    c.legend.ghostCtx.canvas.width = 1;
    c._preRenderCtx.canvas.width = 1;
    c._preRenderCtx.canvas.height = 1;
    c._preRenderCtx.canvas.style.width = 0;
    c._preRenderCtx.canvas.style.height = 0;
    const canvases = ele.querySelectorAll('canvas');
    for (const canvas of canvases) {
      canvas.width = 1;
      canvas.height = 1;
      canvas.style.width = 0;
      canvas.style.height = 0;
    }
    
    #28233

    @joran,

    Thanks for your suggestion. We will further improve this behaviour in future releases.

    —-
    Manoj Mohan
    Team CanvasJS

    #28241

    thanks for the response :)

    can you help suggest where i might be able to find the 5th canvas element? or how to capture it… I just can’t find it and the datastructure is big…. im probably looking right past it

    – Joran

    #28244

    I found it

    this line
    this._preRenderCanvas = createCanvas(this.width, this.height); @ roughly line 5044 in CanvasJS.js

    overwrites the canvas instance that already exists for variable this._preRenderCanvas effectivly orphaning the old canvas (which IOS will not GC because its not size 0)

    I fixed it by patching it to

    
    if(this._preRenderCanvas){
        setCanvasSize(this._preRenderCanvas,0,0)
    }
    this._preRenderCanvas = createCanvas(this.width, this.height,"_preRenderCanvas");
    
Viewing 7 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.