From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
Today we’d like to show you how to create a simple and modern page transition effect. Maybe you have spotted some of the cool effects seen on sites like Nation or Keep Portland Weird, where two or more layers of overlays are animated across the page to reveal some new content. Let’s see how we can do something like that. We’ll focus on the animation part only, not the loading of content. For the effects we’ll be using CSS Animations.
The Markup
For our simple demo, we’ll create a full width and height layout with some arrows that will trigger the overlay animations. The idea is to show a page and in the middle of an overlay animation, switch to another page. While we will be just working with static content, you could plug your dynamic solution in here that fetches some data on the fly.
Our markup looks as follows:
<body>
<main class="container">
<div class="pages">
<div class="page page--current">
<!-- intro page content -->
</div><!-- /page -->
<div class="page">
<!-- some other page -->
</div>
</div><!-- /pages -->
<nav class="pagenav">
<!-- buttons that will trigger the overlay animations -->
</nav>
</main>
</body>
The structure for the overlay will be inserted with JavaScript. We’ll work with a division that is placed as a fixed element on top of everything else. Depending on the direction, we’ll give that element some classes that will help us apply styles properly. The overlay will contain a number of layers that we will be able to define. Each of the layers will have a background color that we’ll set and also a specific animation in our stylesheet.
One example for how the “revealer” might look:
<div class="revealer revealer--right">
<div class="revealer__layer"></div>
<div class="revealer__layer"></div>
<div class="revealer__layer"></div>
</div>
Each layer will additionally have its background color set. In order to create unique effects, we want to animate each layer individually. For that we set the parent initially to an off-screen position and then animate the layers in. This initial setting of the main element is done in our script (see below). The animation behavior of the layers is then defined in our stylesheet depending on the effect class, along with other styles for the page to be revealed.
The CSS
First, let’s take a look at some general styles for our body, container and pages (vendor prefixes omitted):
html,
body {
min-height: 100vh;
overflow-x: hidden;
}
.js .container {
position: relative;
height: 100vh;
overflow: hidden;
}
.js .pages {
position: relative;
overflow: hidden;
width: 100vw;
height: 100vh;
z-index: 0;
}
.page {
padding: 6.5em;
background: #66c6ff;
display: flex;
flex-direction: column;
}
.js .page {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
visibility: hidden;
z-index: 1;
}
.js .page--current {
visibility: visible;
position: relative;
}
We want our page to be full width and height and hide any overflow. By default, and with JS (we use Modernizr), the pages are hidden and the current class sets the respective one to be visible. We want to make sure that our layout is functional without JavaScript, so we add the “conditional” styles.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
Let’s see how the style for the revealer looks like. So we want to place the revealer on top of everything, with a fixed position:
.revealer {
width: 100vw;
height: 100vh;
position: fixed;
z-index: 1000;
pointer-events: none;
}
Depending on which direction we are currently using, we need to set some initial positioning styles. In our script, as we will see in a bit, we will set some other transforms in order to insure that the top side of our revealer is always the one facing towards the screen. This will simplify the effects because the inner layer animations will always be the same (moving upwards), so we don’t need to define new animations for each direction if the parent is rotated and positioned correctly:
.revealer--cornertopleft,
.revealer--cornertopright,
.revealer--cornerbottomleft,
.revealer--cornerbottomright {
top: 50%;
left: 50%;
}
.revealer--top,
.revealer--bottom {
left: 0;
}
.revealer--right,
.revealer--left {
top: 50%;
left: 50%;
}
.revealer--top {
bottom: 100%;
}
.revealer--bottom {
top: 100%;
}
The layers will have a default background color which will be then targeted individually for each effect in a dynamic way:
.revealer__layer {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: #ddd;
}
Next, let’s check out an example effect. The three layer effect adds an animation to each layer with a special easing function. The anim–effect class is added to the body so that we know which effect we are currently using. The revealer–animate is given to the revealer in order to trigger the animations.
As we can see here, we are adding the same animation properties to all the layers, except for the animation-name. With three different animations, we define the delays of each within the keyframes. Like that we ensure that all animations end in the same moment but allow for different appearance timings:
.anim--effect-3 .page:nth-child(2) {
background: #F3A3D3;
}
.anim--effect-3 .revealer--animate .revealer__layer {
animation: anim-effect-3-1 1.5s cubic-bezier(0.550, 0.055, 0.675, 0.190) forwards;
}
.anim--effect-3 .revealer--animate .revealer__layer:nth-child(2) {
animation-name: anim-effect-3-2;
}
.anim--effect-3 .revealer--animate .revealer__layer:nth-child(3) {
animation-name: anim-effect-3-3;
}
@keyframes anim-effect-3-1 {
0% {
transform: translate3d(0, 0, 0);
}
25%, 75% {
transform: translate3d(0, -100%, 0);
animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
}
100% {
transform: translate3d(0, -200%, 0);
}
}
@keyframes anim-effect-3-2 {
0%, 12.5% {
transform: translate3d(0, 0, 0);
}
37.5%, 62.5% {
transform: translate3d(0, -100%, 0);
animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
}
87.5%, 100% {
transform: translate3d(0, -200%, 0);
}
}
@keyframes anim-effect-3-3 {
0%, 25% {
transform: translate3d(0, 0, 0);
animation-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
75%, 100% {
transform: translate3d(0, -200%, 0);
}
}
What we do here is to make the first layer move first but then “pause” in the middle until 75% while on top of the visible screen. Then we move it to the opposite direction to reveal the new content. The same happens for the other two layers, just that here we start them at different keyframes, decreasing the “pause”. The last layer does not have a pause at all, it just goes through to the other side, starting at 25%.
The JavaScript
We did a little plugin that will create the revealer depending on some options. The default options are the following:
Revealer.prototype.options = {
// total number of revealing layers (min is 1)
nmbLayers : 1,
// bg color for the revealing layers
bgcolor : '#fff',
// effect classname
effect : 'anim--effect-1',
// callback
onStart : function(direction) { return false; },
// callback
onEnd : function(direction) { return false; }
};
The function for adding the revealer with its layers and respective effect class looks as follows:
Revealer.prototype._addLayers = function() {
this.revealerWrapper = document.createElement('div');
this.revealerWrapper.className = 'revealer';
classie.add(bodyEl, this.options.effect);
var strHTML = '';
for(var i = 0; i < this.options.nmbLayers; ++i) {
var bgcolor = typeof this.options.bgcolor === 'string' ? this.options.bgcolor : (this.options.bgcolor instanceof Array && this.options.bgcolor[i] ? this.options.bgcolor[i] : '#fff');
strHTML += '
<div style="background:' + bgcolor + '" class="revealer__layer"></div>
';
}
this.revealerWrapper.innerHTML = strHTML;
bodyEl.appendChild(this.revealerWrapper);
};
The most crucial function sets the initial positioning of our reveal element and adds the control classes for triggering the animation and for the chosen effect.
Depending on which direction we choose, we need to make sure that the reveal element gets the right transforms. Remember, we are simply rotating and positioning the revealer in a way so that the top side is always faced towards the screen so that the layers always move upwards. For the corner cases we need to make sure that the width and height of the revealer is set properly to the diagonal of the page. For the left and right case, we need to make sure that the height of the revealer is the width of the screen because we are rotating it 90 degrees.
Revealer.prototype.reveal = function(direction, callbacktime, callback) {
// if animating return
if( this.isAnimating ) {
return false;
}
this.isAnimating = true;
// current direction
this.direction = direction;
// onStart callback
this.options.onStart(this.direction);
// set the initial position for the layers´ parent
var widthVal, heightVal, transform;
if( direction === 'cornertopleft' || direction === 'cornertopright' || direction === 'cornerbottomleft' || direction === 'cornerbottomright' ) {
var pageDiagonal = Math.sqrt(Math.pow(winsize.width, 2) + Math.pow(winsize.height, 2));
widthVal = heightVal = pageDiagonal + 'px';
if( direction === 'cornertopleft' ) {
transform = 'translate3d(-50%,-50%,0) rotate3d(0,0,1,135deg) translate3d(0,' + pageDiagonal + 'px,0)';
}
else if( direction === 'cornertopright' ) {
transform = 'translate3d(-50%,-50%,0) rotate3d(0,0,1,-135deg) translate3d(0,' + pageDiagonal + 'px,0)';
}
else if( direction === 'cornerbottomleft' ) {
transform = 'translate3d(-50%,-50%,0) rotate3d(0,0,1,45deg) translate3d(0,' + pageDiagonal + 'px,0)';
}
else if( direction === 'cornerbottomright' ) {
transform = 'translate3d(-50%,-50%,0) rotate3d(0,0,1,-45deg) translate3d(0,' + pageDiagonal + 'px,0)';
}
}
else if( direction === 'left' || direction === 'right' ) {
widthVal = '100vh'
heightVal = '100vw';
transform = 'translate3d(-50%,-50%,0) rotate3d(0,0,1,' + (direction === 'left' ? 90 : -90) + 'deg) translate3d(0,100%,0)';
}
else if( direction === 'top' || direction === 'bottom' ) {
widthVal = '100vw';
heightVal = '100vh';
transform = direction === 'top' ? 'rotate3d(0,0,1,180deg)' : 'none';
}
this.revealerWrapper.style.width = widthVal;
this.revealerWrapper.style.height = heightVal;
this.revealerWrapper.style.WebkitTransform = this.revealerWrapper.style.transform = transform;
this.revealerWrapper.style.opacity = 1;
// add direction and animate classes to parent
classie.add(this.revealerWrapper, 'revealer--' + direction || 'revealer--right');
classie.add(this.revealerWrapper, 'revealer--animate');
// track the end of the animation for all layers
var self = this, layerscomplete = 0;
this.layers.forEach(function(layer) {
onEndAnimation(layer, function() {
++layerscomplete;
if( layerscomplete === self.options.nmbLayers ) {
classie.remove(self.revealerWrapper, 'revealer--' + direction || 'revealer--right');
classie.remove(self.revealerWrapper, 'revealer--animate');
self.revealerWrapper.style.opacity = 0;
self.isAnimating = false;
// callback
self.options.onEnd(self.direction);
}
});
});
// reveal fn callback
if( typeof callback === 'function') {
if( this.callbacktimeout ) {
clearTimeout(this.callbacktimeout);
}
this.callbacktimeout = setTimeout(callback, callbacktime);
}
};
And that’s all folks, we hope you enjoyed this little effect and get inspired.
References and Credits
- select-css by Filament Group
- Arrow icon made by Madebyoliver from www.flaticon.com is licensed by CC 3.0 BY
Thank you , it’s very impressive and the design is cool .I’ll use it
Perfecto…saluto…sempurno 😛
Wow ! Amazing design animation ! congratz 🙂
Awesome…always its worth visiting this site.
Your work is always very beautiful and very functional continue your good work it is very inspiring
I really love your work.
keep inspiring!
I am a big fan of your work. thank you for all your posts
I really appreciate it
I’m pretty much of a newbie and I couldn’t find a way to use this in conjunction with smoothState.js. Would you give me some tips, pretty please?
Awesome work!
But how to add multiple pages?
Amazing…. Can we use
widthVal = winsize.height + "px"; heightVal = winsize.width + "px";
instead of
widthVal = '100vh' heightVal = '100vw';
for cross browser support?
I’ve tried to use this on my shopify page but can’t seem to get it to work..
think it’s because it uses a custom modernizr.. if you get a chance, or if you have any idea on how to work a way around the problem.. i’ll appreciate any help..
other than that, your work is amazing 😀
Great tutorial! Is there a way to implement this between actual pages and not just sections within a page? If someone could point me in the right direction, I’d be very thankful!
It’s pretty painful to read all this and then not be able to add more than one slide/page 🙁
You can do it by changing:
…
onStart : function(direction) { ... var nextPage = pages[currentPage === 0 ? 0 : currentPage]; ... }, onEnd : function(direction) { ... var nextPage = pages[currentPage === 0 ? pages.length -1 : 0]; ... } ... function reveal(direction) { ... currentPage = currentPage < pages.length -1 ? currentPage+1 : 0; ... }
@Barbara I tried the solution that you post under and it works. But it’s just an ordered navigation, is there a way to direct every icon to an specific page and not just randomly?
Thanks
I was just wondering where/how I should be editing this segment in. I was playing around with it and cant get it to work 😐
@Barbara Nice mod, worked perfectly for multiple pages!
So cool ! Thanks
Great tutorials
Hey is it that we can apply transitions to a change <a href=”I change page.html” rel=”nofollow”> I change page </a> ?
thank you
Wow this is absolutely magnificent effect, really need to implmant that in some wordpress themes 🙂
very cool stuff! thanks a lot.
in the css part, the “animation-timing-function” property is repeated in two places:
– as the second value of the “animation” property (short form)
– in the @keyframe rule
I understand the first one, but I can’t find an other example where it is used inside an @keyframe rule.
Could you explain why it is here?
Thank you
Great stuff! How can I combine this with angular-route???
thank you. most beatiful desing
Wonderful tutorial!! All these themes, design are coded for WordPress and this design is a crucial importance and the strategic use of Presentation is virtually perceptible by touch impression in a scheme that reflects a professional edge and a form that is more attractive to the Viewers than a simple inbound without changes.
Hi! Amazing work!
can somebody please tell me if and how its possible to import this into a wordpress template?
thanks a lot!
I can’t figure out how to get this to work with more than two pages. Any tips?
Wow, what a great multi-layer page reveal effects
Thanks for sharing the tutorial, great work!
How to open specify page if there are more than one page? thanks!!
A wonderful article and a fantastic piece of code. It’s incredible how such a little movement and utilization of layers can hugely change the way in which users navigate a website. It is UI that is at the heart of any web design and I believe these little elements can make all the changes . . .
Hi very nice effect but impossible to put another pages. What can i do
Is anyone else having this issue? The demo page at http://tympanus.net/Tutorials/PageRevealEffects/ is returning a 404. Would you please update the link? Thanks
Hi Rachel, sorry about that. We will fix it soon!
Thanks, Pedro. The link is working now. Very good tutorial and good explanation for the example.
Thanks! Very awesome effect. We can’t wait to try!
How about using it by opening other pages. BTW. Thanks for the awesome effect.
How would one go about using this for a home page pre-loading animation and just fire this on page load?
This is an amazing plugin man I been looking for this. I have a question is it possible to integrate “Content Move” effect with fullpage.js which is a one page scroll which can be found here http://www.alvarotrigo.com/fullPage/#firstPage.
I really need this for a school project and to be clear all I am trying to achieve is the ” Content Move” effect to be activated on scroll when scrolling Up and Down?
Thanks in advance
How to add more than two pages?
Awesome work!
Pls tell us how to link transition arrows to other index.html, to show different content on each arrow 😀
I also was wondering how to do this so that a menu can be created, Does anyone know how to do this?
nice
How do I create more pages?
Amazing tutorial!
Please tell me how to add more pages?
Does anyone know how I use background image on pages?
I have a problem with this effect
tanks for the great tutorial. but how can I point the buttons to specific page?
Hello, thank you for a plugin, it is a good job.
If you don’t mind I have a question, could you help me, please?
I have tried to connect my navbar and logo(homepage) clicks to this plugin, but have a problem with the code, it will be great to solve this problem, Thank you very much.
Here is the code by which I connected navbar and logo(homepage) to the plugin.
var pages = [].slice.call(document.querySelectorAll(‘.pages > .page’)),
currentPage = 0,
revealerOpts = {
// the layers are the elements that move from the sides
nmbLayers : 3,
// bg color of each layer
bgcolor : [‘#52b7b9’, ‘#ffffff’, ‘#53b7eb’],
// effect classname
effect : ‘anim–effect-3’
};
revealer = new Revealer(revealerOpts);
// clicking the page nav
var navbar = document.getElementById(“navbar”).getElementsByTagName(“a”);
for (var i = 0; i < navbar.length; i++) {
navbar[i].addEventListener('click', function() {n = i; reveal('top'); });
}
// triggers the effect by calling instance.reveal(direction, callbackTime, callbackFn)
function reveal(direction) {
var callbackTime = 750;
callbackFn = function() {
// this is the part where is running the turning of pages
classie.remove(pages[currentPage], 'page–current');
currentPage = n;
classie.add(pages[currentPage], 'page–current');
};
revealer.reveal(direction, callbackTime, callbackFn);
}
how can we have more than two page which navigates to there by a menu?
Please help!!!!!!!
Can anyone help out to add more pages in these tutorial