Skip to content

Instantly share code, notes, and snippets.

@kevnk
Last active May 3, 2023 15:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevnk/b95939076e1a31e1c87974c8f1e0f1f8 to your computer and use it in GitHub Desktop.
Save kevnk/b95939076e1a31e1c87974c8f1e0f1f8 to your computer and use it in GitHub Desktop.
Parallax directive for alpinejs
/**
* Installation and setup:
* 0. Install AlpineJS: `npm i -S alpinejs`
* 1. Make sure AlpineJS is running by adding `x-data` attribute on opening `<body>` tag
* 2. Add `x-parallax="path/to/image.jpg"` on element to add parallax bg image
*
* PS. this code was partially generated from ChatGPT 4
*/
// Helper function to load an image and return its natural dimensions
const loadImage = async (src) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
img.onerror = () => reject(new Error('Failed to load image'));
img.src = src;
});
};
Alpine.directive('parallax', (el, { expression }, { effect, cleanup }) => {
const src = expression;
// Set initial styles for the element
el.style.backgroundImage = `url(${src})`;
el.style.backgroundAttachment = 'fixed';
el.style.backgroundPosition = 'center';
el.style.backgroundRepeat = 'no-repeat';
// Function to handle scroll event
const scrollSpeed = 0.5; // Adjust this value to change the parallax speed (0.5 means 50% slower than scroll)
const onScroll = () => {
const yPos = -(window.scrollY - el.offsetTop) * scrollSpeed;
el.style.backgroundPosition = `center ${yPos}px`;
};
onScroll();
const onResize = async () => {
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
const extraHeight = windowHeight * scrollSpeed; // Calculate extra height needed for the parallax effect
try {
// Load image and get natural dimensions
const { width: naturalWidth, height: naturalHeight } = await loadImage(src);
// Calculate aspect ratio and target dimensions
const aspectRatio = naturalWidth / naturalHeight;
let targetHeight = windowHeight + extraHeight;
let targetWidth = targetHeight * aspectRatio;
// If the target width is less than the window width, adjust the dimensions
if (targetWidth < windowWidth) {
targetWidth = windowWidth;
targetHeight = targetWidth / aspectRatio;
}
el.style.backgroundSize = `${targetWidth}px ${targetHeight}px`;
} catch (error) {
console.error('Error:', error.message);
}
};
// Add event listeners and run effects
effect(() => {
onResize(); // Set initial background size
window.addEventListener('resize', onResize);
window.addEventListener('scroll', onScroll);
});
// Clean up when directive is removed or the element is removed from the DOM
cleanup(() => {
window.removeEventListener('resize', onResize);
window.removeEventListener('scroll', onScroll);
});
});
Alpine.start();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment