如何使用 CSS(和 JavaScript?)创建模糊的“磨砂”背景?

IT技术 javascript html css webkit
2021-01-24 06:55:27

我正在尝试使用可以在 webkit 浏览器上运行的 HTML5、CSS3 和 JavaScript 创建 iOS 7 风格的磨砂外观。

从技术上讲,给定以下 HTML:

<style>
  #partial-overlay {
    width: 100%;
    height: 20px;
    background: rgba(255, 255, 255, .2); /* TODO frost */
    position: fixed;
    top: 0;
    left: 0;
  }
</style>
<div id="main-view">
  <div style="width: 50px; height: 50px; background: #f00"></div>
  To my left is a red box<br>
  Now there is just text<br>
  Text that goes on for a few pixels <br>
  or even more
</div>
<div id="partial-overlay">
  Here is some content
</div>

我想将 a-webkit-filter: blur(5px)类的东西应用#main-view.

如果 CSS 被修改为,#partial-overlay { width: 20px; height: 100%; ...}那么我需要将 应用-webkit-filter: blur(5px)到垂直的前 20 像素。

显而易见的解决方案是使用 javascript 克隆#main-view, setoverflow: hidden然后根据需要更改宽度/高度,但在我看来这很难推广到更复杂的页面/CSS 结构。

有没有更好的方法以最小的性能影响和最大的通用性来实现这一目标?

编辑:这是我试图实现的一个例子: 小样

6个回答

感谢您的灵感......它让我找到了这个画布插件,它可以解决问题

新增和改进:-webkit- 和 Firefox 工作示例,现在可重新调整大小/流畅。

JS

$(document).ready(function () {
    frost = function () {
        var w = $('#main-view').width();
        html2canvas(document.body, {
            onrendered: function (canvas) {
                document.body.appendChild(canvas);
                $('canvas').wrap('<div id="contain" />');
            },
            width: w,
            height: 30
        });
        $('canvas, #partial-overlay, #cover').hide();
        $('#cover').fadeIn('slow', function () {
            $('#partial-overlay').fadeIn('slow');
        });
    };

    $('body').append('<div id="cover"></div><svg id="svg-image-blur"><filter id="blur-effect-1"><feGaussianBlur stdDeviation="2"/></filter></svg>');

    $('#main-view').click(function () {
        frost();
        $('#partial-overlay').addClass('vis');
        $(window).resize(function () {
            $('canvas, #partial-overlay, #cover').hide();
        });

        function onResize() {
            if ($('#partial-overlay').hasClass('vis')) {
                frost();
            }
        }
        var timer;
        $(window).bind('resize', function () {
            timer && clearTimeout(timer);
            timer = setTimeout(onResize, 50);
        });

    });

    $('#partial-overlay').click(function () {
        $('#partial-overlay').removeClass('vis');
        $('canvas, #partial-overlay, #cover').hide();
    });
});

CSS

#main-view {
    width:75%;
    height:50%;
    box-sizing: border-box;
    margin:8px;
}
#partial-overlay {
    display:none;
    width: 100%;
    height: 20px;
    position: absolute;
    top: 0;
    left: 0;
    z-index:99;
    background: rgba(255, 255, 255, 0.2);
    cursor:pointer;
}
canvas {
    position: absolute;
    top: 0;
    left: 0;
    -webkit-filter:blur(5px);
    filter: url(#blur-effect-1);
}
#cover {
    display:none;
    height:19px;
    width:100%;
    background:#fff;
    top:0;
    left:0;
    position:absolute;
}
#contain {
    height:20px;
    width:100%;
    overflow:hidden;
    position:absolute;
    top:0;
    left:0;
}
svg {
    height:0;
    width:0;
}

HTML

<div id="main-view">
    <div style="width: 10%; height: 20%; background: #f00; float: left"></div>To my left is a red box
    <br>Now there is just text
    <br>Text that goes on for a few pixels
    <br>or even more</div>
<div id="partial-overlay">Here is some content</div>

我把它放在一个点击函数中,因为我认为这将是最有可能的用例。它在准备好文档时也能正常工作。

尽管画布表示不会是像素完美的,但我认为在大多数情况下它并不重要,因为它被模糊了。

更新:根据要求,现在可以重新调整大小。我还将封面 div 移到了 JS 中,并为 Firefox 添加了一个 svg 回退。调整大小需要在每次调整大小时重新绘制画布,因此我将其设置为在调整大小时隐藏画布、覆盖等,然后在调整大小停止时替换它。

这就是诀窍。唯一的问题是模糊在画布的边缘逐渐消失(边缘周围不是真正的问题,但绝对是在底部)。也许这可以通过一些包装 div 之类的东西来解决,但我似乎无法让它快速工作。
2021-03-14 06:55:27
@AaronYodaiken 如果你需要它更兼容跨浏览器,请查看这个,你可以使用 svg 作为 firefox jsfiddle.net/apaul34208/TfWs3/61 的后备
2021-03-19 06:55:27
模糊中的文本有些有趣(它没有完全模糊,或者画布可能真的不透明或类似的东西)。参见jsfiddle.net/TfWs3/33
2021-03-30 06:55:27
它看起来不错,但它仍然没有像画布在 20 像素处被切断一样模糊,或者我最终的目标是什么。
2021-04-03 06:55:27
如果你能以某种方式举一个例子,其中背景图像是由那些“javascript屏幕截图”中的一个东西制作的,然后模糊,那可能是一个非常聪明的答案。
2021-04-10 06:55:27

基本上,您可以有一个覆盖占位符,您可以在其中复制主要内容并同步两个 div 的滚动,而不是仅在覆盖上应用 css 模糊过滤器。

一个简单的 javascript 就可以做到:

$(document).ready(function(){
  $('.overlay').append( $('.content').html() );
  $('.content').on('scroll', function(){
    $('.overlay').scrollTop($(this).scrollTop());
  });
});

对于 CSS:

.overlay {
    overflow:hidden;
    -webkit-filter: blur(.25em);
    background:#fff;
}

我为您整理了一个工作示例(仅限 webkit):

http://jsfiddle.net/kmxD3/1/

玩得开心!:)

这种方法非常棒,但是我可以看到 div 的顶部有一些不均匀的糖霜。知道什么可能导致这种情况吗?
2021-03-20 06:55:27
@GersonGoulart 太棒了!感谢您的解释 :)
2021-03-21 06:55:27
@Cort3z,这是由于模糊效果逐渐消失(这使您可以看到下面的常规文本。为了更好地理解,请查看本文中模糊示例中的图像:html5rocks.com/en/tutorials/filters /understanding-css(注意右边的图像边缘逐渐变白)。要考虑到这一点,并在模糊的内容上有一个清晰的边缘,你必须把模糊的内容放大一点,然后剪掉它(或制作它脱离画布)以获得更清晰的边缘)。我希望我说得更清楚......这个例子应该有一个更清晰的边缘:jsfiddle.net/kmxD3/105
2021-03-23 06:55:27
这似乎是迄今为止我遇到的最简单、最优雅的解决方案。
2021-03-29 06:55:27

有没有更好的方法以最小的性能影响和最大的通用性来实现这一目标?

答案是否定的

原因是为了做你想做的事情,你需要直接访问用于浏览器窗口的位图来提取或操作你想要模糊的区域中的像素(我希望浏览器中的“aero”看起来很漂亮整洁..) 或一个过滤器,它适用于您应用它的元素后面的元素(或者可以为其设置一个限制区域)。

由于没有本机支持来执行此操作(除了画布和扩展 API,或者从 html 生成画布图像的库 -> 相对较慢)这在任何一种情况下都需要使用技巧(图像、分割 div 等)来完成.

如果您在画布上制作页面中的所有内容,您可以做很多有趣的事情,但您还需要自己执行布局、更新、过滤等操作,因此您将不会返回非优化,因为 Javascript 比本机慢(更不用说它容易出错)。

除非浏览器允许您将窗口的一部分作为画布抓取(永远不会?因为这将要求该页面上的所有内容都具有同源性或具有设置特殊接受标头的内容),否则别无选择,只能做一些技巧。

更新

作为演示,您可以通过使用 html2canvas 等来完成,但必须使用妥协(-> 性能缓慢)- 演示是粗糙的、实验性的,需要调整才能正常工作-但仅用于演示:
http:// jsfiddle.net/AbdiasSoftware/RCaLR/

结果:

在此处输入图片说明

抓取部分背景的通用函数:

getBlurredBG(x, y, width, height, id);

使用 html2canvas 获取窗口的一部分:

function getBlurredBG(x, y, w, h, id) {

    html2canvas(document.body, {
        onrendered: function (canvas) {
            process(canvas, x, y, w, h, id);
        },
        width: (x + w),
        height: (y + h)
    });
}

处理内容:

function process(canvas, x, y, w, h, id) {

    //create new canvas to enable clipping
    //As html2canvas returns a canvas from 0,0 to width and height
    //we need to clip it.
    var ocanvas = document.createElement('canvas');
    ocanvas.width = w;
    ocanvas.height = h;
    ocanvas.style.left = x + 'px';
    ocanvas.style.top = y + 'px';
    ocanvas.style.position = 'absolute';
    ocanvas.id = id;

    var ctx = ocanvas.getContext('2d');
    ctx.drawImage(canvas, x, y, w, h,
                          0, 0, w, h);

    stackBlurCanvasRGB(ocanvas, x, y, w, h, 28)
    lighten(ocanvas);

    ctx.fillStyle = 'rgba(255,255,255,0.5)';
    ctx.fillRect(x, y, w, h);

    ctx.fillStyle = '#999';
    ctx.font = '32px arial';
    ctx.fillText("Partial overlay content", 10, 60);

    document.body.appendChild(ocanvas);
}
@AaronYodaiken 我确实在回答中提到了这一点,但是“实时”使用会很慢(也许优化的部分“快照”可以很好地工作)。人们总是可以缓存快照,但用户需要等待发生这种情况。由于没有本地支持,因此总是需要妥协。
2021-03-15 06:55:27
@AaronYodaiken 那是画布。诸如 html2canvas 之类的库可以做到这一点。然而,它不是实际的快照,而是基于元素几何形状的近似表示(元素->边界->类型->渲染近似)。对于非原始图像,它需要使用代理。但为了这个目的而工作。
2021-03-20 06:55:27
非常感谢您的想法和此解决方案。如果我可以分割赏金,我会的。
2021-03-23 06:55:27
我知道我见过截取整个页面的屏幕截图,您可以将其作为 PNG 或其他格式发送(例如 Google+ 以前的格式)。那个窗口不是->画布吗?
2021-04-10 06:55:27

最近,一个新的-webkit-backdrop-filter. 目前,Safari 9.0 和 Chrome 支持这一功能。

演示:

#blurred {
  -webkit-backdrop-filter: blur(10px);
  width: 200px;
  height: 100px;
  position: fixed;
  top: 50px;
  left: 50px;
  border: 3px solid white;
}
<img src="https://upload.wikimedia.org/wikipedia/commons/e/eb/Ash_Tree_-_geograph.org.uk_-_590710.jpg">

<div id="blurred"> Blurred </div>

目前仅支持 Safari。Chrome 和 Opera 有这个标志。

注意:今天-webkit-backdrop-filter仍然没有很好的支持所以现在如果你想得到这个效果,最好的方法是使用 SVGfeGaussianBlur

这是官方的解决方案。可以将其设置为答案吗?
2021-03-21 06:55:27
@LucaSteeb Chrome 现在支持它,在一个标志后面。希望这在不久的将来成为答案,因为背景过滤器将是实现它的实际方法。其他答案只是廉价的黑客。
2021-03-24 06:55:27
@trusktr 廉价黑客可能有点夸张,但总的来说你是对的。问题是,即使所有新浏览器都支持它,你对使用旧浏览器的用户做了什么——这可能不是一个小数目..
2021-03-25 06:55:27
@LucaSteeb 确实,这是一个问题,但并没有改变其他方法仍然只是黑客的事实。Native 一直拥有这种能力。终于到了让 Web 开始获得真正功能的时候了。
2021-03-31 06:55:27
@trusktr 不,因为它仅在 Safari 中可用,与 Chrome 和 Firefox 相比,它并不真正流行。
2021-04-08 06:55:27

仅 CSS 解决方案: backdrop-filter: blur(4px);