52 votes

Comment utiliser les CSS (et JavaScript?) Pour créer un arrière-plan flou "givré"?

Je suis en train de créer une version d'iOS 7 style aspect givré avec HTML5, CSS3 et JavaScript, qui peut fonctionner sur les navigateurs webkit.

Techniquement, vu le code HTML suivant:

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

Je veux faire quelque chose comme un -webkit-filter: blur(5px) à la première 20px horizontalement de la #main-view.

Si le CSS a été modifié pour être #partial-overlay { width: 20px; height: 100%; ...} alors que j'avais besoin d'appliquer de l' -webkit-filter: blur(5px) à la première 20px verticalement.

La solution évidente est d'utiliser javascript pour faire un clone de l' #main-view, définissez overflow: hidden puis de modifier la largeur/hauteur le cas échéant, mais qui me semble difficile de généraliser à des pages plus complexes/CSS structures.

Est-il une meilleure façon d'atteindre cet objectif avec un minimum de performances et maximale de la généralisation?

EDIT: Voici un exemple de ce que je suis en train de réaliser: Mockup

20voto

apaul34208 Points 7818

Merci pour l'inspiration... Il m'a conduit à cette toile plugin qui fait le tour

Nouveau et Amélioré: -webkit - et Firefox de Travail Exemple, maintenant re-extensible/liquide.

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>

Je l'ai mis dans une fonction de clic, parce que j'ai pensé qu'il serait le plus susceptible de cas d'utilisation. Il fonctionnera tout aussi bien sur le document de prêt.

Bien que la toile de la représentation ne sera pas de pixel parfait, je ne le pense pas vraiment d'importance dans la plupart des cas en raison de son flou.

Mise à jour: Comme l'a demandé c'est maintenant re-extensible. J'ai aussi déplacé le couvercle div dans le JS et a ajouté une svg tomber en arrière pour Firefox. Le redimensionnement exige de la toile afin d'être redessiné à chaque re-taille, alors je l'ai mis en place pour masquer la toile, de superposition, etc alors que vous êtes de redimensionnement et de le remplacer lors de la re-taille s'arrête.

13voto

Gerson Goulart Points 66

Fondamentalement, vous pouvez avoir un paramètre fictif de superposition dans lequel vous dupliquez le contenu principal et synchronisez le défilement des deux divs, puis appliquez le filtre de flou CSS sur la superposition uniquement.

Un simple javascript fera:

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

Et pour le CSS:

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

J'ai mis en place un exemple de travail pour vous (webkit uniquement):

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

S'amuser! :)

11voto

Ken Fyrstenberg Points 38115

Est-il une meilleure façon d'atteindre cet objectif avec un minimum de performances et maximale de généralisation?

La réponse à cette question est non.

La raison en est que, pour faire ce que vous voulez, vous auriez besoin d'un accès direct à l'image utilisé pour la fenêtre du navigateur pour extraire ou de manipuler les pixels dans la zone que vous souhaitez flou (je le souhaite, "aero" dans un navigateur pourrait look très soigné..) ou un filtre qui fonctionne sur les éléments derrière celui de l'appliquer à un (ou peut avoir une limitation de la région définie à elle).

Comme il n'y a pas de support natif pour ce faire (en plus de la toile et l'extension de l'API, ou une bibliothèque qui génèrent des canevas de l'image de l'html> relativement lent), cela devra être fait avec la ruse (images, de la scission de la vrd, etc.) dans les deux cas.

Si vous avez fait tout en votre page sur une toile, vous pourriez faire beaucoup de choses intéressantes, mais vous aussi, vous avez besoin pour effectuer la mise en page, mise à jour, filtrage, etc. vous-mêmes et à cet effet, vous seriez de retour pas non optimisé que le Javascript est plus lent que le natif (pour ne pas mentionner qu'il serait susceptible d'erreurs).

Jusqu'à ce que des navigateurs vous permettent de saisir une partie de la fenêtre comme une toile (jamais? en tant que nécessiterait tout sur la page pour en être de même origine ou ont contenu spéciaux accepter les en-têtes set) il n'y a pas moyen de contourner, mais pour faire des tours.

Mise à jour

Comme une démonstration que vous pouvez le faire en utilisant html2canvas etc, mais d'avoir à utiliser des compromis (-> ralentissement des performances) - la démo est rude, expérimentaux et les besoins des astuces pour bien fonctionner - mais pour l'amour de seulement en démo:
http://jsfiddle.net/AbdiasSoftware/RCaLR/

Résultat:

enter image description here

Fonction généralisée à saisir une partie de l'arrière-plan:

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

Obtenez le cadre de la fenêtre à l'aide de 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)
    });
}

Le contenu:

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

3voto

Connor Points 453

http://thiv.net/frost

Échantillon en direct de ce que j'ai fait (et mis à jour pour ressembler à l'image ci-dessus)

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>
 

Je l'ai fait paraître un peu plus joli que nécessaire, mais ça marche!

2voto

David Nguyen Points 4490

Malheureusement, il n'y a pas de bonne façon de faire cela, car vous avez compris que vous aurez besoin d'une copie de la div principale:

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

L'incrustation repoussera la largeur de l'emballage tandis que la copie principale sera en arrière-plan avec un flou. Évidemment, il y aura des problèmes de performances si le contenu de la copie principale est complexe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X