2

我正在尝试将全屏背景图像加载到画布上,然后对其应用图像过滤器,但有几个问题。我遇到了严重的性能问题,但仅在调整窗口大小时。基本上,我的代码试图保持画布/图像与屏幕尺寸匹配(效果很好)。

更新画布方法:

updateCanvas 方法在加载时调用以初始创建/加载图像对象并将其放置在画布上。如果选择了过滤器,它会调用过滤器方法。它接受 onResize 参数,该参数在窗口调整大小时传递以缩放画布/图像。MAIN 是指用于引用元素的对象。

updateCanvas:function(onResize){ 
        // SETUP VARIABLES
    var img=new Image(), 
            $this=Main.currentOBJ, // REFERENCE FOR .DATA
            objData=$this.data, 
            canvas=Main.OBJ.$Canvas[0], 
        ctx=canvas.getContext("2d"), 
            winW=$(window).width(), winH=$(window).height();

   // SOURCE CAN BE SET IN .DATA OR DEFAULT
   img.src=(objData.bg_pic_src!=='') ? objData.bg_pic_src : Main.ConSRC;

   // LOAD THE IMAGE OBJECT
   img.onload=function(){ 
       var imgW=img.width, imgH=img.height, 
           ratio=imgW/imgH, newW=winW, newH=Math.round(newW/ratio);

       // SETUP IMAGE PROPORTIONS
       if(newH < winH){ var newH=winH, newW=Math.round(newH*ratio); }; 

       // WHEN RESIZING THE BROWSER 
       if(!onResize){ // INTIAL DRAW
          Main.OBJ.$Canvas.attr({'width':newW+'px', 'height':newH+'px'});  
          ctx.drawImage(img,0,0,newW,newH);

          // APPLY FILTERS
      if(objData.bg_pic_filter > 0){ 
             Main.canvasFilter(ctx,newW,newH); // FILTER METHOD
      }else{ 
             Main.OBJ.$OverlayPic.animate({opacity:parseFloat(objData.bg_pic_opacity,10)},
        {duration:objData.bg_pic_speed_in,queue:false}); 
          };
    }else{ // RESIZING
         Main.OBJ.$Canvas.attr({'width':newW+'px', 'height':newH+'px'});  
         ctx.drawImage(img,0,0,newW,newH);

         if(objData.bg_pic_filter > 0){ 
             Main.canvasFilter(ctx,newW,newH); // FILTER METHOD
         };     
    };
};

Canvas Filter 方法: 如果要对背景图像应用图像过滤器,则从 canvasUpdate 方法调用 canvasFilter 方法。

canvasFilter:function(ctx,width,height){ 
      // SETUP VARIABLES
      var objData=Main.currentOBJ.data, 
      canvasWidth=width, canvasHeight=height,
      imgdata=ctx.getImageData(0, 0, canvasWidth, canvasHeight), 
      pix=imgdata.data, l=pix.length;

     // APPLY THE CORRECT LOOP DEPENDING UPON THE FILTER NUMBER 
     switch(objData.bg_pic_filter){
     case 1: ... break;
     case 2: 
         for(var i=l; i>0; i-=4){ 
             var cacher=pix[i+2]; 
             pix[i]=pix[i+1]; 
             pix[i+1]=cacher; 
             pix[i+2]=cacher; 
             pix[i+3]=cacher;
         }; 
     break;
    };          

     // APPLY THE FILER & FADE IN THE BACKGROUND
     ctx.putImageData(imgdata, 0, 0);
     Main.OBJ.$OverlayPic.fadeTo(parseInt(objData.bg_pic_speed_in,10),    
         parseFloat(objData.bg_pic_opacity,10));
};

所有这些都有效,并且初始绘制/过滤器非常快。但是,当我调整窗口大小时,它非常缓慢。我通过安装一个节流器暂时解决了这个问题,它只是设置了一个计时器以避免过快地触发上述功能。这有一点帮助,但会保持背景图像在适当的位置(在图像周围显示开放的纯色背景区域),直到节流计时器启动并触发方法 - 调整画布/图像的大小并应用过滤器。

问题:

  1. 有更快的方法吗?我已经阅读了有关使用 Typed Arrays/Bitwise 进行像素操作循环的信息,但似乎对此的支持很糟糕。我也研究了 CSS3 过滤器来完成同样的事情,但同样,支持是可怕的。那么,这将是具有“现代浏览器”兼容性的全屏背景图像过滤器的唯一方法吗?

  2. updateCanvas 方法每次都会创建一个新的图像对象,我认为这可能是一个瓶颈,但我不确定,也不确定如何解决它?

  3. 我读过其他人使用单独的画布作为缓冲区?在我的情况下,这实际上会提高性能吗?

  4. 我真的认为我的代码中有一个主要的逻辑问题。对我来说,我需要多次循环遍历所有像素信息是没有意义的,但这是我让它工作的唯一方法。有没有办法我可以绘制它/应用过滤器一次,然后在调整大小时,只需调整尺寸而无需重绘/重新循环/重新应用过滤器?我认为它会保留过滤后的图像,但如果我在窗口调整大小时删除对 canvasFilter 方法的调用,它会完全删除过滤器效果。

  5. 我还没有使用过记忆技术,因为我从来没有完全理解它,也从来没有完全理解在哪里/何时使用它。但是,本质上,如果我有一个函数一次又一次地返回相同的结果(例如 canvasFilter 方法),我可以记忆以提高性能 - 还是会因为它在窗口调整大小后拉取不同的像素信息而有所不同?

  6. 我还听说 webGL 渲染可能对此有所帮助?因为我对 webGL 一无所知,所以可能会很遥远。

很抱歉这个 loooonng 问题,但希望这涵盖了一些其他人也可以从中受益的主题。任何建议/提示将不胜感激。谢谢!:)

4

1 回答 1

1

由于听起来您的图像大部分是静态的,因此您可能想尝试的一种方法是使用良好的旧 css。

这里的关键技巧是使用 background-size:cover。请参阅:http ://css-tricks.com/perfect-full-page-background-image/

所以从广义上讲:

  1. 加载您的图像
  2. 在画布中应用您的过滤器。(画布甚至不需要附加到页面上)。
  3. 通过以下方式将画布数据导出为 url:ctx.toDataUrl("image/png");
  4. 将样式添加到您的容器元素或正文中: background-size:cover;background:url('data:image/png;yourcanvasdata');
  5. 通过 js 或 css 过渡属性为容器元素/主体的不透明度设置动画
于 2013-05-01T03:46:47.633 回答