Une très, très, très grande div

109

Pour un de mes projets (voir BigPictu.re ou bigpicture.js GitHub project ), je dois gérer potentiellement un très, très, très gros <div>conteneur.

Je savais qu'il y avait un risque de mauvaises performances avec l'approche simple que j'utilise, mais je ne m'attendais pas à ce qu'elle soit principalement présente avec ... Chrome uniquement!

Si vous testez cette petite page (voir code ci-dessous), le panoramique (clic + glisser) sera:

  • Normal / lisse sur Firefox
  • Normal / lisse même sur Internet Explorer
  • Très lent (presque planter) sur Chrome!

Bien sûr, je pourrais ajouter du code (dans mon projet) pour le faire lorsque vous zoomez beaucoup, le texte avec une taille de police potentiellement très grande serait masqué. Mais quand même, pourquoi Firefox et Internet Explorer le gèrent-ils correctement et non Chrome?

Existe-t-il un moyen en JavaScript, HTML ou CSS de dire au navigateur de ne pas essayer de rendre la page entière (qui fait ici 10000 pixels de large) pour chaque action? (ne rendre que la fenêtre courante!)


<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <style>
            html, body {
                overflow: hidden;
                min-height: 100%; }

            #container {
                position: absolute;
                min-height: 100%;
                min-width: 100%; }

            .text {
                font-family: "Arial";
                position: absolute;
            }
        </style>
    </head>

    <body>
        <div id="container">
            <div class="text" style="font-size: 600px; left:100px; top:100px">Small text</div>
            <div class="text" style="font-size: 600000px; left:10000px; top:10000px">Very big text</div>
        </div>

        <script>
            var container = document.getElementById('container'), dragging = false, previousmouse;
            container.x = 0; container.y = 0;

            window.onmousedown = function(e) { dragging = true; previousmouse = {x: e.pageX, y: e.pageY}; }

            window.onmouseup = function() { dragging = false; }

            window.ondragstart = function(e) { e.preventDefault(); }

            window.onmousemove = function(e) {
                if (dragging) {
                    container.x += e.pageX - previousmouse.x; container.y += e.pageY - previousmouse.y;
                    container.style.left = container.x + 'px'; container.style.top = container.y + 'px';
                    previousmouse = {x: e.pageX, y: e.pageY};
                }
            }
        </script>
    </body>
</html>
Basj
la source
54
[OT] Taille de police 600K. Doit être une fonctionnalité d'accessibilité pour les personnes ayant une très mauvaise vue? ;-)
geert3
61
@ geert3 Je suis sûr que c'est pour un navigateur Web en orbite lunaire
David Wilkins
3
Votre démo est fluide dans Chrome 41.0.2236.0 dev-m
Pier-Luc Gendreau
11
Je suis en canari (41.0.2241.0 canary) et j'obtiens toujours le décalage. Vous devriez l'essayer sur un ordinateur portable au lieu d'une plate-forme de jeu, vous le verrez
markasoftware
3
Contrairement à la croyance populaire, IE est en fait plus rapide que Chrome pour le rendu de la plupart des pages. Son moteur javascript est cependant un peu plus lent.
Falanwe

Réponses:

62

Changer pour position: fixedsemble accélérer les choses.

geert3
la source
27
Ce n'est pas une réponse directe à sa question, mais c'est une solution potentielle à son problème initial (réponse lente dans Chrome). Penser en dehors des sentiers battus devrait être encouragé, à mon humble avis.
geert3
Ici, cela semble fonctionner parfaitement: gget.it/e0ubdh67/big-div-test_fixed.html . Est-ce que tu sais pourquoi ? :)
Basj
2
Je ne peux que deviner. fixedest évidemment moins complexe à mettre en page et peut-être peuvent-ils faire plus d'optimisations. Mais je n'ai pas regardé le code source du moteur de rendu si vous voulez dire cela ;-)
geert3
1
Cette réponse, et celle fournie par ViliusL, ont toutes deux le même commentaire "laisser un commentaire", par des personnes différentes. À quel point cela est cool?
Joe
2
@Joe ceux-ci proviennent d'un ensemble de réponses standard fournies par StackOverflow à l'usage des modérateurs. Certains doivent cependant modérer plus modérément.
geert3
42

Utilisez à la transformplace de top/left:

container.style.transform = 'translate(' + container.x + 'px, ' + container.y + 'px)';

Une démo en direct sur jsFiddle .

Teemu
la source
2
Une chose très étrange : dans votre jsFiddle, c'est vraiment rapide avec Chrome. J'ai fait exactement la modification que vous proposez dans mon code d'origine ici: gget.it/1owxq8kr/big-div-test_transform.html , et ce dernier lien est lent sur Chrome :( Comment est-ce possible? Il ressemble à votre jsFiddle ! [Note: miraculeusement, les réponses de geert3 semblent fonctionner, je ne sais pas pourquoi mais cela fonctionne: gget.it/e0ubdh67/big-div-test_fixed.html]
Basj
@Basj Cela dépend peut-être de la version, mon Chrome (39.0.2171.71 m) parcourt la page liée dans votre commentaire aussi facilement et rapidement que FF. Quoi qu'il en soit, définir la position sur fixedsupprime l'élément du flux de texte et économise beaucoup de rendu. Dans la documentation de transformMDN dit: "... un contexte d'empilement sera créé. Dans ce cas, l'objet agira comme un bloc contenant pour la position: éléments fixes qu'il contient."
Teemu
2
Étrange, j'ai aussi Chrome 39.0.2171.71 m ... et gget.it/1owxq8kr/big-div-test_transform.html panoramique lent, aussi lent que ma version originale (dans la question elle-même). Oohhh peut-être cela dépend de l'accélération matérielle: je n'ai probablement pas d'accélération matérielle, car j'ai un ordinateur portable avec une puce graphique médiocre ...
Basj
1
@Basj ajoute un wrapper <div style="position:relative;min-height: 900px;">Your's divs</div>jsFiddle ainsi fait
Alfonso Rubalcava
1
@AlfonsoRubalcava Oh ok ... Ceci explique pourquoi le jsfiddle est fluide et le lien direct pas fluide (Chrome): gget.it/1owxq8kr/big-div-test_transform.html ! Merci! Ainsi, l'amélioration des performances vient du position:relativeprobablement, ce qui est similaire à la réponse de
geert3
22
  1. Répondez à la première question "pourquoi". L'un des problèmes est la taille de la police . vous avez une taille de police 600000px, la plupart des navigateurs la verront comme trop élevée et seront rendues plus petites, tandis que Chrome essaie de rendre la taille d'origine. On dirait que le chrome ne peut pas repeindre de si grosses lettres avec les styles demandés très rapidement.

Mais en combinant les réponses Teemu et geert3 - en utilisant la transformation et la position: fixed, Chrome fonctionne beaucoup plus rapidement, même avec de grandes polices.

  1. Réponse à la 2ème question: "Y a-t-il un moyen ... de ne pas essayer de rendre la page entière" - vous pouvez essayer d'appliquer l'action de la souris pour les éléments dans le conteneur, pas pour le conteneur entier.

Tailles de police maximales: http://jsfiddle.net/74w7yL0a/

firefox 34 - 2 000 px
chrome 39 - 1 000 000 px
safari 8 - 1 000 000 px
ie 8-11 - 1 431 700 px
ViliusL
la source
6
C'est vrai, en fait. L'OP pose deux questions, dont la première est répondue: "pourquoi FF, IE le gère-t-il correctement et non Chrome?"
Hans Roerdinkholder
Intéressant. Avez-vous quelques éléments en tête qui montrent que Firefox s'arrête à 10k et que Chrome essaie de rendre la taille d'origine? (Ce serait intéressant pour référence future). Merci d'avance @ViliusL!
Basj
1
@Basj a ajouté des tailles de police maximales pour chaque navigateur (testé aujourd'hui), et c'est 2k pour Firefox.
ViliusL
Merci beaucoup @ViliusL! En effet, FF limite le font-sizeet cela pourrait être la raison de l'absence de lenteur sur FF. Mais alors ça aurait dû être très lent sur IE aussi, mais ce n'est pas ... Etrange!
Basj
4

En plus de la réponse de Teemu concernant l'utilisation de translate:

container.style.transform = 'translate(' + container.x + 'px, ' + container.y + 'px)';

Ce que vous devez également utiliser d'autres préfixes de fournisseur, vous pouvez simplement résoudre ce problème en utilisant ceci sur le corps:

height: 100%;
width: 100%;
position: relative;
overflow: hidden;

et ceci sur html:

height: 100%;

cela désactivera cependant le défilement. Donc, ce que je ferais, c'est ajouter un mousedownévénement au corps et appliquer ces styles en utilisant une classe css chaque fois qu'il mousedownest déclenché, et en supprimant cette classe mouseup.

Prisonnier
la source
J'ai essayé d'ajouter ce que vous avez mentionné ici: gget.it/0ufheqmt/big-div-test_prisonersolution.html , c'est ce que vous vouliez dire? Ici, le glissement est encore lent avec Chrome. Pareil pour toi? (PS: je n'ai pas compris: vous suggérez de faire ces modifications CSS au lieu d'utiliser style.transformou d' utiliser transform?). Merci au passage pour votre réponse @Prisoner!
Basj
2

La réponse de @Teemus fait presque tout.

Utilisez transform avectranslate3d au lieu de top/left.

translate3d permet l'accélération matérielle.

container.style.transform = 'translate3d(' + container.x + 'px, ' + container.y + 'px, 0)';

Une démo en direct sur jsFiddle .

Koen.
la source
1

J'ai analysé cela et j'ai trouvé que le problème d'origine était lié à l'architecture d'affichage de Chrome et à son utilisation de fils d'arrière-plan pour rendre la page.

Si vous voulez avoir un rendu rapide, allez dans chrome: flags, faites défiler jusqu'au paramètre Peinture côté impl, et définissez "Désactivé", puis redémarrez le navigateur - la souris sera supprimée.

Ce que j'ai trouvé, c'est que si vous activez le compteur FPS, le FPS signalé dans ce scénario est toujours très élevé, même si les performances réelles à l'écran sont très faibles. Mon explication provisoire (n'étant pas un expert en architecture d'affichage Chrome) est que si le thread d'interface utilisateur et l'affichage sont sur des threads séparés, alors il peut y avoir conflit dans le rendu de la div - dans le cas où le thread d'interface utilisateur et le thread de rendu sont sur le même thread, le thread d'interface utilisateur ne peut pas envoyer de messages plus rapidement que le thread d'interface utilisateur ne peut le rendre.

Je suggérerais que cela soit classé comme un bogue Chrome.

Théodore Ferro
la source
1

Utilisez display: tableet table-layout:fixedsur le div, ou une table enveloppant le div. En HTML:

Le modèle de table HTML a été conçu de telle sorte qu'avec l'aide de l'auteur, les agents utilisateurs puissent rendre les tables de manière incrémentielle (c'est-à-dire à mesure que les lignes de table arrivent) plutôt que d'avoir à attendre toutes les données avant de commencer le rendu.

Pour qu'un agent utilisateur formate une table en une seule passe, les auteurs doivent dire à l'agent utilisateur:

Le nombre de colonnes du tableau. Veuillez consulter la section sur le calcul du nombre de colonnes dans un tableau pour savoir comment fournir ces informations. Les largeurs de ces colonnes. Veuillez consulter la section sur le calcul de la largeur des colonnes pour savoir comment fournir ces informations.

Plus précisément, un agent utilisateur peut rendre une table en une seule passe lorsque les largeurs de colonne sont spécifiées à l'aide d'une combinaison d'éléments COLGROUP et COL. Si l'une des colonnes est spécifiée en termes relatifs ou en pourcentage (voir la section sur le calcul de la largeur des colonnes), les auteurs doivent également spécifier la largeur du tableau lui-même.

Pour un affichage incrémentiel, le navigateur a besoin du nombre de colonnes et de leurs largeurs. La largeur par défaut du tableau est la taille actuelle de la fenêtre (width = "100%"). Cela peut être modifié en définissant l'attribut width de l'élément TABLE. Par défaut, toutes les colonnes ont la même largeur, mais vous pouvez spécifier des largeurs de colonne avec un ou plusieurs éléments COL avant le début des données de la table.

Le problème restant est le nombre de colonnes. Certaines personnes ont suggéré d'attendre la réception de la première ligne du tableau, mais cela peut prendre beaucoup de temps si les cellules contiennent beaucoup de contenu. Dans l'ensemble, il est plus logique, lorsqu'un affichage incrémentiel est souhaité, d'amener les auteurs à spécifier explicitement le nombre de colonnes dans l'élément TABLE.

Les auteurs ont toujours besoin d'un moyen d'indiquer aux agents utilisateurs s'ils doivent utiliser l'affichage incrémentiel ou dimensionner automatiquement le tableau pour l'adapter au contenu des cellules. Dans le mode de dimensionnement automatique à deux passes, le nombre de colonnes est déterminé par la première passe. En mode incrémental, le nombre de colonnes doit être indiqué à l'avance (avec des éléments COL ou COLGROUP).

et CSS:

17.5.2.1 Disposition de table fixe

Avec cet algorithme (rapide), la disposition horizontale du tableau ne dépend pas du contenu des cellules; cela ne dépend que de la largeur du tableau, de la largeur des colonnes et des bordures ou de l'espacement des cellules.

La largeur de la table peut être spécifiée explicitement avec la propriété 'width'. Une valeur de «auto» (pour «display: table» et «display: inline-table») signifie utiliser l'algorithme de mise en page automatique des tableaux. Cependant, si la table est une table de niveau bloc ('display: table') dans un flux normal, un UA peut (mais n'est pas obligé) utiliser l'algorithme de 10.3.3 pour calculer une largeur et appliquer une disposition de table fixe même si la largeur spécifiée est «auto».

Références

Paul Sweatte
la source