简体   繁体   English

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

[英]How to use CSS (and JavaScript?) to create a blurred, "frosted" background?

I'm trying to create an iOS 7 style frosted look with HTML5, CSS3 and JavaScript which can work on webkit browsers.我正在尝试使用 HTML5、CSS3 和 JavaScript 创建一个 iOS 7 风格的磨砂外观,它可以在 webkit 浏览器上运行。

Technically, given the following HTML:从技术上讲,给定以下 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>

I'd like to apply something like a -webkit-filter: blur(5px) to the first 20px horizontally of #main-view .我想将-webkit-filter: blur(5px)类的东西应用到#main-view的前 20px 水平方向。

If the CSS was modified to be #partial-overlay { width: 20px; height: 100%; ...}如果 CSS 被修改为#partial-overlay { width: 20px; height: 100%; ...} #partial-overlay { width: 20px; height: 100%; ...} #partial-overlay { width: 20px; height: 100%; ...} then I'd need to apply the -webkit-filter: blur(5px) to the first 20px vertically. #partial-overlay { width: 20px; height: 100%; ...}然后我需要将-webkit-filter: blur(5px)垂直应用到前 20px。

The obvious solution is to use javascript to make a clone of the #main-view , set overflow: hidden and then change the width/height as appropriate but that seems to me hard to generalize to more complex pages/CSS structures.显而易见的解决方案是使用 javascript 克隆#main-view ,设置overflow: hidden然后根据需要更改宽度/高度,但在我看来,这很难推广到更复杂的页面/CSS 结构。

Is there a better way to achieve this with minimal performance hit and maximal generalizability?有没有更好的方法以最小的性能影响和最大的通用性来实现这一目标?

EDIT : Here is an example of what I'm trying to achieve:编辑:这是我试图实现的一个例子:小样

Thanks for the inspiration... It led me to this canvas plugin which does the trick感谢您的灵感......它让我找到了这个画布插件,它可以解决问题

New and Improved: -webkit- and Firefox Working Example , now re-sizable/fluid.新增和改进: -webkit- 和 Firefox 工作示例,现在可重新调整大小/流畅。

JS 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 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 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>

I put it in a click function, because I figured it would be the most likely use case.我把它放在一个点击函数中,因为我认为这将是最有可能的用例。 It will work just as well on document ready.它在准备好文档时也能正常工作。

Although the canvas representation wont be pixel perfect, I don't think it will really matter in most cases because its being blurred.虽然画布表示不会是像素完美的,但我认为在大多数情况下它并不重要,因为它被模糊了。

Update: As requested this is now re-sizable.更新:根据要求,现在可以重新调整大小。 I also moved the cover div into the JS and added an svg fall back for Firefox.我还将封面 div 移到了 JS 中,并为 Firefox 添加了一个 svg 回退。 The resizing requires the canvas to be redrawn on each re-size, so I set it up to hide the canvas, overlay, etc while you're resizing and then replace it when the re-size stops.调整大小需要在每次调整大小时重新绘制画布,因此我将其设置为在调整大小时隐藏画布、覆盖等,然后在调整大小停止时替换它。

Basically you could have a overlay placeholder where you duplicate the main content and sync the scrolling of both divs, than apply css blur filter on the overlay only.基本上,您可以有一个覆盖占位符,您可以在其中复制主要内容并同步两个 div 的滚动,而不是仅在覆盖上应用 css 模糊过滤器。

A simple javascript will do:一个简单的 javascript 就可以做到:

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

And for the CSS:对于 CSS:

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

I put together a working example for you (webkit only):我为您整理了一个工作示例(仅限 webkit):

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

Have fun!玩得开心! :) :)

Is there a better way to achieve this with minimal performance hit and maximal generalizability?有没有更好的方法以最小的性能影响和最大的通用性来实现这一目标?

The answer to this is no .答案是否定的

The reason is that in order to do what you want you would need direct access to the bitmap used for the browser window to extract or manipulate the pixels in the area you want to blur (I wish, "aero" in a browser could look pretty neat..) or a filter that works on the elements behind the one you apply it to (or can have a limiting region set to it).原因是为了做你想做的事情,你需要直接访问用于浏览器窗口的位图来提取或操作你想要模糊的区域中的像素(我希望浏览器中的“aero”看起来很漂亮整洁..) 或一个过滤器,它适用于您应用它的元素后面的元素(或者可以为其设置一个限制区域)。

As there is no native support to do this (besides canvas and extension API, or a library that generate canvas image from the html -> relatively slow) this will need to be done with trickery (images, splitting divs etc.) in either case.由于没有本机支持来执行此操作(除了画布和扩展 API,或者从 html 生成画布图像的库 -> 相对较慢)这在任何一种情况下都需要使用技巧(图像、分割 div 等)来完成.

If you made everything in your page on a canvas you could do a lot of interesting things, but you would also need to perform layout, update, filtering etc. yourselves and therefor you would be back no non-optimized as Javascript is slower than native (not to mention it would be error prone).如果您在画布上制作页面中的所有内容,您可以做很多有趣的事情,但您还需要自己执行布局、更新、过滤等操作,因此您将不会返回非优化,因为 Javascript 比原生慢(更不用说它容易出错)。

Until browsers allow you to grab a section of the window as a canvas (never? as that would require everything on that page to be same-origin or have content with special accept headers set) there is no way around but to do tricks.直到浏览器允许您将窗口的一部分作为画布抓取(永远不会?因为这将要求该页面上的所有内容都是同源的或具有设置特殊接受标头的内容),否则别无选择,只能做一些技巧。

Update更新

As an demonstration that you can do it by using html2canvas etc, but having to use compromises (-> slow performance) - the demo is rough, experimental and needs tweaks to work well - but for the sake of demo only:作为演示,您可以使用 html2canvas 等来完成它,但必须使用妥协(-> 性能缓慢)-演示是粗糙的、实验性的,需要调整才能正常工作-但仅出于演示目的:
http://jsfiddle.net/AbdiasSoftware/RCaLR/ http://jsfiddle.net/AbdiasSoftware/RCaLR/

Result:结果:

在此处输入图片说明

Generalized function to grab part of background:抓取部分背景的通用函数:

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

Get part of window using html2canvas:使用 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)
    });
}

Process the content:处理内容:

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

Recently, a new -webkit-backdrop-filter .最近,一个新的-webkit-backdrop-filter This is currently supported in Safari 9.0, and Chrome behind a flag.目前,Safari 9.0 和 Chrome 支持这一功能。

Demo:演示:

 #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>

Support right now is only Safari.目前仅支持 Safari。 Chrome and Opera have this under a flag. Chrome 和 Opera 有这个标志。

Note: Today -webkit-backdrop-filter still doesn't have great support so for now if you want to get this effect, the best way is using SVG's feGaussianBlur注意:今天-webkit-backdrop-filter仍然没有很好的支持,所以现在如果你想得到这个效果,最好的方法是使用 SVG 的feGaussianBlur

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

I've made some css-trick without backdrop-filter because chrome doesn't support it by default我做了一些没有backdrop-filter css-trick,因为默认情况下 chrome 不支持它

My original code with sass : https://codepen.io/mixal_bl4/pen/EwPMWo使用 sass 的原始代码: https : //codepen.io/mixal_bl4/pen/EwPMWo

 $(()=>{ let sdm = $('.some-draggable-modal'); sdm.center().draggable(); }); jQuery.fn.center = function () { this.css("position","absolute"); this.css("top", Math.max(0, (($(window).height() - $(this).outerHeight()) / 2) + $(window).scrollTop()) + "px"); this.css("left", Math.max(0, (($(window).width() - $(this).outerWidth()) / 2) + $(window).scrollLeft()) + "px"); return this; };
 body { background: -webkit-repeating-linear-gradient(135deg, #fff, #fff 25px, #e2edc1 25px, #e2edc1 50px) fixed; background: repeating-linear-gradient(-45deg, #fff, #fff 25px, #e2edc1 25px, #e2edc1 50px) fixed; background-attachment: fixed; background-size: cover; } html:before, body:before { content: ""; display: block; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.3); } html .some-draggable-modal, body .some-draggable-modal { width: 150px; height: 150px; border: 1px solid lime; display: -webkit-box; display: -ms-flexbox; display: flex; place-content: center; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; text-align: center; border-radius: 6px; cursor: move; position: relative; overflow: hidden; } html .some-draggable-modal .title, body .some-draggable-modal .title { position: relative; z-index: 1; color: black; } html .some-draggable-modal:before, body .some-draggable-modal:before { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: -webkit-repeating-linear-gradient(135deg, #fff, #fff 25px, #e2edc1 25px, #e2edc1 50px) fixed; background: repeating-linear-gradient(-45deg, #fff, #fff 25px, #e2edc1 25px, #e2edc1 50px) fixed; background-attachment: fixed; background-size: cover; } html .some-draggable-modal:hover:before, body .some-draggable-modal:hover:before { -webkit-filter: blur(2px); filter: blur(2px); } html .some-draggable-modal:hover:after, body .some-draggable-modal:hover:after { content: "filter: blur(2px)"; position: absolute; left: 0; right: 0; bottom: 10px; color: green; }
 <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> <div class="some-draggable-modal"> <span class="title">You can drag me :)</span> </div>

http://thiv.net/frost http://thiv.net/frost

Live sample of what I did ( and updated to look just like the image above )我所做的实时示例(并更新为与上图一样)

Code:代码:

<style>
  #partial-overlay {
    width: 100%;
    height: 45px;
    background: #ffffff; /* TODO frost */
    -webkit-opacity:0.70;
    -webkit-filter: blur(5px);
    position: absolute;
    top: 20px;
    left: 0px;
    z-index:5;
  }
  #main-view
  {
   position: fixed;
   top: 20px;
   left: 80px;
   z-index:-1;
  }
</style>
<div id="main-view">
  <div style="width: 50px; height: 50px; background: #f00; position:fixed; left:10px; top: 40px; -webkit-filter: blur(2px); "></div>
    <div style="width: 80px; height: 60px; background: #fff; position:fixed; left:0px; top: 66px; -webkit-filter: blur(5px);"></div>
    <div style="width: 50px; height: 30px; background: #f00; position:fixed; left:10px; top: 60px;"></div>
  <p style="font-family:Sans, Arial, Verdana;">
  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
  </p>
</div>
<div id="partial-overlay">

</div>

I made it look a bit prettier than it needs to be, but It works!我让它看起来比它需要的漂亮一点,但它有效!

Unfortunately there is no nice way of doing this, as you figured out you will need a copy of the main div:不幸的是,没有好的方法可以做到这一点,因为您发现您需要主 div 的副本:

<div class="wrapper">
   <div class="overlay"></div>
   <div id="main-copy"></div>
</div>

Overlay will push the wrapper width around while, main-copy will be in the background with blur.叠加将推动包装宽度,而主副本将在背景中模糊。 Obviously there will be performance issues if the content in main-copy is complex.如果主副本中的内容很复杂,显然会出现性能问题。

It has still very limited suport (only Firefox) but one way to get it could be this:它的支持仍然非常有限(仅限 Firefox),但获得它的一种方法可能是:

Firefox only demo仅 Firefox 演示

The CSS is quite simple: CSS 非常简单:

#partial-overlay {
    width:400px; 
    height:100px; 
    background: -moz-element(#main-view);
    top: 0px;
    left: 200px;
    position: absolute;
    opacity: 0.3;
}

and the key is to use as background for partial overlay the main-view element.关键是用作背景部分覆盖主视图元素。

This demo uses only opacity because filters are not availables for Firefox.此演示仅使用不透明度,因为过滤器不适用于 Firefox。

The element property for the background has been approved by w3c, so it could show sometime in the future in webkit背景的元素属性已经被 w3c 认可,所以它可以在未来某个时候在 webkit 中显示

In the demo partial-overlay has been shift to the right to make more clear what is what在演示中部分覆盖已移至右侧以更清楚地说明什么是什么

You can now use the backdrop-filter CSS property.您现在可以使用backdrop-filter CSS 属性。

Latest versions of Chrome, Opera and Edge support it by default, while Safari and Legacy Edge support it with the -webkit- prefix, and Firefox supports the unprefixed version if the layout.css.backdrop-filter.enabled preference is set to true .最新版本的 Chrome、Opera 和 Edge 默认支持它,而 Safari 和 Legacy Edge 通过-webkit-前缀支持它,如果layout.css.backdrop-filter.enabled首选项设置为true ,Firefox 支持无前缀版本。

So your CSS becomes:所以你的 CSS 变成了:

#partial-overlay {
    width: 100%;
    height: 20px;
    background: rgba(255, 255, 255, .2); /* TODO frost */
    position: fixed;
    top: 0;
    left: 0;
    
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM