We’ve been playing around the idea of replacing the refresh of a web page with an animation, that takes place while the new page content is loaded using Ajax. We used the pushState method to manipulate the browser history.
Inspiration came from this beautiful website: jardins-poudriere.ch.
Creating the structure
The HTML structure is composed of a <main>
element, wrapping the page content, a div.cd-cover-layer
which is used to create the layer covering the content during the page transition, and a div.cd-loading-bar
to create the loading bar animation.
<main>
<div class="cd-index cd-main-content">
<div>
<h1>Page Transition</h1>
<!-- your content here -->
</div>
</div>
</main>
<div class="cd-cover-layer"></div> <!-- this is the cover layer -->
<div class="cd-loading-bar"></div> <!-- this is the loading bar -->
Adding style
We used the body::before
and body::after
pseudo-elements to create the 2 blocks that cover the page content during the page transition: these elements are in fixed position, with height equal to 50vh and width equal to 100% of the viewport. By default, they are hidden outside the viewport using the CSS transform property (translateY(-100%)/translateY(100%)). When the user triggers a page transition, these elements are moved back into the viewport (using the .page-is-changing
class added to the <body>
element).
Here is a quick animation that shows the starting position of the body::before
, body::after
and the div.cd-loading-bar
elements (gif created in After Effects):
body::after, body::before {
/* these are the 2 half blocks which cover the content once the animation is triggered */
height: 50vh;
width: 100%;
position: fixed;
left: 0;
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.page-is-changing::after, body.page-is-changing::before {
transform: translateY(0);
}
The fade-out effect of the page content during the page transition is achieved animating the opacity of the div.cd-cover-layer
. It covers the entire .cd-main-content
element, has the same background-color, and its opacity is animated from 0 to 1 when the .page-is-changing
class is assigned to the <body>
.
The progress bar animation is created using the .cd-loading-bar::before
pseudo-element: by default, it is scaled down (scaleX(0) and transform-origin: left center), while it is scaled back up when the page transition is triggered (scaleX(1)).
.cd-loading-bar {
/* this is the loading bar - visible while switching from one page to the following one */
position: fixed;
height: 2px;
width: 90%;
}
.cd-loading-bar::before {
/* this is the progress bar inside the loading bar */
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.page-is-changing .cd-loading-bar::before {
transform: scaleX(1);
}
Smooth animations are achieved using CSS Transitions. We have been using a different transition-delay
value for each animated element in order to perform the different animations in the right order.
Events handling
We have been using the data-type="page-transition"
to target the links triggering the action. When a click event is detected, the changePage()
function is executed:
$('main').on('click', '[data-type="page-transition"]', function(event){
event.preventDefault();
//detect which page has been selected
var newPage = $(this).attr('href');
//if the page is not animating - trigger animation
if( !isAnimating ) changePage(newPage, true);
});
This function triggers the page animation and loads the new content (loadNewContent()
function):
function changePage(url, bool) {
isAnimating = true;
// trigger page animation
$('body').addClass('page-is-changing');
//...
loadNewContent(url, bool);
//...
}
When the new content is loaded, it replaces the old content inside the <main>
element, the .page-is-changing
class is removed from the body (to reverse the page animation) and the new loaded page is added to the window.history (using the pushState()
method).
function loadNewContent(url, bool) {
var newSectionName = 'cd-'+url.replace('.html', ''),
section = $('<div class="cd-main-content '+newSectionName+'"></div>');
section.load(url+' .cd-main-content > *', function(event){
// load new content and replace <main> content with the new one
$('main').html(section);
//...
$('body').removeClass('page-is-changing');
//...
if(url != window.location){
//add the new page to the window.history
window.history.pushState({path: url},'',url);
}
});
}
In order to trigger the same page animation when user clicks the browser back button, we listen for the popstate
event, and execute the changePage()
function when it is fired:
$(window).on('popstate', function() {
var newPageArray = location.pathname.split('/'),
//this is the url of the page to be loaded
newPage = newPageArray[newPageArray.length - 1];
if( !isAnimating ) changePage(newPage);
});
You can read more about the popstate event and how browsers handle it here.
Note: we implemented a basic load() function to upload new content, but you may want to replace it with, for example, a $.ajax call in order to handle errors, etc.