-2

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>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
yodalr
  • 9,778
  • 10
  • 32
  • 47
  • Yes it's a bug. A browser should never crash. Now, you apparently already know at least one way around: use an . What else do you want from stackoverflow community? Report that bug if it's not there already. There is [this one](https://bugs.chromium.org/p/chromium/issues/detail?id=1015729) which is marked as fixed, but their workaround there could very well not fit for mobile devices. Maybe you could just comment on that issue. – Kaiido Apr 03 '20 at 02:40
  • Also, unrelated, but doing `innerWidth = foo` actually **sets** [`window.innerWidth`](https://developer.mozilla.org/en-US/docs/Web/API/window/innerWidth) to `foo`, messing up with any other scripts on your page which would rely on this value to be what it's supposed to be. Also, store your DOM elements instead of creating so much junk with your jQuery objects creation at every iteration. – Kaiido Apr 03 '20 at 02:40
  • @Kaiido, please explain, how am I creating jQuery objects with every iteration? – yodalr Apr 03 '20 at 09:40
  • `ctx_bg.drawImage(` **`$(".canvas_tile")[0]`** ... That creates a new jQuery object at every iteration. Just store once `const source = $(.canvas_tile")[0]` outside of any loop, and then call `ctx_bg.drawImage(source`... – Kaiido Apr 03 '20 at 09:46
  • Why that aggressive tone? Simply [try yourself](https://jsfiddle.net/du943fyn/) `$(elem) === $(elem)`. It will be false because each call to `$()` returns a new Object. And each call to `$(selector)` invokes the very long path of walking down the DOM to find the selected element. – Kaiido Apr 03 '20 at 11:28
  • Check with chrome memory tools, I don't see that anything is leaking or new objects being created. – yodalr Apr 03 '20 at 13:03
  • I'm not saying it's leaking, if some day you find a memory leak then file a bug to the browser's vendor. I'm saying that your code is creating garbage, sure this garbage will be collected, but calling the collector and pressuring the memory are very bad things to do. – Kaiido Apr 03 '20 at 13:25

1 Answers1

0

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.

yodalr
  • 9,778
  • 10
  • 32
  • 47