canvashtml5-canvasmobile-chrome

Mobile Chrome bug - animated canvas crashes the browser when canvas height is 3-5 times the screen height AND canvas on canvas is used


If you use this, it works fine (using img on canvas):

ctx_bg.drawImage($("#image_tile")[0], positions[i].x, positions[i].y);

If you use this, it crashes the browser within 3 seconds (using canvas on canvas):

ctx_bg.drawImage($(".canvas_tile")[0], positions[i].x, positions[i].y);

Above 2 lines are the only difference between the 2 showcases below. Tested on Android (Samsung S10, Chrome 80.0.3987.149).

Also, canvas on canvas works fine when the canvas height is smaller (1-2 screen heights). Also, it works fine on desktop chrome!

Is this a mobile browser bug or is it fixable?

EDIT: Since you can't run below code on stackoverflow mobile view, then here are quick links to view on mobile:

This uses 2nd CANVAS particles and CRASHES on mobile:

$(document).ready(function() {

    ctx_bg = $(".bg_canvas")[0].getContext("2d");
    ctx_child = $(".canvas_tile")[0].getContext("2d");

    ctx_child.beginPath();
    ctx_child.arc(20, 20, 5, 0, 1.5 * Math.PI);
    ctx_child.stroke();

    innerWidth = $("body").innerWidth();
    innerHeight = $("body").innerHeight()*5; 
    numberOfElements = 222;
    positions = [];
    angle = 0;


    $(".bg_canvas")[0].width = innerWidth; 
    $(".bg_canvas")[0].height = innerHeight;

    for (var i=0; i<numberOfElements; i++) {
        positions.push({x: Math.random()*innerWidth, y: Math.random()*innerHeight});
    };


    function animateCircles() {
        bgAnimation = requestAnimationFrame(animateCircles)
        ctx_bg.clearRect(0, 0, innerWidth, innerHeight);
        for (var i = 0; i < numberOfElements; i++){
            positions[i].y+=3;
            if (positions[i].y > innerHeight) {
                positions[i].y = 0;
            }
            ctx_bg.drawImage($(".canvas_tile")[0], positions[i].x, positions[i].y);
            // ctx_bg.drawImage($("#image_tile")[0], positions[i].x, positions[i].y);
        }
    }
    animateCircles()
})
body, html {
    padding: 0;
    margin: 0;
    width: 100%;
    height: 100%;
}
#image_tile, .canvas_tile  {
    display: none;
}
<!DOCTYPE html>

<html>
    <head>
        <title>KRAATER</title>
        <meta charset="UTF-8">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
    </head>
    <body>
        <img id="image_tile" src="https://www.gravatar.com/avatar/435af02114568dbaf00005b28c3ef592?s=48&d=identicon&r=PG">
        <canvas class="canvas_tile"></canvas>
        <canvas class="bg_canvas"></canvas>
    </body>
</html>

This uses 2nd BITMAP particles and WORKS on mobile:

$(document).ready(function() {

    ctx_bg = $(".bg_canvas")[0].getContext("2d");
    ctx_child = $(".canvas_tile")[0].getContext("2d");

    ctx_child.beginPath();
    ctx_child.arc(20, 20, 5, 0, 1.5 * Math.PI);
    ctx_child.stroke();

    innerWidth = $("body").innerWidth();
    innerHeight = $("body").innerHeight()*5;
    numberOfElements = 222;
    positions = [];
    angle = 0;


    $(".bg_canvas")[0].width = innerWidth; 
    $(".bg_canvas")[0].height = innerHeight;

    for (var i=0; i<numberOfElements; i++) {
        positions.push({x: Math.random()*innerWidth, y: Math.random()*innerHeight});
    };


    function animateCircles() {
        bgAnimation = requestAnimationFrame(animateCircles)
        ctx_bg.clearRect(0, 0, innerWidth, innerHeight);
        for (var i = 0; i < numberOfElements; i++){
            positions[i].y+=3;
            if (positions[i].y > innerHeight) {
                positions[i].y = 0;
            }
            // ctx_bg.drawImage($(".canvas_tile")[0], positions[i].x, positions[i].y);
            ctx_bg.drawImage($("#image_tile")[0], positions[i].x, positions[i].y);
        }
    }
    animateCircles()
})
body, html {
    padding: 0;
    margin: 0;
    width: 100%;
    height: 100%;
}
#image_tile, .canvas_tile  {
    display: none;
}
<!DOCTYPE html>

<html>
    <head>
        <title>KRAATER</title>
        <meta charset="UTF-8">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
    </head>
    <body>
        <img id="image_tile" src="https://www.gravatar.com/avatar/435af02114568dbaf00005b28c3ef592?s=48&d=identicon&r=PG">
        <canvas class="canvas_tile"></canvas>
        <canvas class="bg_canvas"></canvas>
    </body>
</html>


Solution

  • For now, I solved it with a hack which converts the canvas data first to image:

    $("<img src='" + targetCanvas.toDataURL() + "'>");
    

    Then appending this to the DOM and loading that image into the large canvas. Not very efficient, as with large img objects the rendering will slow down, so I suggest only using it to render for phone sizes and use canvas on canvas on desktop.

    Will award "accepted answer" to another post that is not a hack, if it comes around.

    Another solution would be to make the main canvas's position fixed and moving the content based on scrolled amount, but it will have lag.