I’ve long appreciated a fun little touch on the Twitter iPhone app: as you drag down on the “Me” screen, the header illustration simultaneously zooms and blurs, in proportion to the amount of drag. I thought that might be a useful technique to replicate in browsers, particularly for banner images in articles…
The markup is very simple:
<article>
<header>
<img src="placid-pond.jpg" alt>
</header>
…
</article>
On the same page, we add some SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<filter id="blur">
<feGaussianBlur stdDeviation="0" />
</filter>
</svg>
This SVG is to achieve the blur effect for Firefox, which doesn’t yet support CSS filters. Note that the inline svg tag is given 0 width and height, so it doesn’t affect the page layout.
This blur effect is called upon in the page’s stylesheet:
header {
overflow: hidden;
font-size: 0;
}
article header img {
width: 100%;
height: auto;
filter: url(#blur);
}
The JavaScript is quite a bit more complex. First, everything goes inside a function that is called when the browser window is scrolled:
window.onscroll = function (event) {
}
We need quite a bit of information inside this function. The variables used are:
var headerImg = document.querySelector("article header img"),
headerImgOffset = headerImg.getBoundingClientRect(),
imgTop = headerImgOffset.top,
imgBottom = headerImgOffset.bottom,
imgHeight = headerImg.offsetHeight,
viewportHeight = document.documentElement.clientHeight,
blur = document.getElementById("blur"),
svgblur = blur.getElementsByTagName("feGaussianBlur")[0],
topEffectStart = Math.abs((viewportHeight - imgHeight)/5);
Reading through them, the variables should make sense:
headerImg
references the header image at the top of the article. (For the sake of simplicity, I am assuming the page has only one article and header element).headerImgOffset
allows us to reference the image’s position and dimensions: in our case,imgTop, imgBottom
andimgHeight
.viewportHeight
is just that: the height of the open browser window.blur
references the SVG code on the page;svgblur
is the specific element that causes the blur effect in Firefox.topEffectStart
is the difference between the current viewport height and the height of the image, divided by 5. This will ensure that the blur effect only starts when the header image is in the top fifth of the browser window. I’ve usedMath.abs
to ensure that this number is always positive, as it’s possible for the image to be taller than the current viewport height.
Having determined all this, we add a conditional statement inside the function:
if (imgTop < topEffectStart && imgBottom > 0 ) {
blurFactor = Math.abs(imgTop / 100);
scaleFactor = 1+Math.abs(imgTop / 1000);
headerImg.style.webkitFilter = "blur(" + blurFactor + "px)";
svgblur.setAttribute("stdDeviation", blurFactor);
headerImg.style.webkitTransform = "scale(scaleFactor)";
headerImg.style.transform = "scale(scaleFactor)";
}
Meaning if the top of the image is at least in the top fifth of the viewport window, and the image is still in view, do the following:
- Take the current position of the top of the image, relative to the top of the viewport, divide it by 100, and turn it into a positive floating point value we’ll call
blurFactor
. - Similarly, take the same difference, divide it by 1000 (as scale is much more sensitive) and add the result to 1, making
scaleFactor
. - Apply
blurFactor
, in pixels, to the header image, as the value of the Webkit blur filter. - Similarly, change the value of
stdDeviation
in the SVG to match (for Firefox). - Finally, apply
scaleFactor
to a CSS transform for the header image.
One advantage of using both scale
and blur
together is that the blur on the outside edges of the image is obscured: even overflow: hidden
can’t hide that, under normal circumstances.
With a few small changes, this same script could be used to focus the image only when it is in the center of the page, working to the user’s own central vision and focus of attention.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/clFJB