在JavaScript画布中的透明框后面模糊 [英] Blur behind transparent box in JavaScript canvas

查看:72
本文介绍了在JavaScript画布中的透明框后面模糊的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在JavaScript画布的透明框(fillStyle = 'rgba(255, 255, 255, 0.2)')后面实现模糊效果?这是到目前为止我得到的:

How can I achieve a blur behind a transparent box (fillStyle = 'rgba(255, 255, 255, 0.2)') in JavaScript canvas? Here's what I've got so far:

var canvas = document.getElementById('draw');
var c = canvas.getContext('2d');

function main() {
    c.fillStyle = '#222';
    c.fillRect(0, 0, canvas.width, canvas.height);

    c.fillStyle = '#000';
    c.fillRect(32, 32, 64, 64);
    c.fillStyle = 'rgba(255, 255, 255, 0.2)';
    c.filter = 'blur(5px)';
    c.fillRect(16, 16, 128, 24);
}

但是发生的是,矩形本身本身是模糊的,而不是模糊矩形后面的背景.

But what happens, is instead of blurring the background behind the rectangle, is the rectangle itself is blurred, kind of obviously.

在最后的脚本中,我可能会使用路径而不是rects.

In the final script, I will probably use paths instead of rects.

推荐答案

Context2D滤镜将仅应用于新图形,因此也要模糊背景,实际上必须重新绘制要成为背景的部分模糊.

Context2D filters will be applied only on your new drawings, so to also blur the background, you would actually have to redraw the part of the background you want to be blurred.

幸运的是,画布本身可以 drawImage .

Fortunately, canvas can drawImage itself.

var blurredRect = {
  x: 80,
  y: 80,
  height: 200,
  width: 200,
  spread: 10
};
var ctx = canvas.getContext('2d');

var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';

function draw() {
  canvas.width = img.width / 2;
  canvas.height = img.height / 2;
  // first pass draw everything
  ctx.drawImage(img, 0,0, canvas.width, canvas.height); 
  // next drawings will be blurred
  ctx.filter = 'blur('+ blurredRect.spread +'px)';
  // draw the canvas over itself, cropping to our required rect
  ctx.drawImage(canvas,
    blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height,
    blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height
  );
  // draw the coloring (white-ish) layer, without blur
  ctx.filter = 'none'; // remove filter
  ctx.fillStyle = 'rgba(255,255,255,0.2)';
  ctx.fillRect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
}

<canvas id="canvas"></canvas>

但是,画布 blur 过滤器与CSS过滤器有点不同,它可以使扩展停留在绘制区域内.这意味着在我们的示例中,矩形周围有一个5px的边框,没有中心那么模糊.

But, canvas blur filter is a bit different than CSS one in that it will make the spreading stay inside the drawn area. This means that in our case, we have a 5px border around our rectangle that is less blurred than the center.

要解决此问题,我们可以以不同的顺序处理整个事情,并使用 globalCompositeOperation 属性*:

To workaround, we can take the whole thing in a different order and play with globalCompositeOperation property*:

var blurredRect = {
  x: 80,
  y: 80,
  height: 200,
  width: 200,
  spread: 10
};
var ctx = canvas.getContext('2d');

var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';

function draw() {
  var spread = blurredRect.spread,
    ratio = 0.5,
    // make our blurred rect spreads
    x = blurredRect.x - spread,
    y = blurredRect.y - spread,
    w = blurredRect.width + (spread * 2),
    h = blurredRect.height + (spread * 2);
    
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  
  // this time we will first draw the blurred rect
  ctx.filter = 'blur('+ spread +'px)';
  // this time we draw from the img directly
  ctx.drawImage(img,
    x / ratio, y / ratio, w / ratio, h / ratio,
    x, y, w, h
  );
  
  // now we will want to crop the resulting blurred image to the required one, so we get a clear-cut

  ctx.filter = 'none'; // remove filter
  // with this mode, previous drawings will be kept where new drawings are made
  ctx.globalCompositeOperation = 'destination-in';
  ctx.fillStyle = '#000'; // make it opaque
  ctx.rect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
  ctx.fill(); // clear-cut done
  // reuse our rect to make the white-ish overlay
  ctx.fillStyle = 'rgba(255,255,255,0.2)';
  // reset gCO to its default
  ctx.globalCompositeOperation = 'source-over';
  ctx.fill();
  
  // now we will draw behind the our blurred rect
  ctx.globalCompositeOperation = 'destination-over';
  ctx.drawImage(img, 0,0, canvas.width, canvas.height); 

  // reset to defaults
  ctx.globalCompositeOperation = 'source-over';
}

<canvas id="canvas"></canvas>

但是,这种方法要求我们将整个背景作为可绘制的内容进行访问,在上面的示例中,这只是一张图像,但是在现实生活中,这可能意味着您必须在第二个屏幕外进行此操作画布.

But this approach requires that we keep access to the whole background as a drawable thing, in the example above that was just an image, but in real life, this might mean you'd have to do this operation on a second offscreen canvas.

var blurredRect = {
  x: 80,
  y: 80,
  height: 200,
  width: 200,
  spread: 2
};
var ctx = canvas.getContext('2d');
// create an off-screen canvas
var bCanvas = canvas.cloneNode();
var bCtx = bCanvas.getContext('2d');

var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';

function draw() {
  var spread = blurredRect.spread;

  canvas.width = bCanvas.width = img.width / 2;
  canvas.height = bCanvas.height = img.height / 2;

  // now we have a composed background
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  ctx.font = '40px Impact';
  ctx.fillStyle = 'white';
  ctx.fillText('..SO BLUR ME..', 120, 282);
  
  // make our clear-cut on the offscreen canvas
  bCtx.filter = 'blur(' + spread +'px)';
  bCtx.drawImage(canvas,
    blurredRect.x - spread, blurredRect.y - spread, blurredRect.width + spread * 2, blurredRect.height + spread * 2,
    blurredRect.x - spread, blurredRect.y - spread, blurredRect.width + spread * 2, blurredRect.height + spread * 2
  );
  // clear-cut
  bCtx.filter = 'none';
  bCtx.globalCompositeOperation = 'destination-in';
  bCtx.beginPath();
  bCtx.rect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
  bCtx.fillStyle = '#000';
  bCtx.fill();
  // white-ish layer
  bCtx.globalCompositeOperation = 'source-over';
  bCtx.fillStyle = 'rgba(255,255,255,0.2)';
  bCtx.fillRect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);

  // now just redraw on the visible canvas
  ctx.drawImage(bCanvas, 0,0);
 
}

<canvas id="canvas"></canvas>

*有人可能会说,我们可以使用ctx.clip()代替屏幕外的画布和gCO,但是由于您说的是Path可能比rect更复杂,所以我不建议这样做.确实,尽管它需要更少的代码,并且可能使用更少的内存,但是剪切对抗锯齿来说是不好的,并且由于您正在进行模糊处理,因此看起来很丑陋.

*One may say that instead of an offscreen canvas and gCO we could have used ctx.clip(), but since you said it might a more complex Path than a rect, I will not advise to do so. Indeed, while it would require less code, and maybe use less memory, clipping is just bad with antialiasing, and since you are doing blurring, that will just look plain ugly.

这篇关于在JavaScript画布中的透明框后面模糊的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆