Monday 17 October 2016

Polyfill for CSS backdrop-filter (on whole page)

On my latest project, which was based on Bootstrap, the client (/ designer) wanted to substitute the default gray background of the modal window with a nice gaussian blur.
Default Bootstrap modal
Bootstrap modal with gaussian blur over background
Fortunately, literally a couple of weeks beforehand I'd skimmed a reddit post on a very similar thing (except the modal is blurred instead of the background).  CSS for browsers that support it, some sort of SVG fallback for those which don't.  Easy, I thought naively.  On closer inspection you'll notice that all the other methods actually blur one particular image which is used as the background.  This won't work in our case, we need to blur the entire rest of the page.

Ok first off, let's get to the code for browsers which support the backdrop-filter tag (as of this post, that's only iOS Safari, and Chrome if you have the "experimental web platform features" flag checked).  We can use the CSS @supports at-rule to make sure this is only handled by browsers which support it (and an inverse rule for the remainder).  This is pretty well supported, but you can always polyfill it as well.

@supports (backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px)) {
    .modal-backdrop.in {
        opacity: 1;
        background-color: transparent;
        -webkit-backdrop-filter: blur(4px);
        backdrop-filter: blur(4px);
    }
}

(the screenshot above is using this code in Chrome - with the flag enabled)

In all the digging through other posts on workarounds using SVG, and testing out various solutions I had a sudden moment of clarity (or madness).  What if I somehow got an image of the entire page and set that as the (blurred) background-image? Well, a SO search result shows that it's very much possible, and html2canvas is the framework to use.

Shut up and give me the code!

Ok ok...

The CSS:

@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
    .modal-backdrop {
        background-color: initial;
        -webkit-transition: all .25s ease-in-out;
        -moz-transition: all .25s ease-in-out;
        transition: all .25s ease-in-out;
        opacity: 0;
    }
}

And the Javascript:

if (!CSS.supports('backdrop-filter', 'blur(4px)') && !CSS.supports('-webkit-backdrop-filter', 'blur(4px)')) {
    $('[data-target="#myModal"]').click(function () {
        html2canvas(document.body).then(function (canvas) {
            $('.modal-backdrop').css({
                "background-image": 'url(' + canvas.toDataURL("image/png") + ')',
                "filter": "blur(4px)",
                "opacity": "1"
            });
        });
    });
}
Where [data-target="#myModal"] is the link/button/whatever that opens your modal.

Boom! We have a nice .25 second transition to a blurred screenshot of the page as our modal background.  By testing for support, theoretically we should have nice degradation back to the default Bootstrap modal (for things like IE10).

Note: I have not tested this in all browsers, or even many browsers, just the latest of Chrome, Firefox, Edge, and IE.  If you have a lot of components on your page then html2canvas can take a while to render the image.  Use (or don't) after testing per the requirements of your solution.

2 comments:

  1. Thanks for putting me on the right track for this one. It's so *annoying* how designers always want to copy Mac UI designs, even if they're hard to do anywhere else... Anyway. I found "html2canvas" to be too unreliable to be useful, but fortunately I found another implementation of the same idea at https://github.com/tsayen/dom-to-image which was a bit more useful.

    ReplyDelete
  2. jQuery is not JavaScript and it's not even compatible with itself, just stop using it and learn real JavaScript. Also I was requested to work with Bootstrap once and it too does absolutely ridiculous things, just do a simple reset using * {border: 0; margin: 0; padding: 0;}

    ReplyDelete