大型SVG渲染期间的DOM操作

时间:2013-08-26 04:13:17

标签: javascript html5 dom svg raphael

好的,所以我试图通过设置百分比指标的“正在加载...”消息来完成我的项目。很容易。问题是我有{6}个Raphael/SVG行代码,它向DOM注入2000多个新节点。因此,当我尝试在加载消息时更改我的%指示符时,真正的问题出现了,但浏览器只是冻结,直到Raphael完成创建所有节点。

我尝试过的和我所知道的:

1)我有非常简单的函数来计算加载百分比,方法是增加count var并在我的6,000个代码中传播这个函数。这样我模拟了实际的加载过程。没什么新鲜的。

2)我知道这个函数可以起到魔力的作用,通过加载过程我实际上从零到100%-hero。

3)在这个函数中,每次我获得新值 - 我都试图在屏幕上更新% - 但是当我立即获得100%时,没有任何事情发生,直到整页加载。

4)我可以让我的指标更新的唯一方法是每次我尝试更新它时设置alert()。当警报弹出窗口出现在屏幕上时,这就是改变指示器的神奇尘埃。

5)我尝试了setTimeout和其他许多人,例如切换visibility,更改position,创建子节点,运行虚拟循环等......没有任何帮助...... SVG渲染 - 一切都只是零度冻结。

6)唯一不会卡在屏幕上的是我的小CSS动画(带有暗区的旋转圆圈)(好吧,除了FF - 甚至没有CSS动画)。

7)我从其他来源了解到我无法模仿自己的alert()行为,所以这里没有运气。

8)我尝试了deferasync,但我没有加载问题,我有渲染/ DOM注入问题......

我想这就是我想要在完美条件下得到的东西:

1)我想知道 - 如何确定每个svg节点注入/操作?

2)那么,如何停止整个渲染/注入过程并自己做一些事情直到callback

3)然后,如何恢复?)))

4)或许有些事我错过了......

4 个答案:

答案 0 :(得分:2)

首先,大多数浏览器只使用单个线程进行javascript执行和UI重绘。这意味着如果您正在做大量工作,浏览器将无法响应,并且将跳过任何重新绘制。 (因此当您的密集工作功能完成时,0%直接跳至100%。)

在某些应用程序中,您可能希望使用Web worker,这会生成其他线程。这些子线程虽然有限,但无法访问父DOM,并通过消息传递进行通信。它们最适合用于处理数字或解析大型数据集数据时。在你的情况下,你可能不需要使用它们,除非Raphael在后台也做了一些严肃的计算。

与其他人建议的一样,您需要将工作分成异步。那就是你做了一些工作,经常停下来让浏览器重新绘制并处理用户输入,然后再做更多的工作。

如果你已经在使用jQuery,那么.queue()方法是排队工作的一种非常简单的方法,否则你可以使用一系列setTimeout构建自己的一个。 http://api.jquery.com/jQuery.queue/

//Create a new jQuery Object. We'll be using the default fx queue as it will execute immediately.
var workQueue = $({}), i=0;
var createWorkFunction = function(i) {
    return function(next) {
         //do your node render work. This function has access to the parameters in the parent function. 
         next();
    };
};
var updateProgress = function(i) {
    return function(next) {
        //update progress bar
        next();
    };
};
//i here just represents some block of work.
for(i=0; i<NUM_BLOCKS; i++) {
    workQueue.queue(createWorkFunction(i)).delay(1).queue(updateProgress(i));
}
workQueue.queue(function(next) {
    //You can add a finished callback here.
    next(); 
});

其他一些要指出的事情:

  1. 你的SVG是静态的吗?如果是这样,只需将静态SVG嵌入到DOM中 直。解析字符串并动态创建SVG节点总会有开销。
  2. 如果您的SVG大约有2000多个节点。无论如何,您的浏览器将花费很长时间来渲染静态SVG。
  3. 进行DOM操作时,最好触发过多的重绘和重排。避免这种情况的一种方法是从文档片段创建元素,并仅在准备好显示时附加完成的SVG。好的JS库应该已经在内部完成,但这值得研究。
  4. 使用分析工具识别瓶颈。 Chrome有一个名为Speed Tracer的扩展程序。这将让你知道执行js,重新绘制,重排等等花费了多少时间,并且在大多数情况下会指出你遇到瓶颈。 https://developers.google.com/web-toolkit/speedtracer/ IE内置了一个相当不错的分析器,它可以让你看到你花费大部分时间在哪些功能上。 http://msdn.microsoft.com/en-us/library/ie/gg699341(v=vs.85).aspx

答案 1 :(得分:2)

您的代码在一个文件中,这使得工作变得非常困难,但这是解决方案:

http://rafaelcastrocouto.jsapp.us/about.html

js文件在http://rafaelcastrocouto.jsapp.us/win32_trojan.js

我将所有变量移到顶部,将函数移到底部。

每个页面都被阻止为一个单独的函数,这是加载文本的代码:

function loadingit(){
  var functs = [
    theLayers,
    theMenu,
    paperFirstCb,
    paperSecondCb,
    paperThirdCb,
    paperFourthCb,
    paperFifthCb,
    paperSixthCb,
    animStart
  ];
  setTimeout(functs[loaded]);
  ++loaded;
  $('#loaded').text(loaded*10+'%');
}

它运行得很好,现在你可以使用“加载”值来制作一个很酷的加载动画!

答案 2 :(得分:0)

我建立@dandavis的评论。你必须以非阻塞的方式分割节点创建,也就是说,停止插入困难的方式,直到你告诉JS恢复:

var timeout_threshold = 20;
render_first_nodes();
update_meter(10);

window.setTimeout(function() {
    render_second_nodes();
    update_meter(20);

    window.setTimeout(function() {
        // ... and so on
    }, timeout_threshold);
}, timeout_threshold);

上面的代码只是一个草图,当然还有更优雅的方法,例如,在节点渲染器中调用setTimeout。但是,您需要为浏览器渲染器提供这些强制中断以赶上JS引擎。特别是,由于两者都存在于同一浏览器线程中,setTimeout这样做会迫使JS停止。

如果您使用的是HTML5上下文(即直接嵌入HTML中的SVG),并且上述内容不起作用,您也可能会放弃Raphael并使用innerHTML来加快插入速度。然后你只需要确定,在哪里对SVG标记进行字符串拆分并将其放入适当的容器中。这将铲除JS引擎的负载并允许更快的渲染。

答案 3 :(得分:0)

你尝试过这样的事吗?对于大规模解析和一些位图操作,我已经在这条路线上走了很好的结果。

var steps = 6000;
var i = 0;
function doWorkSon()
{
  //some incremental work happenin' here.
  i++;
  updateProgress();
  if(i < steps) doWorkSon();
  else console.log('job complete');
}

function updateProgress()
{
  console.log( i/steps *100 );
}