Work

Work

Snippets

Snippets

Graphics

Graphics

Blog

Blog

Email Search

Progress Pie

By Dónal - September 2017

Scroll event

Visual feedback

A visualisation of page scroll progression.

View Demo

You’re reading an article online only to suddenly begin scrolling rapidly to get a sense of how long the page is, and whether or not it’s worth your while seeing it through in one sitting? Sure, you could glance at the scrollbar to gain some rough perspective, but sometimes it’s not a true visual cue as a lot of the page could be occupied by comments, adverts, or other bulky content. Quite regularly I find articles which occupy only a quarter or less of their page length, often due to a heavy comments section below. From this point of view the scrollbar can be misleading.

Progress pie is a simple idea that provides more intuitive visual feedback on an article’s progression as the user scrolls through. It is essentially a small pie-chart in the corner of the screen that only appears on scroll when article itself is within the boundaries of the screen. The user sees the size of the ‘consumed’ sector relative to the whole pie, giving a clear indication of how much content remains. The pie in this case will represent the article (within a larger page), however it could just as easily be applied to the whole page.

Making the pie

Let’s begin by creating the pie markup with some basic Javascript (include jQuery for convenience). We’ll append the ‘pie’ to the body and then append a ‘spinner’ element and a ‘blocker’ element to the pie. Together the spinner and blocker will control the angle shown within the pie.

// Create pie
$('<div />', { 'id': 'pie' }).appendTo('body');
$('<div />', { 'id': 'spinner' }).appendTo('#pie');
$('<div />', { 'id': 'blocker' }).appendTo('#pie');

Using CSS, we’ll make the spinner half the width of the pie and position it to the right. Similarly, the blocker will be half it’s width and sit to the left (1). The spinner and blocker will be the same colour at first, in this case a dark red, while the pie underneath will be a bright red. Using the CSS property ‘transform-origin: left;’ on the spinner, it will rotate about its left side, in the same location as the centre of the pie (2). The blocker sits over the spinner as the spinner rotates underneath, hence ‘blocking’ our view of it. By giving the outer pie a hidden overflow the corners of the blocker and spinner are trimmed off (3).

Building a rotating pie with CSS.

As the spinner rotates, we can see the red pie being revealed underneath will represent the sector of ‘progress’. However, a problem occurs when we reach past 180 degrees – with the blocker in place and the spinner emerging on the other side, the angle begins to decrease instead of continuing to increase. To resolve this we need to flip the blocker to the right side once the spinner has passed 180 degrees. In addition, we need to change the colour of the blocker to the same as the pie underneath (4). This can be achieved by dynamically adding an ‘overhalf’ class to the blocker with Javascript. Now the bright red ‘progress’ section will combine with the bright red of blocker to the right and the newly revealed pie to the left. Nearing towards 360 degrees, all that can be seen of the spinner is a small sector of dark red (5). Here’s the CSS:

#pie {
position: fixed;
bottom: 50px;
right: 50px;
width: 70px;
height: 70px;
border-radius: 50%;
background: red;
overflow: hidden;
opacity: 0;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
transition: all 0.5s ease;
}
#pie.active {
opacity: 1;
}
#spinner {
position: absolute;
top: 0;
left: 50%;
width: 50%;
height: 100%;
background: darkred;
transform-origin: left;
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
transform: rotate(0deg);
}
#blocker {
position: absolute;
top: 0;
left: 0%;
width: 50%;
height: 100%;
background: darkred;
}
#blocker.overhalf {
left: 50%;
background: red;
}

Notice also the ‘active’ class that will bring the pie to full opacity from its default zero opacity, which will be applied in response to the user’s interactivity.

Making it work

Now to connect the pie to the page scroll. As stated before, the aim here is to have the 360 degrees of the pie represent the article length within its page, while the bright red sector represents the visible and previous content (‘consumed content’). There may be content before and after the article but will not influence the operation of the pie.

At this point, let’s assume a page full of content including an article somewhere in the middle with an id=”article”. There are some dynamic calculations to be done first, so let’s define a few necessary variables:

var articleHeight = $('#article').outerHeight();
var articleTop = $('#article').offset().top;
var articleBottom = articleTop + articleHeight;
var scrnHeight = $(window).height();
var scrolled = $(window).scrollTop();
var pie = $('#pie');
var spinner = pie.children('#spinner');
var blocker = pie.children('#blocker');

Next we’ll create a function that will be called on page load, scroll, and resize. Within this function will be 3 dynamic tasks: 1) Show pie only if article is visible, 2) Calculate and apply the angle within the pie, and 3) Position the blocker appropriately.

function progressPie() {
// Inner function content here
}
$(window).on('load', function(){ progressPie(); });
$(window).scroll ( function() { progressPie(); });
$(window).resize ( function() { progressPie(); });

1. Show pie only if article is visible

The pie will appear when the article first appears on screen, and will disappear once the article has been scrolled out of view completely. In order to do this we will add the ‘active’ class to the pie on each scroll event IF the top of the article is above the bottom of the viewport AND the bottom of the article is below the top of the viewport:

if( (articleTop-scrolled) < scrnHeight && articleBottom > scrolled) {
 
     // Pie appears only when scrolling
     if(pie.hasClass('active')) {
          clearTimeout(timer);
	  pie.removeClass('active');
     }
     pie.addClass('active');
     timer = setTimeout(function(){
          pie.removeClass('active');
     }, 1000); // Hold for 1s
 
} // end if

Inside the above if statement, we are adding the active class and setting a timer of one second after which the class will be removed again. If the scroll occurs within that second, the timer will clear and the same process will be applied from there. This ensures the pie remains visible while scrolling continuously, and will only disappear when scrolling is idle for one second. The fade in/out time of the pie is controlled by CSS transition time.

2. Calculate and apply the angle within the pie

Once again, the bright red sector of the pie represents ‘consumed’ content of the article, while the dark red is what remains. In order to represent this on the pie at any given scroll position, we need to calculate the article content that has passed the bottom of the viewport as a percentage of the entire article content. Once we know this percentage variable, we can fairly easily work out how many degrees of a circle it equates to. From here, the spinner can be dynamically rotated by applying the degree amount with a CSS method:

// Calculate and apply degrees to pie
var percentage = ( ((scrnHeight-articleTop)+scrolled) / articleHeight ) * 100;
var degrees = (360 / 100) * percentage;
if (degrees <= 360) {
     spinner.css('transform', 'rotate(' + degrees + 'deg)');
}

3. Position the blocker appropriately

The blocker needs to know where to sit in order to display the pie angle correctly. If the calculated angle is over 180 degrees, the blocker will flip (instantly) to the opposite side of the pie, to continue working together with the spinner. Adding and removing a preset class, as done before, is the most manageable way of achieving this:

// If over 180deg, flip cover to hide spinner
if (degrees > 180) {
     blocker.addClass('overhalf');
} else {
     blocker.removeClass('overhalf');
}

So finally putting all our previous statements in the correct order, we have:

function progressPie() {
 
   var articleHeight = $('#article').outerHeight();
   var articleTop = $('#article').offset().top;
   var articleBottom = articleTop + articleHeight;
   var scrnHeight = $(window).height();
   var scrolled = $(window).scrollTop();
   var pie = $('#pie');
   var spinner = pie.children('#spinner');
   var blocker = pie.children('#blocker');
 
   // If #article is within screen boundary
   if( (articleTop-scrolled) < scrnHeight && articleBottom > scrolled) {
 
      // Pie appears only when scrolling
      if(pie.hasClass('active')) {
         clearTimeout(timer);
         pie.removeClass('active');
      }
      pie.addClass('active');
      timer = setTimeout(function(){
         pie.removeClass('active');
      }, 1000); // Hold for 1s
 
      // Calculate and apply degrees to pie
      var percentage = ( ((scrnHeight-articleTop)+scrolled) / articleHeight ) * 100;
      var degrees = (360 / 100) * percentage;
      if (degrees <= 360) {
         spinner.css('transform', 'rotate(' + degrees + 'deg)');
      }
 
      // If over 180deg, flip cover to hide spinner
      if (degrees > 180) {
         blocker.addClass('overhalf');
      } else {
         blocker.removeClass('overhalf');
      }
 
   } // end if
 
}
 
$(window).on('load', function(){ progressPie(); });
$(window).scroll ( function() {	progressPie(); });
$(window).resize ( function() {	progressPie(); });

And there you have it – a simple yet indicative visual of article consumption!

View Demo

Share on

Share on Facebook Share on Twitter Share on Google Plus Share on Pinterest

View more

CSS

Google

JavaScript

Lists

Mouse control

Multi-layered

Parallax

Scroll event

Visual feedback