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 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 and imgHeight.
  • 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 used Math.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