The team behind Medium has created a great experience. The execution of this greatness lies in getting the details right. For example, once logged in, the home page’s image header gradually blurs out as the user scrolls down the page. Another example is the parallax effect as the title and subtitle fading out of the view, against the large full bleed background image, when scrolling down on the page. These interactions not only provide context for the reader, they also make the experience engaging and pleasant. I’ve decided to recreate these two experiences.
Checkout them out: Blur and Parallax.
Typography
Going the extra mile
Even though typography isn’t involved the interactions, it does plays a huge role in Medium’s overall experience. Therefore, I’ve decided to adopt a similar typographic design. Medium uses a sans-serif typeface — JAF Bernino for titles and headings. This bold type is well balanced. It follows a long tradition of humanist typefaces, in that it communicates in a dignified yet unpretentious way. The serif typeface Freight Text is used for the article body, with its soft edges and legible ratios, allows the text to simply flow through. These typefaces, along with the interactions, make the Medium much more special that just another blogging site.
I selected Merriweather as the serif typeface for the article body. Like Freight Text, Merriweather features a generous x-height, and a slightly condensed letter form. The combination of these two traits makes it legible yet precise. Open Sans is used as the title and heading type. With is open forms and humanist roots, Open Sans brings about the same level of clarity that JAF Bernino Sans does.
Blur
With typography set and images selected, a flat HTML page was first built. Next I laid out the steps on how to recreate the experience in terms of the technical steps involved. It comes down to:
- If the user scrolls down, detect the speed of the scroll
- The blur radius should increase according to the speed
- If the user scrolls back up, the speed should become negative
- The blur decreases based on the negative speed
When I first started, CSS3 filter blur property seemed liked a perfect choice. It was hardware accelerated in Chrome, so I assumed it to be well supported by the major browsers. To my surprise, neither Firefox nor IE supported CSS filters. This rules out a pure CSS solution for dynamically changing the blur radius. With CSS out of the picture, I embarked on a journey to find a JS library to complete the task.
StackBlur.js
I found this great library created by Mario Klingemann. StackBlur.js takes a source image on the page and performs a Gaussian blur to the it. It then renders the blurred image on an html5 canvas element on the same page. HTML5 canvas, unlike CSS3 filter, has a great cross browser compatibility. After discovering this library, the blur became relatively clear (pun intended☺). With this library, I could simply call the stackBlurImage function to pass in three parameters: the image source id, the destination id, and amount of blur radius set by a number.
Problem
With the blur in place, jQuery will take care of the rest. $(window).scroll() detects if there is a scroll event on the screen. Inside this function, it calculates the amount of scrolling in distance and then calls the stackBlurImage function, passing in an adjusted dynamic scroll distance value as the third parameter.
So this should work right? Not quite. The blur effect works with the scroll, but it has a dramatic lag — to the point of making the site unusable. This happens because each time the user scrolls, the jQuery scroll events fire continuously until the scroll stops, which means that the image is re-rendered and re-drawn on the canvas continuously for the duration of the scroll. This process takes a big toll on the browser’s performance and hence makes it unusable. So how do we deal with this?
Solution
While the browser doesn’t handle dynamic blur very well, it does though, handle changes in opacity rather effortlessly. Instead of rendering the blur dynamically, the image is first blurred on initial page load and then injected into the canvas as a completely transparent element placed on top of the original image. The starting opacity for the blurred image is set to zero on this page load. As the reader scrolls down, the opacity gradually increases with the scroll amount. The blurred image gradually fades into being opaque and covers the original image. This effect is not 100% identical to dynamically generated blur, but it is very close. Unless someone examine it very closely, the difference cannot be observed. Given that it can be easily produced with a few lines of JavaSript, I think this is an optimal tradeoff. Check it out here.
Parallax
In a nutshell, Medium’s parallax effect is a result of the title and the page moving at different speeds. The opacity of the title block also decreases according to how much the reader has scrolled down, to eventually become transparent. The key here is to determine the viewer’s scroll distance and then continuously update the title’s position and opacity according to the change in scroll distance.
With jQuery, all these things can be done quite easily.
var lastScrollTop = $(window).scrollTop();$(window).scroll(function(){
var scrollAmt = $(this).scrollTop();
var deltaS = scrollAmt — lastScrollTop;
$(‘.titles’).css({
bottom: “-=” + deltaS/4.5,
opacity: “-=” + deltaS/700
}); lastScrollTop = scrollAmt;
});
The scroll amount (variable scrollAmt) is calculated on the fly when the user starts scrolling. How fast the scroll takes place, is stored in the variable deltaS. DeltaS is the speed of the scroll — it is result of subtracting the original scroll position from the new scroll position triggered by the reader’s actions (whether it’s a mouse wheel scroll, dragging down the scroll bar, or pressing the down arrow or page down key).
When the reader scrolls down, the new scroll position becomes greater than the previous one, therefore deltaS is therefore always positive. When the user scrolls back up, the reverse happens, it becomes negative. This makes it very easy to change element styles on the fly, in accordance with the scroll, using an adjusted version of deltaS as the dynamic factor.
After a bit of trial and error, a rate of deltaS/4.5 produces the optimal animation for the movement ; a rate of deltas/700 produces the right fade in opacity. Here comes the crucial line of the code to make it all work. In the code snippet above, on the last line:
By setting the current scroll position as the last scroll position, after the changing the styles, the new lastScrollTop position stays relevant for the next scroll event.
View the parallax effect here.
Lessons Learned
Without inspecting on how Medium built these interactions, the process of the recreating them made me realize that many great interactions are possible, using nothing but simple jQuery. All it takes are simple math and logic — a great interaction doesn’t necessarily require writing a lot of code.