IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Cacher les canvas hors écran, fausse bonne idée ?


Sujet :

JavaScript

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut Cacher les canvas hors écran, fausse bonne idée ?
    Bonjour

    Pour un petit jeu en 3d iso, j'ai une scène avec beaucoup de canvas (disons peut être 3000 en mémoire), de taille 200 x 200 environ.
    La scène est très grande, bien plus que mon écran.

    J'ai toujours pensé, et on m'a toujours conseillé, de cacher les canvas non affichés à l'écran, pour qu'ils sortent des calculs de zIndex et du rendertree du browser.

    Dans le code ci-dessous, la ligne "cx += x; cy += y;" est justement sensée faire ceci. L'idée est d'alléger la mémoire : sans cette ligne, les carrés restent tout le temps affichés. Avec la ligne, si les canvas sont hors écran, ils passent en display:none.

    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    <!DOCTYPE html>
    <html>
    <body>
     
        <style>
            
        body {
            overflow: hidden;
        }
     
        </style>
     
        <div id="anchor" style="position:absolute"></div>
     
        <script>
            
            // crée 1000 carrés rouge et les affiche
            for (let i=0; i<1000; i++) {
                    
                    var canvas = document.createElement('canvas');
                
                    canvas.width  = 60;
                    canvas.height = 60; 
                    canvas.style.zIndex   = 8;
                    canvas.style.position = "absolute";
                    canvas.style.border   = "1px solid";
                    canvas.style.background = "red";
                canvas.style.left = Math.random() * 500 + "px";
                canvas.style.top = Math.random() * 500 + "px";
                
                // bcp plus fluide de tout ajouter à anchor et de bouger
                // anchor, plutôt que de looper sur chaque canvas et de 
                // modifier leur position canvas par canvas
                    document.getElementById("anchor").appendChild(canvas);
            }
            
            document.onkeydown = function checkKey(e) {
            
                var start = new Date();
                
                e = e || window.event;
                var anchor = document.getElementById("anchor")
                var x = anchor.style.left;
                var y = anchor.style.top;
                
                x = x || "0px";
                y = y || "0px";
                x = parseInt(x.substring(0, x.length - 2));
                y = parseInt(y.substring(0, y.length - 2));
                
                if (e.keyCode == '38') y -= 10;
                else if (e.keyCode == '40') y += 10;
                else if (e.keyCode == '37') x -= 10;
                else if (e.keyCode == '39') x += 10;
            
                anchor.style.left = x + "px";
                anchor.style.top = y + "px";
                
                var descendents = anchor.getElementsByTagName('*');
                 
                for (i=0; i<descendents.length; i++) {
                    
                    var c = descendents[i];
                    
                    var cx = c.style.left;
                    cx = parseInt(cx.substring(0, cx.length - 2));
                    var cy = c.style.top;
                    cy = parseInt(cy.substring(0, cy.length - 2));
                    
                    cx += x; cy += y;
                   
                    if ((cx + 60 < document.documentElement.clientWidth && cx > 0) && cy + 60 < document.documentElement.clientHeight && cy > 0) {
                        c.style.display = "block";
                        c.style.zIndex = cy;
                    } else { 
                        c.style.display = "none";
                    }
                }
                
                var time = new Date() - start;
                
                console.log(time);
            }
     
        </script>
     
    </body>
    </html>

    Résultat :
    - avec cette ligne, quand je scroll up, j'ai au début des temps d’exécution d'environ 170 ms, puis 13/14 quand tout a disparu vers le haut.
    - sans : tout le temps 13/14.

    J'en déduis que afficher/cacher les canvas est peut être bon pour la mémoire, mais que ces opérations sont en fait très très couteuses pour le framerate, ai-je bien compris ? Si oui, existe-t-il une solution propre pour cacher les canvas et les faire sortir du rendertree, pour économiser la mémoire, tout en économisant le framerate également ?

    Merci !

  2. #2
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 209
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 209
    Par défaut
    Bonjour,
    pas regardé en détail mais le constat est qu'il te faut mettre en « cache » les données que tu réutilises souvent.

    Si l'on regarde par exemple cette ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ((cx + 60 < document.documentElement.clientWidth && cx > 0) && cy + 60 < document.documentElement.clientHeight && cy > 0) {
    elle serait favorablement remplaçable par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ((cx > 0 && cx < MAX_X) && ( cy > 0 && cy < MAX_Y)) {
    avec les déclarations suivantes, juste avant la boucle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    const MAX_Y = document.documentElement.clientHeight - 60;
    const MAX_X = document.documentElement.clientWidth - 60;
    le gain devrait être substantiel.

  3. #3
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    Salut,

    J'étais assez dubitatif avant de tester mais je te confirme, le gain est énorme.

    Merci !

  4. #4
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 986
    Par défaut
    Comme ça ne fonctionnait pas chez moi (firefox 113.0.2 linux), je l'ai réécrit à ma sauce:
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    <!DOCTYPE html>
    <html>
        <head>
        <style>
            body {
                overflow: hidden;
            }
            
            #anchor {
                position: absolute;
            }
     
            #anchor > canvas {
                width: 60px;
                height: 60px;
                z-index: 8;
                position: absolute;
                background-color: red;
                border: 1px solid black;
            }
            </style>
        </head>
    <body>
        <div id="anchor"></div>
     
        <script>
            const anchor = document.getElementById('anchor');
            
            const step = 10;
     
            const moves = new Map([
                ['ArrowLeft',  { x: -step, y: 0     }],
                ['ArrowUp',    { x: 0,     y: -step }],
                ['ArrowRight', { x: step,  y: 0     }],
                ['ArrowDown',  { x: 0,     y: step  }]
            ]);
            
            for (let i=0; i<1000; i++) {
                let canvas = document.createElement('canvas');
                
                canvas.style.left = `${Math.round(Math.random() * 500)}px`;
                canvas.style.top = `${Math.round(Math.random() * 500)}px`;
                
                anchor.appendChild(canvas);
            }
     
            document.addEventListener('keydown', (e) => {
                    
                if (!moves.has(e.code)) 
                    return;
     
                const move = moves.get(e.code);
                
                let start = new Date();
                
                let x = parseInt(anchor.style.left || 0);
                let y = parseInt(anchor.style.top || 0);
                           
                anchor.style.left = `${x + move.x}px`;
                anchor.style.top = `${y + move.y}px`;
                
                const MAX_X = document.documentElement.clientWidth - 60;
                const MAX_Y = document.documentElement.clientHeight - 60;
                           
                for (const canvas of anchor.getElementsByTagName('canvas')) {    
                    let cx = parseInt(canvas.style.left) + x;
                    let cy = parseInt(canvas.style.top) + y;
                                    
                    canvas.style.display = (cx > 0 && cx < MAX_X && cy > 0 && cy < MAX_Y)
                        ? 'block'
                        : 'none';
                }
                
                console.log(new Date() - start);
            },
            false
        );
        </script>
    </body>
    </html>

    Une idée de piste: pour éviter de passer en revue les mille canvas à chaque fois qu'on appuie sur une flêche, on pourrait créer une structure (deux en fait, une pour les abscisses, une pour les ordonnées) qui associerait une tranche de 10 (ou plus) à l'ensemble des canvas positionnés dans cette tranche. De cette manière, il suffit de savoir quelle tranche est concernée par le mouvement, pour ne passer en revue que les canvas contenus dans la tranche.

  5. #5
    Membre éclairé
    Homme Profil pro
    Electron libre since 80's
    Inscrit en
    Juillet 2016
    Messages
    92
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Aube (Champagne Ardenne)

    Informations professionnelles :
    Activité : Electron libre since 80's
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 92
    Par défaut
    Hum, ton écran peut comporter disons 20 canvas.

    Tu créé ces 20 canvas tu les affiche.

    En cas de scroll, tu changes le contenu des canvas.

    @nosmoking : normalement, lr compilateur JIT s'occupe de cette optimisation.Mais autant le faire soi-même est très formateur.

  6. #6
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 209
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 209
    Par défaut
    Citation Envoyé par xor AX AX
    @nosmoking : normalement, lr compilateur JIT s'occupe de cette optimisation.
    pas sur la lecture de propriétés d'éléments du DOM celles-ci pouvant être modifiées n'importe où.

    D'autre part un test rapide montre qu'il n'y a pas d'optimisation à ce niveau et comme le dit MrPchoun
    J'étais assez dubitatif avant de tester mais je te confirme, le gain est énorme.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 345
    Dernier message: 29/04/2025, 09h34
  2. [Article] Pourquoi générer le code JavaScript est une fausse bonne idée
    Par sekaijin dans le forum Général JavaScript
    Réponses: 11
    Dernier message: 25/01/2015, 22h14
  3. SSII la fausse bonne idée ?
    Par Mr_Exal dans le forum Emploi
    Réponses: 20
    Dernier message: 17/07/2012, 22h00
  4. Réponses: 2
    Dernier message: 27/01/2009, 22h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo