Um div muito, muito, muito grande

109

Para um projeto meu (ver BigPictu.re ou bigpicture.js projeto GitHub ), eu tenho que lidar com potencialmente um, muito, muito grande muito <div>recipiente.

Eu sabia que havia um risco de desempenho ruim com a abordagem simples que uso, mas não esperava que estivesse presente principalmente com ... apenas o Chrome!

Se você testar esta pequena página (veja o código abaixo), a panorâmica (clique e arraste) será:

  • Normal / suave no Firefox
  • Normal / suave mesmo no Internet Explorer
  • Muito lento (quase travando) no Chrome!

Claro, eu poderia adicionar algum código (em meu projeto) para fazer isso quando você aumentar muito o zoom, o texto com tamanho de fonte potencialmente muito grande ficará oculto. Mas ainda assim, por que o Firefox e o Internet Explorer lidam com isso corretamente e não com o Chrome?

Existe uma maneira em JavaScript, HTML ou CSS de dizer ao navegador para não tentar renderizar a página inteira (que tem 10000 pixels de largura aqui) para cada ação? (renderizar apenas a janela de visualização atual!)


<!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
fonte
54
[OT] Tamanho da fonte de 600 K. Deve ser um recurso de acessibilidade para pessoas com visão muito ruim? ;-)
geert3
61
@ geert3 Tenho certeza de que é para um navegador da web em órbita lunar
David Wilkins
3
Sua demonstração está tranquila no Chrome 41.0.2236.0 dev-m
Pier-Luc Gendreau
11
Estou em canário (41.0.2241.0 canário) e ainda estou sentindo atraso. Vocês deveriam experimentá-lo em um laptop em vez de um equipamento de jogos, vocês verão
markasoftware
3
Ao contrário da crença popular, o IE é realmente mais rápido que o Chrome para renderizar a maioria das páginas. Seu mecanismo de javascript é um pouco mais lento.
Falanwe

Respostas:

62

Mudar para position: fixedparece acelerar as coisas.

geert3
fonte
27
Não é uma resposta direta à sua pergunta, mas é uma solução potencial para seu problema inicial (resposta lenta no Chrome). Pensar fora da caixa deve ser encorajado, IMHO.
geert3
Aqui, parece funcionar perfeitamente: gget.it/e0ubdh67/big-div-test_fixed.html . Você sabe por quê ? :)
Basj
2
Eu só posso adivinhar. fixedé obviamente menos complexo de definir e talvez eles possam fazer mais otimizações. Mas eu não olhei para o código-fonte do mecanismo de renderização, se você quer dizer isso ;-)
geert3
1
Esta resposta, e a fornecida pela ViliusL, têm o mesmo comentário "deixe um comentário", por pessoas diferentes. Quão legal é isso?
Joe
2
@Joe estas são de um conjunto de respostas padrão fornecidas por StackOverflow para uso por moderadores. Alguns precisam moderar mais moderadamente.
geert3
42

Use em transformvez de top/left:

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

Uma demonstração ao vivo em jsFiddle .

Teemu
fonte
2
Uma coisa muito estranha : no seu jsFiddle, é realmente rápido com o Chrome. Fiz exatamente a modificação que você propõe em meu código original aqui: gget.it/1owxq8kr/big-div-test_transform.html , e este último link é lento no Chrome :( Como isso é possível? Parece o mesmo que seu jsFiddle ! [Observação: milagrosamente, as respostas de geert3 parecem funcionar, não sei por que, mas funciona: gget.it/e0ubdh67/big-div-test_fixed.html]
Basj
@Basj Talvez seja dependente da versão, meu Chrome (39.0.2171.71 m) faz uma panorâmica da página vinculada em seu comentário tão suave e rápido quanto FF. De qualquer forma, definir a posição como fixedtira o elemento do fluxo de texto e economiza muita re-renderização. Na documentação do transformMDN diz: "... um contexto de empilhamento será criado. Nesse caso o objeto atuará como um bloco de contenção para a posição: elementos fixos que ele contém."
Teemu
2
Estranho, eu também tenho o Chrome 39.0.2171.71 m ... e gget.it/1owxq8kr/big-div-test_transform.html movimenta lentamente, tão lento quanto minha versão original (na própria pergunta). Oohhh possivelmente depende da aceleração de hardware: provavelmente não tenho aceleração de hardware, porque tenho um laptop com um chip gráfico ruim ...
Basj
1
@Basj adiciona um invólucro <div style="position:relative;min-height: 900px;">Your's divs</div>jsFiddle também
Alfonso Rubalcava
1
@AlfonsoRubalcava Oh ok ... Isso explica porque o jsfiddle é bom e o link direto não (Chrome): gget.it/1owxq8kr/big-div-test_transform.html ! Obrigado! Portanto, a melhoria no desempenho vem da position:relativeprobabilidade, que é semelhante à resposta de
geert3
22
  1. Responda à primeira pergunta "por quê". Um dos problemas é o tamanho da fonte . você tem o tamanho da fonte 600000px, a maioria dos navegadores a verá como muito alta e ficará menor, enquanto o Chrome tenta renderizar o tamanho original. Parece que o cromo não pode repintar essas letras grandes com os estilos solicitados muito rapidamente.

Mas combinar as respostas Teemu e geert3 - usando transform e position: fixed, faz com que o Chrome funcione muito mais rápido, mesmo com fontes grandes.

  1. Resposta para a segunda pergunta: "Existe uma maneira ... de não tentar renderizar a página inteira" - você pode tentar aplicar a ação do mouse para elementos no contêiner, não para o contêiner inteiro.

Tamanhos máximos de fonte: 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
fonte
6
Sim, na verdade. O OP faz duas perguntas, a primeira delas é respondida: "por que o FF, IE lida com isso corretamente e não o Chrome?"
Hans Roerdinkholder
Interessante. Você tem alguns elementos em mente que mostram que o Firefox para em 10k e que o Chrome tenta renderizar o tamanho original? (Seria interessante para referência futura). Agradecemos antecipadamente @ViliusL!
Basj
1
@Basj adicionou tamanhos máximos de fonte para cada navegador (testado hoje), e é 2k para o firefox.
ViliusL
Muito obrigado @ViliusL! Na verdade, o FF limita o font-sizee esta pode ser a razão para a não lentidão no FF. Mas deveria ter sido muito lento no IE também, mas não é ... Estranho!
Basj
4

Além da resposta de Teemu sobre o uso de traduzir:

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

Que você também deve usar prefixos de outros fornecedores. Você pode simplesmente corrigir isso usando no corpo:

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

e isso em html:

height: 100%;

isso, no entanto, desabilitará a rolagem. Então, o que eu faria é adicionar um mousedownevento ao corpo e aplicar esses estilos usando uma classe css sempre que mousedownfor acionada e removendo essa classe mouseup.

Prisioneiro
fonte
Tentei adicionar o que você mencionou aqui: gget.it/0ufheqmt/big-div-test_prisonersolution.html , é isso que você quis dizer? Aqui ainda é lento para arrastar com o Chrome. O mesmo para você? (PS: não entendi: você sugere fazer essas modificações CSS ao invés de usar style.transformou com usar transform?). A propósito, obrigado pela sua resposta @Prisoner!
Basj
2

A resposta de @Teemus quase faz tudo.

Use transform com emtranslate3d vez de top/left.

translate3d permite a aceleração de hardware.

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

Uma demonstração ao vivo em jsFiddle .

Koen.
fonte
1

Eu analisei isso e descobri que o problema original estava relacionado à arquitetura de exibição do Chrome e seu uso de threads de fundo para renderizar a página.

Se você quiser ter uma renderização rápida, vá para chrome: flags, vá até a configuração Impl-side painting, defina "Disabled" e reinicie o navegador - o mousemove será suave.

O que descobri é que se você ativar o contador de FPS, o FPS relatado neste cenário ainda é muito alto, embora o desempenho real na tela seja muito baixo. Minha explicação provisória (não sendo um especialista em arquitetura de exibição do Chrome) é que, se o thread da IU e a exibição estiverem em threads separados, pode haver contenção na renderização do div - no caso em que o thread da IU e o thread de renderização estão no mesmo thread, o UI thread não pode enviar mensagens mais rápido do que o UI thread pode processar.

Eu sugeriria que isso fosse arquivado como um bug do Chrome.

Theodore Ferro
fonte
1

Use display: tablee table-layout:fixedna div, ou uma mesa envolvendo a div. Em HTML:

O modelo de tabela HTML foi projetado para que, com a assistência do autor, os agentes do usuário possam renderizar tabelas de forma incremental (ou seja, conforme as linhas da tabela chegam) em vez de ter que esperar por todos os dados antes de começar a renderizar.

Para que um agente de usuário formate uma tabela em uma passagem, os autores devem dizer ao agente de usuário:

O número de colunas da tabela. Consulte a seção sobre como calcular o número de colunas em uma tabela para obter detalhes sobre como fornecer essas informações. As larguras dessas colunas. Consulte a seção sobre como calcular a largura das colunas para obter detalhes sobre como fornecer essas informações.

Mais precisamente, um agente do usuário pode renderizar uma tabela em uma única passagem quando as larguras das colunas são especificadas usando uma combinação de elementos COLGROUP e COL. Se qualquer uma das colunas for especificada em termos relativos ou percentuais (consulte a seção sobre como calcular a largura das colunas), os autores também devem especificar a largura da própria tabela.

Para exibição incremental, o navegador precisa do número de colunas e de suas larguras. A largura padrão da tabela é o tamanho da janela atual (largura = "100%"). Isso pode ser alterado definindo o atributo de largura do elemento TABLE. Por padrão, todas as colunas têm a mesma largura, mas você pode especificar as larguras das colunas com um ou mais elementos COL antes do início dos dados da tabela.

O problema restante é o número de colunas. Algumas pessoas sugeriram esperar até que a primeira linha da tabela fosse recebida, mas isso pode levar muito tempo se as células tiverem muito conteúdo. No geral, faz mais sentido, quando a exibição incremental é desejada, fazer com que os autores especifiquem explicitamente o número de colunas no elemento TABLE.

Os autores ainda precisam de uma maneira de dizer aos agentes do usuário se devem usar a exibição incremental ou dimensionar a tabela automaticamente para caber no conteúdo da célula. No modo de dimensionamento automático de duas passagens, o número de colunas é determinado pela primeira passagem. No modo incremental, o número de colunas deve ser declarado na frente (com elementos COL ou COLGROUP).

e CSS:

17.5.2.1 Layout de mesa fixo

Com este algoritmo (rápido), o layout horizontal da tabela não depende do conteúdo das células; depende apenas da largura da tabela, da largura das colunas e das bordas ou espaçamento das células.

A largura da tabela pode ser especificada explicitamente com a propriedade 'largura'. Um valor de 'auto' (para 'display: table' e 'display: inline-table') significa usar o algoritmo de layout de tabela automático. No entanto, se a tabela for uma tabela em nível de bloco ('display: table') em fluxo normal, um UA pode (mas não precisa) usar o algoritmo de 10.3.3 para calcular uma largura e aplicar um layout de tabela fixo, mesmo que a largura especificada é 'auto'.

Referências

Paul Sweatte
fonte