Как использовать CSS (и JavaScript?) Для создания размытого "замороженного" фона?

Я пытаюсь создать стилизованный внешний вид iOS 7 с HTML5, CSS3 и JavaScript, который может работать в браузерах 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>

Я хотел бы применить что-то вроде -webkit-filter: blur(5px) к первому 20px по горизонтали от #main-view.

Если CSS был изменен как #partial-overlay { width: 20px; height: 100%; ...}, тогда мне нужно будет применить -webkit-filter: blur(5px) к первому 20px по вертикали.

Очевидным решением является использование javascript для создания клона #main-view, установка overflow: hidden, а затем изменение ширины/высоты по мере необходимости, но мне представляется сложным обобщение на более сложные страницы/структуры CSS.

Есть ли лучший способ достичь этого с минимальным достижением производительности и максимальной обобщаемостью?

EDIT. Вот пример того, чего я пытаюсь достичь: Mockup

Ответ 1

Спасибо за вдохновение... Это привело меня к этому плагину холста, который делает трюк

Новый и улучшенный: - рабочий пример 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 и добавил svg для Firefox. Изменение размера требует, чтобы холст был перерисован на каждом повторном размере, поэтому я устанавливаю его, чтобы скрыть холст, наложение и т.д., Когда вы изменяете размер, а затем замените его, когда размер реза.

Ответ 2

В принципе, у вас может быть оверлейный заполнитель, где вы дублируете основное содержимое и синхронизируете прокрутку обоих div, чем применяете фильтр css blur только для наложения.

Простой 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;
}

Я собрал для вас рабочий пример (только вебкит):

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

Удачи!:)

Ответ 3

Есть ли лучший способ добиться этого с минимальным поражением производительности и максимальная обобщаемость?

Ответ на этот вопрос: нет.

Причина в том, что для того, чтобы делать то, что вы хотите, вам нужен прямой доступ к растровому изображению, используемому для окна браузера, для извлечения или манипулирования пикселями в области, которую вы хотите размыть (я хочу, "aero" в браузере может выглядеть довольно аккуратно..) или фильтр, который работает с элементами, находящимися за тем, к которому вы его применили (или может иметь установленный для него лимитирующий регион).

Поскольку для этого нет встроенной поддержки (помимо canvas и API расширения, или библиотеки, которые генерируют изображение холста из html → относительно медленно), это нужно сделать с помощью обмана (изображений, разделяющих div и т.д.), в любом случае.

Если вы сделали все на своей странице на холсте, вы могли бы сделать много интересного, но вам также нужно было бы выполнить макет, обновление, фильтрацию и т.д., и поэтому вы не вернетесь без оптимизации, поскольку Javascript медленнее, чем родной (не говоря уже о склонности к ошибкам).

До тех пор, пока браузеры не позволят вам захватить раздел окна в виде холста (никогда, поскольку это потребует, чтобы все на этой странице было одинаковым или имело контент со специальными наборами заголовков), нет никакого способа обойти, но сделать фокусы.

Обновление

Как демонстрация того, что вы можете сделать это с помощью html2canvas и т.д., но для использования компромиссов (- > медленная производительность) - демо грубая, экспериментальная и нуждается в настройках, чтобы работать хорошо - но ради демонстрации только: < ш > http://jsfiddle.net/AbdiasSoftware/RCaLR/

Результат:

enter image description here

Обобщенная функция для захвата части фона:

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

Ответ 4

В последнее время новый -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>

Ответ 5

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>

Я сделал это немного красивее, чем нужно, но он работает!

Ответ 6

Я сделал некоторый css-трюк без backdrop-filter, потому что хром не поддерживает его по умолчанию

Мой исходный код с 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>

Ответ 7

К сожалению, нет никакого приятного способа сделать это, поскольку вы выяснили, что вам понадобится копия главного div:

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

Overlay будет нажимать ширину обертки, а главная копия будет в фоновом режиме с размытием. Очевидно, будут проблемы с производительностью, если содержание в основной копии сложное.

Ответ 8

У него все еще очень ограниченная поддержка (только 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

В демо-частичном перекрытии был сдвиг вправо, чтобы сделать более понятным, что это за