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)
对于这样的事情你可以使用文档片段,这些是不属于实际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)