<div class="btns">
<button class="btn js-btn1">Scroll to section 1</button>
<button class="btn js-btn2">Scroll to section 2</button>
<button class="btn js-btn3">Scroll to section 3</button>
<button class="btn js-btn500">Scroll to position 500px</button>
<button class="btn js-btn50000">Scroll to position 50000px (on purpose longer than document)</button>
</div>
<div class="section js-section1">JS - Section 1</div>
<div class="section js-section2">JS - Section 2</div>
<div class="section js-section3">JS - Section 3</div>
@import "https://fonts.googleapis.com/css?family=Pacifico";
* {
margin: 0;
box-sizing: border-box;
}
.btns {
position: fixed;
top: 1rem;
left: 1rem;
right: 1rem;
text-align: center;
display: flex;
justify-content: center;
}
.section {
width: 100%;
height: 100vh;
line-height: 100vh;
text-align: center;
background-size: cover;
font-size: 5vw;
text-shadow: 1px 1px 1px rgba(255,255,255,.25);
&.js-section1 {
background-image: url(https://images.pexels.com/photos/895228/pexels-photo-895228.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260);
}
&.js-section2 {
background-image: url(https://images.pexels.com/photos/551579/pexels-photo-551579.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260);
}
&.js-section3{
background-image: url(https://images.pexels.com/photos/569170/pexels-photo-569170.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260)
}
}
.btn{
position: relative;
padding: 10px 20px;
margin: 0 5px;
float: left;
border-radius: 10px;
font-family: 'Pacifico', cursive;
font-size: 18px;
color: #FFF;
text-decoration: none;
transition: all 0.1s;
background-color: #3498DB;
border-bottom: 5px solid #2980B9;
text-shadow: 0px -2px #2980B9;
}
.btn:active{
transform: translate(0px,5px);
border-bottom: 1px solid;
}
View Compiled
// Browser support:
// Chrome >= 24
// Firefox >= 23
// IE >= 10
// Opera >= 15
// Safari >= 8 (on previous versions it breaks on 'now' in window.performance)
// Android 4.4
// Firefox >= 23
// IE Mobile >= 10
// Opera Mobile >= 15
// Safari iOS >= 9
// Chrome for Android >= 35
/**
*
* @param {(number|HTMLElement)} destination - Destination to scroll to (DOM element or number)
* @param {number} duration - Duration of scrolling animation
* @param {string} easing - Timing function name (Allowed values: 'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic', 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint', 'easeOutQuint', 'easeInOutQuint')
* @param {function} callback - Optional callback invoked after animation
*/
function scrollIt(destination, duration = 200, easing = 'linear', callback) {
// Predefine list of available timing functions
// If you need more, tween js is full of great examples
// https://github.com/tweenjs/tween.js/blob/master/src/Tween.js#L421-L737
const easings = {
linear(t) {
return t;
},
easeInQuad(t) {
return t * t;
},
easeOutQuad(t) {
return t * (2 - t);
},
easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
easeInCubic(t) {
return t * t * t;
},
easeOutCubic(t) {
return (--t) * t * t + 1;
},
easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
easeInQuart(t) {
return t * t * t * t;
},
easeOutQuart(t) {
return 1 - (--t) * t * t * t;
},
easeInOutQuart(t) {
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
},
easeInQuint(t) {
return t * t * t * t * t;
},
easeOutQuint(t) {
return 1 + (--t) * t * t * t * t;
},
easeInOutQuint(t) {
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
}
};
// Store initial position of a window and time
// If performance is not available in your browser
// It will fallback to new Date().getTime() - thanks IE < 10
const start = window.pageYOffset;
const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();
// const startTime = typeof(window.performance['now']) == 'function' ? performance.now() : new Date().getTime();
// Take height of window and document to sesolve max scrollable value
// Prevent requestAnimationFrame() from scrolling below maximum scollable value
// Resolve destination type (node or number)
const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight;
const destinationOffset = typeof destination === 'number' ? destination : destination.offsetTop;
const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset);
// If requestAnimationFrame is not supported
// Move window to destination position and trigger callback function
if ('requestAnimationFrame' in window === false) {
window.scroll(0, destinationOffsetToScroll);
if (callback) {
callback();
}
return;
}
// function resolves position of a window and moves to exact amount of pixels
// Resolved by calculating delta and timing function choosen by user
function scroll() {
const now = 'now' in window.performance ? performance.now() : new Date().getTime();
const time = Math.min(1, ((now - startTime) / duration));
const timeFunction = easings[easing](time);
window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start));
// Stop requesting animation when window reached its destination
// And run a callback function
if (window.pageYOffset === destinationOffsetToScroll) {
if (callback) {
callback();
}
return;
}
// If window still needs to scroll to reach destination
// Request another scroll invokation
requestAnimationFrame(scroll);
}
// Invoke scroll and sequential requestAnimationFrame
scroll();
}
// Scroll to section 1
document.querySelector('.js-btn1').addEventListener('click', () => {
scrollIt(
document.querySelector('.js-section1'),
300,
'easeOutQuad',
() => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
);
});
// Scroll to section 2
document.querySelector('.js-btn2').addEventListener('click', () => {
scrollIt(
document.querySelector('.js-section2'),
300,
'easeOutQuad',
() => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
);
});
// Scroll to section 3
document.querySelector('.js-btn3').addEventListener('click', () => {
scrollIt(
document.querySelector('.js-section3'),
300,
'easeOutQuad',
() => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
);
});
// Scroll to 500px from top
document.querySelector('.js-btn500').addEventListener('click', () => {
scrollIt(
500,
300,
'easeOutQuad',
() => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
);
});
// Scroll to 50000px from top (on purpose longer than document)
document.querySelector('.js-btn50000').addEventListener('click', () => {
scrollIt(
50000,
300,
'easeOutQuad',
() => console.log(`Just finished scrolling to ${window.pageYOffset}px`)
);
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.