一个非常,非常,非常大的 Div

对于我的一个项目(见 大画报Bigpicure.js GitHub 项目) ,我必须处理潜在的非常,非常,非常大的 <div>容器。

我知道我所使用的简单方法存在性能不佳的风险,但是我并不期望它只出现在 Chrome 上!

如果您测试 这一小页(见下面的代码) ,平移(单击 + 拖动)将是:

  • Firefox 上的正常/平滑
  • 即使在 Internet Explorer 上也是正常/平滑的
  • 非常缓慢(几乎崩溃)的 Chrome!

当然,我可以添加一些代码(在我的项目中)来做到这一点,当你放大很多,字体可能非常非常大的文本将被隐藏。但还是 为什么火狐和 Internet Explorer 能正确处理它,而 Chrome 却不能?

是否有一种方法在 JavaScript,HTML,或 CSS 告诉浏览器 不去尝试呈现整个页面(这是10000像素宽在这里)的每一个动作? (只呈现当前视图!)


<!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>
12899 次浏览

Use transform instead of top/left:

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

A live demo at jsFiddle.

Changing to position: fixed seems to speed things up.

In addition to Teemu's answer of using translate:

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

Which you should also use other vendor prefixes, You can simply fix this by using this on the body:

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

and this on html:

height: 100%;

this will, however, disable scrolling. So what I'd do is, add a mousedown event to the body and apply those styles using a css class whenever mousedown is triggered, and removing that class on mouseup.

  1. Answer to first quest "why". One of problems are font size. you have font size 600000px, most browser will see it as too high and render smaller, while chrome tries to render original size. Looks like chrome can not repaint such big letters with your requested styles very fast.

But combining Teemu and geert3 answers - using transform and position:fixed, makes chrome works much more faster even with big fonts.

  1. Answer to 2nd question: "Is there a way ... not to try to render the whole page" - you can try to apply mouse action for elements in container, not for whole container.

Maximum font sizes: 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

@Teemus' answer almost does it all.

Use transform with translate3d instead of top/left.

translate3d enables hardware acceleration.

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

A live demo at jsFiddle.

I analyzed this and I found that the original problem related to the Chrome display architecture, and its use of background threads to render the page.

If you want to have fast rendering, go into chrome:flags, scroll to the setting Impl-side painting, and set "Disabled", then restart the browser - the mousemove will be smooth.

What I found is that if you enable the FPS counter, the reported FPS in this scenario is still very high, even though the actual onscreen performance is very low. My tentative explanation (not being a Chrome display architecture expert) is that if the UI thread and display are on separate threads, then there can be contention in the rendering of the div - in the case where the UI thread and rendering thread is on the same thread, the UI thread cannot send messages faster than the UI thread can render.

I would suggest that this should be filed as a Chrome bug.

Use display: table and table-layout:fixed on the div, or a table wrapping the div. In HTML:

The HTML table model has been designed so that, with author assistance, user agents may render tables incrementally (i.e., as table rows arrive) rather than having to wait for all the data before beginning to render.

In order for a user agent to format a table in one pass, authors must tell the user agent:

The number of columns in the table. Please consult the section on calculating the number of columns in a table for details on how to supply this information. The widths of these columns. Please consult the section on calculating the width of columns for details on how to supply this information.

More precisely, a user agent may render a table in a single pass when the column widths are specified using a combination of COLGROUP and COL elements. If any of the columns are specified in relative or percentage terms (see the section on calculating the width of columns), authors must also specify the width of the table itself.

For incremental display, the browser needs the number of columns and their widths. The default width of the table is the current window size (width="100%"). This can be altered by setting the width attribute of the TABLE element. By default, all columns have the same width, but you can specify column widths with one or more COL elements before the table data starts.

The remaining issue is the number of columns. Some people have suggested waiting until the first row of the table has been received, but this could take a long time if the cells have a lot of content. On the whole it makes more sense, when incremental display is desired, to get authors to explicitly specify the number of columns in the TABLE element.

Authors still need a way of telling user agents whether to use incremental display or to size the table automatically to fit the cell contents. In the two pass auto-sizing mode, the number of columns is determined by the first pass. In the incremental mode, the number of columns must be stated up front (with COL or COLGROUP elements).

and CSS:

17.5.2.1 Fixed table layout

With this (fast) algorithm, the horizontal layout of the table does not depend on the contents of the cells; it only depends on the table's width, the width of the columns, and borders or cell spacing.

The table's width may be specified explicitly with the 'width' property. A value of 'auto' (for both 'display: table' and 'display: inline-table') means use the automatic table layout algorithm. However, if the table is a block-level table ('display: table') in normal flow, a UA may (but does not have to) use the algorithm of 10.3.3 to compute a width and apply fixed table layout even if the specified width is 'auto'.

References