单个页面上有1000个DOM元素

Bas*_*asj 2 html javascript html5 dom html5-canvas

对于大"文本地图" BigPicture的项目,我需要有超过1000个文本输入. 单击+拖动时,可以"平移"显示的区域.

但性能非常差(在Firefox和Chrome上都有):渲染1000多个DOM元素并不快.

当然,另一个具有更好性能的解决方案是:处理a <canvas>,将文本渲染为位图,每次我们想要编辑文本时,让我们显示一个独特的DOM <textarea>,它消失了编辑完成,文本呈现为位图再次... 它的工作原理(我目前正朝着这个方向努力),但它需要更多的代码才能在画布上进行编辑.

问题:是否有可能提高在HTML页面上渲染1000多个DOM元素的性能,因此我根本不需要使用它<canvas>

或者在使用1000多个DOM元素平移页面时,不可能获得良好的性能?

在此输入图像描述


备注:

1)在我使用的演示中,<span contendteditable="true">因为我想要多行输入+自动调整大小,但渲染性能与标准相同<textarea>.*

2)作为参考,这是我创建1000个文本元素的方法.

for (i=0; i < 1000; i++)
{
  var blax = (Math.random()-0.5)*3000;
  var blay = (Math.random()-0.5)*3000;
  var tb = document.createElement('span');
  $(tb).data("x", blax / $(window).width());
  $(tb).data("y", blay / $(window).height());
  $(tb).data("size", 20 * currentzoom);
  tb.contentEditable = true;
  tb.style.fontFamily = 'arial';
  tb.style.fontSize = '20px';
  tb.style.position  = 'absolute';
  tb.style.top = blay + 'px';
  tb.style.left = blax + 'px';
  tb.innerHTML="newtext";
  document.body.appendChild(tb);
}
Run Code Online (Sandbox Code Playgroud)

Qui*_*nce 5

对于这样的事情你可以使用文档片段,这些是不属于实际DOM树的DOM节点(更多信息可以在这里找到https://developer.mozilla.org/en-US/docs/Web/ API/document.createDocumentFragment),因此您可以对片段进行所有设置,然后附加片段,该片段只会导致重新流而不是1000.

所以这是一个例子 - http://jsfiddle.net/leighking2/awzoz7bj/ - 快速检查运行时间大约需要60-70ms才能运行

var currentzoom = 1;
var docFragment = document.createDocumentFragment();
var start = new Date();
for (i=0; i < 1000; i++)
{
  var blax = (Math.random()-0.5)*3000;
  var blay = (Math.random()-0.5)*3000;
  var tb = document.createElement('span');
  $(tb).data("x", blax / $(window).width());
  $(tb).data("y", blay / $(window).height());
  $(tb).data("size", 20 * currentzoom);
  tb.contentEditable = true;
  tb.style.fontFamily = 'arial';
  tb.style.fontSize = '20px';
  tb.style.position  = 'absolute';
  tb.style.top = blay + 'px';
  tb.style.left = blax + 'px';
  tb.innerHTML="newtext";
  docFragment.appendChild(tb);
}

document.body.appendChild(docFragment);

var end = new Date();
console.log(end-start)
Run Code Online (Sandbox Code Playgroud)

相比原来花了大约645ms运行http://jsfiddle.net/leighking2/896pusex/

更新因此,为了再次提高拖动速度,请将个别编辑保留在DOM之外,以避免每次鼠标拖动1000次重排的成本

所以这是使用jquery的detach()方法的一种方法(例如http://jsfiddle.net/sf72ubdt/).这将删除DOM中的元素,但是将它们的所有属性提供给您,以便您可以操作它们并在以后重新插入它们

redraw = function(resize) {
    //detach spans
    var spans = $("span").detach();
    //now loop other them, because they are no longer attached to the DOM any changes are
    //not going to cause a reflow of the page
    $(spans).each(function(index) {
        var newx = Math.floor(($(this).data("x") - currentx) / currentzoom * $(window).width());
        var newy = Math.floor(($(this).data("y") - currenty) / currentzoom * $(window).height());

        if (resize) {
            displaysize = Math.floor($(this).data("size") / currentzoom);
            if (displaysize) {
                $(this).css({
                    fontSize: displaysize
                });
                $(this).show();
            } else
                $(this).hide();
        }
        //changed this from offset as I was getting a weird dispersing effect around the mouse
        // also can no longer test for visible but i assume you want to move them all anyway.
        $(this).css({
            top: newy + 'px',
            left: newx + 'px'
        });
    });
    //reattach to the body
    $("body").append(spans);

};
Run Code Online (Sandbox Code Playgroud)

更新2 -

因此,为了获得更多的性能,您可以缓存窗口宽度和高度,使用vanilla for循环,使用vanilla js来更改跨度的css.现在每次重绘(在chrome上)需要大约30-45毫秒(http://jsfiddle.net/leighking2/orpupsge/),相比我上面的更新,它在80-100ms左右看到它们(http://jsfiddle.net/leighking2/b68r2xeu /)

所以这里是更新的重绘

redraw = function (resize) {
    var spans = $("span").detach();
    var width = $(window).width();
    var height = $(window).height();

    for (var i = spans.length; i--;) {
        var span = $(spans[i]);
        var newx = Math.floor((span.data("x") - currentx) / currentzoom * width);
        var newy = Math.floor((span.data("y") - currenty) / currentzoom * height);
        if (resize) {
            displaysize = Math.floor(span.data("size") / currentzoom);
            if (displaysize) {
                span.css({
                    fontSize: displaysize
                });
                span.show();
            } else span.hide();
        }


        spans[i].style.top = newy + 'px',
        spans[i].style.left = newx + 'px'
    }

    $("body").append(spans);
};
Run Code Online (Sandbox Code Playgroud)

SNIPPET示例 -

var currentzoom = 1;
var docFragment = document.createDocumentFragment();
var start = new Date();
var positions = []
var end = new Date();
console.log(end - start);

var currentx = 0.0,
currenty = 0.0,
currentzoom = 1.0,
xold = 0,
yold = 0,
button = false;

for (i = 0; i < 1000; i++) {
var blax = (Math.random() - 0.5) * 3000;
var blay = (Math.random() - 0.5) * 3000;

var tb = document.createElement('span');
$(tb).data("x", blax / $(window).width());
$(tb).data("y", blay / $(window).height());
$(tb).data("size", 20 * currentzoom);
tb.contentEditable = true;
tb.style.fontFamily = 'arial';
tb.style.fontSize = '20px';
tb.style.position = 'absolute';
tb.style.top = blay + 'px';
tb.style.left = blax + 'px';
tb.innerHTML = "newtext";
docFragment.appendChild(tb);
}
document.body.appendChild(docFragment);

document.body.onclick = function (e) {
if (e.target.nodeName == 'SPAN') {
    return;
}
var tb = document.createElement('span');

$(tb).data("x", currentx + e.clientX / $(window).width() * currentzoom);
$(tb).data("y", currenty + e.clientY / $(window).height() * currentzoom);
$(tb).data("size", 20 * currentzoom);
tb.contentEditable = true;
tb.style.fontFamily = 'arial';
tb.style.fontSize = '20px';
tb.style.backgroundColor = 'transparent';
tb.style.position = 'absolute';
tb.style.top = e.clientY + 'px';
tb.style.left = e.clientX + 'px';
document.body.appendChild(tb);
tb.focus();
};

document.body.onmousedown = function (e) {
button = true;
xold = e.clientX;
yold = e.clientY;
};

document.body.onmouseup = function (e) {
button = false;
};

redraw = function (resize) {
var start = new Date();
var spans = $("span").detach();
var width = $(window).width();
var height = $(window).height();

for (var i = spans.length; i--;) {
    var span = $(spans[i]);
    var newx = Math.floor((span.data("x") - currentx) / currentzoom * width);
    var newy = Math.floor((span.data("y") - currenty) / currentzoom * height);
    if (resize) {
        displaysize = Math.floor(span.data("size") / currentzoom);
        if (displaysize) {
            span.css({
                fontSize: displaysize
            });
            span.show();
        } else span.hide();
    }


    spans[i].style.top = newy + 'px',
    spans[i].style.left = newx + 'px'
}

$("body").append(spans);
var end = new Date();
console.log(end - start);
};

document.body.onmousemove = function (e) {
if (button) {
    currentx += (xold - e.clientX) / $(window).width() * currentzoom;
    currenty += (yold - e.clientY) / $(window).height() * currentzoom;

    xold = e.clientX;
    yold = e.clientY;

    redraw(false);
}
};

$(function () {
$('body').on('mousedown', 'span', function (event) {
    if (event.which == 3) {
        $(this).remove()
    }
})
});

zoomcoef = function (coef) {
middlex = currentx + currentzoom / 2
middley = currenty + currentzoom / 2
currentzoom *= coef
currentx = middlex - currentzoom / 2
currenty = middley - currentzoom / 2
redraw(true)
}

window.onkeydown = function (event) {
if (event.ctrlKey && event.keyCode == 61) {
    zoomcoef(1 / 1.732);
    event.preventDefault();
}
if (event.ctrlKey && event.keyCode == 169) {
    zoomcoef(1.732);
    event.preventDefault();
}
if (event.ctrlKey && event.keyCode == 48) {
    zoomonwidget(1 / 1.732);
    event.preventDefault();
}
};
Run Code Online (Sandbox Code Playgroud)
  html, body {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
  }
Run Code Online (Sandbox Code Playgroud)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
Run Code Online (Sandbox Code Playgroud)