button.btn Next
.container
- for (var i = 1; i <= 6; i++)
div(class='page page-' + i)
h1
span Page
| Transitions
View Compiled
@import url('https://fonts.googleapis.com/css?family=Lato|Dancing+Script');
$duration: .5s;
$ease: ease-out;
$animations: (
Move: (
Right: translate3d(100%, 0, 0),
Left: translate3d(-100%, 0, 0),
Top: translate3d(0, -100%, 0),
Bottom: translate3d(0, 100%, 0),
),
MoveFade: (
Right: (
transform: translate3d(100%, 0, 0),
opacity: 0.2
),
Left: (
transform: translate3d(-100%, 0, 0),
opacity: 0.2
),
Top: (
transform: translate3d(0, -100%, 0),
opacity: 0.2
),
Bottom: (
transform: translate3d(0, 100%, 0),
opacity: 0.2
)
),
);
// ---------------------------
// animations
@function is-map($var) {
@return type-of($var) == map
}
// Assign animation propertys & keyframe values from animations map.
@each $animationType, $value in $animations {
@each $prop, $val in $value {
@if is-map($val) == true {
.from#{$prop}Fade {
animation: from#{$prop}Fade $duration $ease;
}
@keyframes from#{$prop}Fade {
from { opacity: map-get($val, opacity); transform: map-get($val, transform) }
}
.to#{$prop}Fade {
animation: to#{$prop}Fade $duration $ease;
}
@keyframes to#{$prop}Fade {
from { }
to { opacity: map-get($val, opacity); transform: map-get($val, transform) }
}
}
@else {
.from#{$prop} {
animation: from#{$prop} $duration $ease;
}
@keyframes from#{$prop} {
from { transform: $val }
}
.to#{$prop} {
animation: to#{$prop} $duration $ease;
}
@keyframes to#{$prop} {
to { transform: $val }
}
}
}
}
// --------------------------
// pages
.page {
width: 100%;
height: 100%;
position: absolute;
text-align: center;
top: 0;
left: 0;
overflow: hidden;
visibility: hidden;
backface-visibility: hidden;
transform: translate3d(0,0,0);
transform-style: preserve-3d;
will-change: transform;
h1 {
font-size: 4.5rem;
font-weight: 700;
font-family: 'Dancing Script';
letter-spacing: 4px;
span {
color: rgba(0,0,0,0.3);
}
}
&--current {
visibility: visible;
z-index: 1;
}
}
.page-1 { background: #99B898; }
.page-2 { background: #FECEA8; }
.page-3 { background: #FF847C; }
.page-4 { background: #E84A5F; }
.page-5 { background: #05465A; }
.page-6 { background: #2A363B; }
// ---------------------------
// base
*, *::before, *::after {
box-sizing: border-box;
}
html, body { height: 100%; margin: 0; padding: 0;}
body {
font-family: 'Lato', Arial, sans-serif;
color: #fff;
background: #333;
overflow: hidden;
}
.container {
width: 100%;
height: 100vh;
position: relative;
perspective: 1200px;
}
.btn {
display: block;
position: absolute;
margin: 20px 0;
padding: 14px 20px;
width: 250px;
left: calc(50% - 125px);
top: 160px;
font-size: 14px;
font-weight: 700;
font-family: inherit;
letter-spacing: 1px;
text-transform: uppercase;
background: #fff;
color: #888;
cursor: pointer;
border: none;
border-radius: 4px;
outline: none;
z-index: 100;
box-shadow: 0 3px 0 rgba(0,0,0,0.1);
transform: translateY(0);
transition: 0.3s ease-out;
&:hover {
transform: translateY(-5px);
box-shadow: 0 3px 15px rgba(0,0,0,0.7);
}
}
View Compiled
const Transitions = (() => {
const pages = Array.from(document.querySelectorAll('.page'));
const btn = document.querySelector('.btn');
let pageIndex = 0;
let animationIndex = 0;
let isAnimating = false;
let currentPageEnded = false;
let nextPageEnded = false;
function init() {
if (pages.length < 2) {
throw new Error('Must be at least 2 pages to transition between.');
}
// store current classNames.
pages.forEach(page => page.data = {classList: page.getAttribute('class')});
pages[pageIndex].classList.add('page--current');
btn.addEventListener('click', function() {
if (isAnimating) return false;
if (animationIndex === animations.length) {
animationIndex = 0;
}
transitionPage(animationIndex);
++animationIndex;
});
}
function transitionPage(index) {
if (isAnimating) {
return false;
}
isAnimating = true;
// update currents value and add className to nextPage.
const currentPage = pages[pageIndex];
pageIndex = (pageIndex < pages.length - 1) ? ++pageIndex : 0 ;
const nextPage = pages[pageIndex];
nextPage.classList.add('page--current');
// Search through animations and get related classNames for anim at animation index.
const transitionType = animations[animationIndex];
const key = Object.keys(transitionType);
console.log(transitionType, key)
const {[key]: {inClass, outClass}} = transitionType;
// add eventListeners for page transitions
currentPage.addEventListener('animationend', function _() {
currentPage.removeEventListener('animationend', _);
currentPageEnded = true;
if (nextPageEnded) {
endAnimation(currentPage, nextPage);
}
});
currentPage.classList.add(...outClass);
nextPage.addEventListener('animationend', function _() {
nextPage.removeEventListener('animationend', _);
nextPageEnded = true;
if (currentPageEnded) {
endAnimation(currentPage, nextPage);
}
});
nextPage.classList.add(...inClass);
}
function endAnimation(outPage, inPage) {
currentPageEnded = false;
nextPageEnded = false;
isAnimating = false;
outPage.setAttribute(`class`, `${outPage.data.classList}`);
inPage.setAttribute(`class`, `${inPage.data.classList} page--current`);
}
const animations = [
{
fromRight: {
inClass: ['fromRight'],
outClass: ['toLeft']
}
},
{
fromLeft: {
inClass: ['fromLeft'],
outClass: ['toRight']
}
},
{
fromBottom: {
inClass: ['fromBottom'],
outClass: ['toTop']
}
},
{
fromTop: {
inClass: ['fromTop'],
outClass: ['toBottom']
}
},
{
fromRightFade: {
inClass: ['fromRightFade'],
outClass: ['toLeftFade']
}
},
{
fromLeftFade: {
inClass: ['fromLeftFade'],
outClass: ['toRightFade']
}
},
{
fromBottomFade: {
inClass: ['fromBottomFade'],
outClass: ['toTopFade']
}
},
{
fromTopFade: {
inClass: ['fromTopFade'],
outClass: ['toBottomFade']
}
},
];
init();
})();
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.