Skip to content

Instantly share code, notes, and snippets.

@adrianb93
Created August 17, 2015 11:34
Show Gist options
  • Save adrianb93/1db9d96ace9813f04fdb to your computer and use it in GitHub Desktop.
Save adrianb93/1db9d96ace9813f04fdb to your computer and use it in GitHub Desktop.
Simple parallax background effect.
// Simple parallax background effect by Adrian Brown
//
// Example HTML:
// <div class="parallax fullscreen"
// style="background-image: url(example.jpg);"
// data-img-width="1280"
// data-img-height="800"
// data-difference="100">
// </div>
//
// Requires jQuery.
var lastTime,
ticking,
scrolling,
resizing,
bypassHeightCheck,
bypassScrollCheck,
isTouch,
lastWindowW,
lastWindowH,
lastScrollY,
headerH; // This example is presuming there is a fixed <header> tag
// Resize background image on parallax elements
function backgroundResize() {
var windowH = lastWindowH,
fullscreenH = windowH - headerH,
threeQtrH = (windowH * 0.7) - headerH;
$(".parallax").each(function() {
var section = $(this),
imgW = section.attr("data-img-width"),
imgH = section.attr("data-img-height"),
conW = section.width(),
conH = section.height(),
ratio = imgW / imgH, // Width is always 100%
diff = parseFloat(section.attr("data-difference")),
remainingH = 0;
// Parallax distance (difference needed in image size)
diff = diff ? diff : 0;
// Resize section
section.css('max-height', fullscreenH); // Make sure height is less than viewport
if (section.hasClass('fullscreen')) { section.css("height", fullscreenH); }
if (section.hasClass('threeqtr')) { section.css("height", threeQtrH); }
// Remaining height to have fullscreen image only on parallax
if (!isTouch) { remainingH = windowH - conH; }
// Set img values depending on cont
imgH = conH + remainingH + diff;
imgW = imgH * ratio;
// Fix when too large
if (conW > imgW) {
imgW = conW;
imgH = imgW / ratio;
}
section.data("newheight", imgH); // Will need this for the parallax
section.css("background-size", imgW + "px " + imgH + "px");
});
}
// Run effect on all .parallax elements in view
function parallaxPosition() {
var windowH = lastWindowH,
scrollY = lastScrollY,
windowBottom = scrollY + windowH,
windowCurrent = (scrollY + windowBottom) / 2;
$(".parallax").each(function() {
var section = $(this),
height = section.height(),
top = section.offset().top,
bottom = top + height,
inView = scrollY < bottom && windowBottom > top;
if (inView) {
var imgH = section.data("newheight"),
min = 0,
max = - imgH + windowH,
overflowH = height < windowH ? imgH - height : imgH - windowH;
top = top - overflowH;
bottom = bottom + overflowH;
// Parallax value
var value = min + (max - min) * (windowCurrent - top) / (bottom - top);
// Set background-position
var horizontalAlign = "50%"; // Center align (default)
if (section.hasClass('left')) { horizontalAlign = "0%"; } // Left align
if (section.hasClass('right')) { horizontalAlign = "100%"; } // Right align
// Update background position
section.css("background-position", horizontalAlign + " " + value + "px");
}
});
}
// Flag all updates to occur on next update. Request's an update by default.
function flagForFullUpdate(trigger) {
trigger = typeof trigger !== 'undefined' ? !!trigger : true;
resizing = true;
scrolling = true;
bypassHeightCheck = true;
bypassScrollCheck = true;
if (trigger) { requestUpdate(); }
}
// Run an update
function update() {
if (resizing) {
var windowW = $(window).width();
var windowH = $(window).height();
if (windowW !== lastWindowW || windowH !== lastWindowH || bypassHeightCheck) {
if (windowH !== lastWindowH) {
flagForFullUpdate(false); // Need to update parallax bg y-position
}
lastWindowW = windowW;
lastWindowH = windowH;
backgroundResize();
}
}
if (scrolling) {
var scrollY = $(window).scrollTop();
if (scrollY !== lastScrollY || bypassScrollCheck) {
lastScrollY = scrollY;
if (!isTouch) { parallaxPosition(); }
}
}
// Update has finished, allow for further updates.
bypassHeightCheck = false;
bypassScrollCheck = false;
scrolling = false;
resizing = false;
ticking = false;
}
// Request an update
function requestUpdate() {
if (!ticking) {
window.requestAnimationFrame(update); // Seems to run smoother using this
ticking = true; // Prevent further updates
}
}
// Init
function initParallax() {
// Instantiate variables
ticking = false;
scrolling = false;
resizing = false;
bypassHeightCheck = false;
bypassScrollCheck = false;
isTouch = "ontouchstart" in window;
lastWindowW = $(window).width();
lastWindowH = $(window).height();
lastScrollY = $(window).scrollTop();
headerH = $("header").outerHeight();
// Enable parallax
if (!isTouch) { $(".parallax").each(function() { $(this).css("background-attachment", "fixed"); }); }
// requestAnimationFrame() support
if (!window.requestAnimationFrame) {
lastTime = 0;
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
// Start
flagForFullUpdate();
}
// Scroll function
function onScroll() {
scrolling = true;
requestUpdate();
}
// Resize function
function onResize() {
resizing = true;
requestUpdate();
}
// Event handlers
window.addEventListener('scroll', onScroll, false);
window.addEventListener('resize', onResize, false);
// Run the code
$(function() { initParallax(); });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment