52

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

从技术上讲,给定以下 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)垂直应用到前 20px。

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

有没有更好的方法以最小的性能损失和最大的泛化性来实现这一点?

编辑:这是我想要实现的一个例子: 小样

4

10 回答 10

20

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

新增和改进:-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 后备。调整大小需要在每次调整大小时重新绘制画布,因此我将其设置为在调整大小时隐藏画布、叠加层等,然后在调整大小停止时替换它。

于 2013-06-16T15:23:41.580 回答
13

基本上,您可以有一个覆盖占位符,您可以在其中复制主要内容并同步两个 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/

玩得开心!:)

于 2013-09-23T22:24:57.357 回答
11

有没有更好的方法以最小的性能损失和最大的泛化性来实现这一点?

答案是否定的。

原因是为了做你想做的事,你需要直接访问用于浏览器窗口的位图,以提取或操作你想要模糊的区域中的像素(我希望浏览器中的“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);
}
于 2013-06-16T05:58:35.387 回答
10

最近,一个新的-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还没有很好的支持,所以现在如果你想得到这个效果,最好的方法是使用 SVG 的feGaussianBlur

于 2015-08-11T22:12:49.617 回答
6

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

于 2019-07-30T20:39:02.690 回答
4

我做了一些 css-trick 没有backdrop-filter因为 chrome 默认不支持它

我使用 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>

于 2017-09-19T11:04:30.323 回答
3

http://thiv.net/frost

我所做的实时示例(并更新为与上图一样)

代码:

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

我让它看起来比它需要的更漂亮,但它有效!

于 2013-06-16T04:30:20.257 回答
2

它的支持仍然非常有限(仅限 Firefox),但获得它的一种方法可能是:

仅 Firefox 演示

CSS 非常简单:

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

关键是用作部分覆盖主视图元素的背景。

此演示仅使用不透明度,因为过滤器不适用于 Firefox。

背景的元素属性已被 w3c 批准,因此它可能会在将来的某个时间在 webkit 中显示

在演示中,部分叠加已向右移动,以更清楚地说明什么是什么

于 2013-06-16T20:32:56.597 回答
2

不幸的是,没有很好的方法可以做到这一点,因为您发现您需要一份主 div 的副本:

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

覆盖将推动包装宽度,而主副本将在背景中模糊。如果主副本中的内容很复杂,显然会出现性能问题。

于 2013-06-14T16:46:20.953 回答
2

您现在可以使用backdrop-filterCSS 属性。

Chrome、Opera 和 Edge 的最新版本默认支持它,而 Safari 和 Legacy Edge 则通过前缀支持它,如果首选项设置为-webkit-,则 Firefox 支持无前缀版本。layout.css.backdrop-filter.enabledtrue

所以你的 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);
}
于 2020-06-26T16:29:26.347 回答