Skip to content

Instantly share code, notes, and snippets.

@guillaumepiot
Last active August 14, 2017 13:58
Show Gist options
  • Save guillaumepiot/8fd21410f5bbc40166e981f7b553d5fe to your computer and use it in GitHub Desktop.
Save guillaumepiot/8fd21410f5bbc40166e981f7b553d5fe to your computer and use it in GitHub Desktop.
parallax.js
// For a given HTML snippet with the class "parallax"
//
// <div class="parallax" style="height: 400px; overflow: hidden;">
// <div style="background-image:url('http://via.placeholder.com/1600x780');"></div>
// </div>
(function(){
var speedRatio = 4
var minYOffset = -200
// All parallaxable elements on the page will be registered here
var parallaxElms = []
function scrollHandler () {
var winHeight = window.innerHeight
var scrollY = window.scrollY
parallaxElms.forEach(function (elm) {
// Get the position of the image container relatively to the page viewport
var r = elm['elm'].getBoundingClientRect()
elmTop = r.top
elmBottom = r.bottom
elmHeight = r.height
// Calculate the amount of translation available based in the height
// of the image minus the height of the image container
var translatableRange = elm.imageHeight - elmHeight
// Prevent the parallaxing effect when the image container if off screen
if (elmTop > winHeight) {
return
}
if (elmBottom < 0) {
return
}
// Calculate the negative percentage of the container position in
// relation to the view port. When the top of the image container
// appears at the bottom of the page, it's 100%. When the bottom of the
// image container go over the top of the page, it's 0%.
var perc = (elmBottom / (winHeight + elmHeight))
// Workout the translation amount to do based on the percentage of
// travel of the image container from the bottom to the top of the page
var y = - (perc * translatableRange)
// Apply the transation the inner div containing the actual image
elm['elm'].children[0].style.transform = "translate3d(0px, "+ y +"px, 0px)";
})
}
function initParallax () {
var elms = document.querySelectorAll('.parallax')
var numOfThings = elms.length
var currentThings = 0
// Create a function that counts the amount of callback it expects to
// receive, only call the final function when all have returned.
function callback() {
currentThings++
if (currentThings === numOfThings) {
resizeHandler()
}
}
// When we first load the page, measure the size of each background
// image
elms.forEach(function (elm) {
// Get the background image url from the element style
imageUrl = elm.children[0].style.backgroundImage.slice(5, -2)
img = new Image()
// Wait for the image to be loaded, then add its dimensions to the
// parallax elements array.
img.addEventListener('load', function(){
parallaxElms.push({
elm: elm,
imageOriginalHeight: this.height,
imageOriginalWidth: this.width,
})
callback()
})
// Assigning the image source will trigger the image load.
img.src = imageUrl
})
document.addEventListener('scroll', scrollHandler)
window.addEventListener('resize', resizeHandler)
}
function resizeHandler () {
// Every time we resize the window, calculate the display height of
// the image in relation to the width of its container. That way, we always
// optimise the amount of scroll available.
parallaxElms.forEach(function (elm) {
containerWidth = elm.elm.offsetWidth
var ratio = containerWidth / elm.imageOriginalWidth
var imageDisplayHeight = elm.imageOriginalHeight * ratio
elm.imageHeight = imageDisplayHeight
elm.elm.children[0].style.height = imageDisplayHeight + 'px'
})
scrollHandler()
}
/////////////////////////////////////////////////////////////////////////////
// Bootstrap any file uploaders
function documentReady () {
return (document.readyState === 'interactive' || document.readyState === 'complete')
}
function bootstrap () {
document.removeEventListener('readystatechange', bootstrap)
initParallax()
}
if (documentReady()) {
bootstrap()
} else {
document.addEventListener('readystatechange', bootstrap)
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment