画布模糊滤镜在渲染大尺寸图像时悬挂浏览器,如' 2000 x 3000'

时间:2017-05-04 10:48:56

标签: javascript jquery canvas filter blur

我正在尝试制作一个jquery插件,它通过画布过滤图像。我在此链接中找到了一些过滤器代码

https://www.script-tutorials.com/demos/118/index.html

问题在于我在应用对比度,灰度效果时它们工作正常而不是挂起浏览器,运行平稳,但是当我对大尺寸图像使用模糊效果时,例如' 2000x3000',浏览器被绞死。

这是我的插件代码(仅适用于模糊)::

   (function ($) {
$.fn.canfilter = function (options) {
    var thisobg = $(this);
    var canvas = document.createElement('canvas');
    thisobg.empty();
    thisobg.append(canvas);
    var context;
    var iW = $(".cropped").find('img').innerWidth(); // image width
    var iH = $(".cropped").find('img').innerHeight(); // image height
    var p1 = 0.99;
    var p2 = 0.99;
    var p3 = 0.99;
    var er = 0; // extra red
    var eg = 0; // extra green
    var eb = 0; // extra blue
    var iBlurRate = 0;
    var func = 'color'; // last used function
     if (options === 'blur1') {
        resetToBlur1();
    }
    else if (options === 'blur2') {
        resetToBlur2();
    }
    else {
        //do default action
    }

    // ------------------------- blur ---------------------
    function Blur() {
        func = 'blur'; // last used function
        var imgObj = new Image();
        imgObj.src = $(".cropped").find('img').attr('src');//$("#image").attr('src');
        canvas.width = iW;
        canvas.height = iH;
        context = canvas.getContext('2d');
        context.drawImage(imgObj, 0, 0, iW, iH);
        var imgd = context.getImageData(0, 0, iW, iH);
        var data = imgd.data;
        for (br = 0; br < iBlurRate; br += 1) {
            for (var i = 0, n = data.length; i < n; i += 4) {
                iMW = 4 * iW;
                iSumOpacity = iSumRed = iSumGreen = iSumBlue = 0;
                iCnt = 0;
                // data of close pixels (from all 8 surrounding pixels)
                aCloseData = [
                    i - iMW - 4, i - iMW, i - iMW + 4, // top pixels
                    i - 4, i + 4, // middle pixels
                    i + iMW - 4, i + iMW, i + iMW + 4 // bottom pixels
                ];
                // calculating Sum value of all close pixels
                for (e = 0; e < aCloseData.length; e += 1) {
                    if (aCloseData[e] >= 0 && aCloseData[e] <= data.length - 3) {
                        iSumOpacity += data[aCloseData[e]];
                        iSumRed += data[aCloseData[e] + 1];
                        iSumGreen += data[aCloseData[e] + 2];
                        iSumBlue += data[aCloseData[e] + 3];
                        iCnt += 1;
                    }
                }
                // apply average values
                data[i] = (iSumOpacity / iCnt) * p1 + er;
                data[i + 1] = (iSumRed / iCnt) * p2 + eg;
                data[i + 2] = (iSumGreen / iCnt) * p3 + eb;
                data[i + 3] = (iSumBlue / iCnt);
            }
        }
        context.putImageData(imgd, 0, 0);
    }
    function resetToBlur1() {
        p1 = 1;
        p2 = 1;
        p3 = 1;
        er = eg = eb = 0;
        iBlurRate = 1;
        Blur();
    }
    // -- blur 2
    function resetToBlur2() {
        p1 = 1;
        p2 = 1;
        p3 = 1;
        er = eg = eb = 0;
        iBlurRate = 4;
        Blur();
    }
    // ------------------------- // blur ---------------------    

   };
 }(jQuery));

以下是HTML代码:

    <div class="cropped">
        <img id="croppedImg" src="03bc52c30e5eea81e4e816364fe4249a.jpg" alt=""/>
    </div>
    <div id="panel">
      <!-- viewable canvas -->
    </div>
  <input type="button" id="blur1" value="Blur1" />
  <input type="button" id="blur2" value="Blur2" />

并在脚本::

   $("#blur1").click(function(){
           $("#panel").filter('blur1'); 
        });
   $("#blur2").click(function(){
           $("#panel").filter('blur2'); 
        });

我需要一个简单的模糊代码,它不会挂起浏览器。请给我一个解决方案。

由于

1 个答案:

答案 0 :(得分:0)

我不会过多地介绍你的代码,但让我们从一个与你的问题无关的重要事情开始:你不是在将图像绘制到画布上之前等待图像已加载。这可能适用于某些UA,但在其他UA上肯定会失败。

现在,对模糊问题。

在大图像上自己做模糊效果会很慢,因为你必须在所有像素上多次迭代,有this lib,这样做很好,但也可能会挂起浏览器。 / p>

因此,您可能希望在工作室中运行算法,以便在进行计算时不冻结主页面,但这仍然会很慢:

var img = new Image();
img.onload = init;
img.crossOrigin = 'anonymous';
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/John_William_Waterhouse_A_Mermaid.jpg/800px-John_William_Waterhouse_A_Mermaid.jpg";

function initWorker() {
  var worker_file = new Blob([worker_script.textContent]);
  var worker_url = URL.createObjectURL(worker_file);
  return new Worker(worker_url);
}

function init() {
  var worker = initWorker();
  var c = document.createElement('canvas');
  var ctx = c.getContext('2d');
  c.width = canvas.width = img.width;
  c.height = canvas.height = img.height;
  var visibleCtx = canvas.getContext('2d');

  var amount = 2;
  var blur = 5;
  worker.onmessage = function(e) {
    visibleCtx.putImageData(e.data, 0, 0);

    blur += amount;
    if (blur > 10 || blur <= 0) {
      amount *= -1;
    }
    blurImage(blur);

  };

  function blurImage(blurRate) {
    var imageData = ctx.getImageData(0, 0, c.width, c.height);
    worker.postMessage({
      imageData: imageData,
      blurRate: blurRate
    }, [imageData.data.buffer]);
  }
  ctx.drawImage(img, 0, 0);
  blurImage(blur);
}
<canvas id="canvas"></canvas>
<script type="worker-script" id="worker_script">
   	onmessage = function(event){
  	    var d = event.data;
  	    var imgData = d.imageData.data.length ?
  	    	d.imageData : new ImageData(d.imageData.data, d.imageData.width, d.imageData.height);
  		self.postMessage(Blur(imgData, d.blurRate), [imgData.data.buffer]);
  		};
  	function Blur(imgd, iBlurRate){
  		var p1 = 1;
	        p2 = 1;
    	    p3 = 1;
        	er = eg = eb = 0;

		var iW = imgd.width;
        var data = imgd.data;
        for (var br = 0; br < iBlurRate; br += 1) {
            for (var i = 0, n = data.length; i < n; i += 4) {
                iMW = 4 * iW;
                iSumOpacity = iSumRed = iSumGreen = iSumBlue = 0;
                iCnt = 0;
                // data of close pixels (from all 8 surrounding pixels)
                aCloseData = [
                    i - iMW - 4, i - iMW, i - iMW + 4, // top pixels
                    i - 4, i + 4, // middle pixels
                    i + iMW - 4, i + iMW, i + iMW + 4 // bottom pixels
                ];
                // calculating Sum value of all close pixels
                for (e = 0; e < aCloseData.length; e += 1) {
                    if (aCloseData[e] >= 0 && aCloseData[e] <= data.length - 3) {
                        iSumOpacity += data[aCloseData[e]];
                        iSumRed += data[aCloseData[e] + 1];
                        iSumGreen += data[aCloseData[e] + 2];
                        iSumBlue += data[aCloseData[e] + 3];
                        iCnt += 1;
                    }
                }
                // apply average values
                data[i] = (iSumOpacity / iCnt) * p1 + er;
                data[i + 1] = (iSumRed / iCnt) * p2 + eg;
                data[i + 2] = (iSumGreen / iCnt) * p3 + eb;
                data[i + 3] = (iSumBlue / iCnt);
            }
        }
        return imgd;
    }
 </script>

但是,最好的解决方案是使用ctx.filter属性。浏览器将在GPU上进行模糊处理:

var img = new Image();
img.onload = init;
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/John_William_Waterhouse_A_Mermaid.jpg/800px-John_William_Waterhouse_A_Mermaid.jpg"
var ctx = c.getContext('2d');
if(ctx.filter !== 'none'){
  console.warn("your browser doesn't support the filter property," +
   "we should fallback to the getImageData + worker solution");
  throw 'not-supported';
  }
var blur = 5;
var amount = .4;

function init(){
  c.width = this.width;
  c.height = this.height;
  draw();
  }

function draw(){
  blur = blur + amount;
  if(blur > 20 || blur <=0){
    amount *= -1;
    }
  ctx.filter = 'blur('+blur+'px)';
  ctx.drawImage(img, 0, 0);
  requestAnimationFrame(draw);
  }
<canvas id="c"></canvas>