防止 DOM 滞后

Posted

技术标签:

【中文标题】防止 DOM 滞后【英文标题】:Prevent lagging in DOM 【发布时间】:2013-06-20 09:36:48 【问题描述】:

所以我正在编写一种绘图脚本,现在它工作正常(虽然代码仍然需要清理,需要更多功能),但是当绘制太多时,mousemove 非常滞后。这是主要的javascript

   $('#canvas').on('mousedown', function()
       going = !going;
       $(this).on('mousemove', function(e)
           if(cursor == 'paint' && going == true)
       $('.fall').each(function()
           if ($(this).css("opacity") == 0)
               $(this).remove();
           ;
       );
       var ps = $('#canvas').offset().top;
       var t = (e.pageY - ps - $('.fall').height()).toString() + 'px';
       var l = (e.pageX - $('.fall').width()).toString() + 'px';
       $('.fall').css("margin_left",l);
       $('.fall').css("margin_top",t);
               var doit = '<div class="fall" style="position:absolute;margin-left:' + l + ';margin-top:' + t + ';background-color:'+ color +';box-shadow: 0px 0px 5px ' + color + ';"></div>'
       $('#canvas').prepend(doit); 
           
           else if(cursor == 'erase')
           $('.fall').mouseenter(function()
               $(this).fadeOut('fast',function()
                   $(this).remove()
               );
           );
       ;
   );

本质上,当你点击绘图部分时,如果点击绘图按钮,你可以绘制:jsfiddle。

我的问题:

如果你画得太多,尤其是在开始和停止时,它不会在mousemove 上添加足够多的内容(我假设)DOM 被淹没了。

问题:

在不产生延迟的情况下向 DOM 添加许多 div 的有效方法是什么?这可能吗?

注意:

这是一个个人项目,我对使用以前创建的绘图 API 不感兴趣

【问题讨论】:

您正在为每个“mousedown”添加另一个“mousemove”处理程序。调用 .on() 不会删除任何以前的处理程序! hmmmm 我想这是真的!我将如何删除处理程序? 没有必要删除旧的处理程序;只需将该代码添加到另一个“mousemove”处理程序即可。此外,更新“光标”的代码非常昂贵。您应该尝试从事件对象中获取鼠标坐标,而不是询问 DOM。 另外:“mousemove”经常触发。 DOM 操作非常昂贵。 “.fall”还有另一个“无限处理程序”问题。 @Pointy 根据 chrome 检查器堆大小在几秒钟后超过 10mb。你认为mousemove是罪魁祸首吗?如果可以,可以做什么? 【参考方案1】:

您可以做很多事情来提高性能。

下面的代码是对问题中代码的大量重构。乍一看,它可能看起来效率较低,因为它的行数大约是原始行数的两倍。但是,行数不是这里的问题。有两个基本原则适用:

在 mousemove 处理程序中尽可能少地进行 DOM 交互,并在 mousedown 时尽可能多地进行交互。 包括一个“分频器电路”以限制调用 mousemove 处理程序的次数。这是通过在每次调用时分离 mousemove 事件处理程序并在短暂延迟后重新附加来实现的,条件是鼠标仍处于按下状态。

另见代码中的 cmets。

jQuery(function($) 
    ...
    var $canvas = $("#canvas");
    var data = 
        name: 'fall'//a unique string for namespacing the muousemove event.
    ;
    $canvas.on('mousedown', function() 
        going = !going;
        data.$fall = $('.fall');//this collection is created once per mousedown then managed inside mm to avoid unnecessary DOM interaction
        data.mousedown = true;
        data.colorCSS = 
            'background-color': color,
            'box-shadow': '0px 0px 5px ' + color
        ;
        data.fallWidth = data.$fall.width();
        data.fallHeight = data.$fall.height();
        attachMouseMoveHandler();
    ).on('mouseup', function() 
        data.mousedown = false;
    ).trigger('mouseup');

    function attachMouseMoveHandler() 
        if(data.mousedown);
            $canvas.on('mousemove.' + data.name, mm);//the event is namespaced so its handler can be removed without affecting other canvas functionality
    

    //The mousemove handler
    function mm(e) 
        if(going && cursor == 'paint') 
            data.$fall.each(function() 
                data.$fall = data.$fall.not(this);//manage data.$fall rather than re-form at every call of mm()
                var $this = $(this);
                if ($this.css("opacity") == 0) 
                    $this.remove();
                ;
            );
            data.$fall = data.$fall.add($('<div class="fall" />').css(data.colorCSS).prependTo($canvas)).css(
                'margin-left': (e.pageX - data.fallWidth) + 'px',
                'margin-top': (e.pageY - $canvas.offset().top - data.fallHeight) + 'px'
            );
        
        else if(cursor == 'erase') 
            data.$fall.mouseenter(function() 
                data.$fall = data.$fall.not(this);//manage data.$fall rather than re-form at every call of mm()
                var $this = $(this).fadeOut('fast', function() 
                    $this.remove();
                );
            );
        ;
        $canvas.off('mousemove.' + data.name);
        setTimeout(attachMouseMoveHandler, 50);//adjust delay up/down to optimise performance
    
);

仅针对语法进行测试

我不得不做出一些假设,主要是关于什么变成了鼠标按下时的固定数据。这些假设可能不正确,因此您很可能仍有一些工作要做,但只要您在上述整体框架内工作,您的性能问题很有可能会消失。

【讨论】:

+1,很好的解释和代码似乎很有前途,但是当我尝试将它实现到我的 jsfiddle 中时,它打破了小提琴......也许我放错了? 我刚刚对答案中的代码做了一个次要但可能重要的修改。 data.$fall.add 现在改为 data.$fall = data.$fall.add(...。如果这不能解决问题,那么我需要查看它以判断出了什么问题。 我链接了 jsfiddle,显示了问题中的所有内容......这是一个使用你的代码的 jsfiddle:jsfiddle.net/xyuBJ/1 ...当你比较它们时,你的滞后更少,但更糟糕的是除非您消除延迟,否则绘图...但这会增加延迟...这甚至可能吗? 对不起,我什至没有注意到小提琴链接。商定的性能很差。我想必须有专门的 html5/canvas 技术来优化这样的应用程序 - 我不知道的东西。只能建议您进行一些研究,也许可以找到一些类似应用程序的工作示例 - 看看它们是否工作得更好 - 如果是这样,那么你就知道目标是什么了。

以上是关于防止 DOM 滞后的主要内容,如果未能解决你的问题,请参考以下文章

当我用鼠标向下滚动时,如何防止 WordPress 网站滞后?

如何在 DOM 追加期间防止表格行高故障?

如何防止 iframe 在 DOM 中移动时重新加载

防止图像在非 DOM jQuery AJAX 解析上加载

HTML Javascript - 防止从 dom 树的子节点执行脚本

防止我的 Elm 应用程序在第 3 方更改 DOM 时崩溃