【问题标题】:Blur behind transparent box in JavaScript canvasJavaScript画布中透明框后面的模糊
【发布时间】:2018-11-14 04:30:11
【问题描述】:

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

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);
}

但是会发生什么,不是模糊矩形后面的背景,而是矩形本身是模糊的,有点明显。

在最终脚本中,我可能会使用路径而不是矩形。

【问题讨论】:

  • 你到底想达到什么目的?真的不清楚。
  • @Kaiido 我想实现类似于新的 Windows 丙烯酸玻璃效果的东西,除了我只想要没有位移的模糊。

标签: javascript html5-canvas


【解决方案1】:

Context2D 过滤器将仅应用于您的新绘图,因此要同时模糊背景,您实际上必须重新绘制要模糊的背景部分。

幸运的是,canvas 可以自己drawImage

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>

但是,canvas blur 过滤器与 CSS 过滤器有点不同,因为它会使扩展停留在绘制区域内。这意味着在我们的例子中,我们在矩形周围有一个 5px 的边框,它比中心更模糊。

为了解决问题,我们可以以不同的顺序处理整个事情并使用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>

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

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,但是由于您说它可能是比矩形更复杂的路径,所以我不建议这样做。事实上,虽然它需要更少的代码,并且可能会使用更少的内存,但剪切对于抗锯齿来说是很糟糕的,而且因为你正在做模糊,这看起来很丑。

【讨论】:

  • 这在 safari 上无法模糊?它适用于 chrome,但仅在 safari 上显示白色半透明矩形。
  • @Crashalot 是的,Safari 仍然不支持ctx.filter,对于模糊,你可以使用类似StackBlur 的东西。 (ps:特征检测可以查看if(!("filter" in ctx))
  • 感谢您的澄清!仍然无法提供咨询,甚至每月有几个小时来咨询此类问题?
猜你喜欢
  • 2015-03-18
  • 2013-01-24
  • 2015-10-25
  • 1970-01-01
  • 2019-07-15
  • 2017-04-14
  • 1970-01-01
  • 2022-06-28
  • 1970-01-01
相关资源
最近更新 更多