Skip to content

Instantly share code, notes, and snippets.

@CodeMyUI
Created December 17, 2019 05:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CodeMyUI/ae830d4e8e24bd6d4ad7b4137a624d4d to your computer and use it in GitHub Desktop.
Save CodeMyUI/ae830d4e8e24bd6d4ad7b4137a624d4d to your computer and use it in GitHub Desktop.
Efficient Scroll Zoom

Efficient Scroll Zoom

This method utilizes the Intersection Observer to zoom the image only when it is in view. This cuts down on the processing power required for this effect.

A Pen by Chris Weissenberger on CodePen.

License.

<div class="background">
<div class="container">
<div class="image">
<img src="https://source.unsplash.com/random/1" data-scroll-zoom />
</div>
<div class="image">
<img src="https://source.unsplash.com/random/2" data-scroll-zoom />
</div>
<div class="image">
<img src="https://source.unsplash.com/random/3" data-scroll-zoom />
</div>
<div class="image">
<img src="https://source.unsplash.com/random/4" data-scroll-zoom />
</div>
<div class="image">
<img src="https://source.unsplash.com/random/5" data-scroll-zoom />
</div>
<div class="image">
<img src="https://source.unsplash.com/random/6" data-scroll-zoom />
</div>
<div>
<p>This method utilizes the Intersection Observer to zoom the image <em>only when it is in view,</em> which cuts down on the processing power required for this effect.</p>
</div>
</div>
</div>
// Higher number = more zoom
let scaleAmount = 0.5;
function scrollZoom() {
const images = document.querySelectorAll("[data-scroll-zoom]");
let scrollPosY = 0;
scaleAmount = scaleAmount / 100;
const observerConfig = {
rootMargin: "0% 0% 0% 0%",
threshold: 0
};
// Create separate IntersectionObservers and scroll event listeners for each image so that we can individually apply the scale only if the image is visible
images.forEach(image => {
let isVisible = false;
const observer = new IntersectionObserver((elements, self) => {
elements.forEach(element => {
isVisible = element.isIntersecting;
});
}, observerConfig);
observer.observe(image);
// Set initial image scale on page load
image.style.transform = `scale(${1 + scaleAmount * percentageSeen(image)})`;
// Only fires if IntersectionObserver is intersecting
window.addEventListener("scroll", () => {
if (isVisible) {
scrollPosY = window.pageYOffset;
image.style.transform = `scale(${1 +
scaleAmount * percentageSeen(image)})`;
}
});
});
// Calculates the "percentage seen" based on when the image first enters the screen until the moment it leaves
// Here, we get the parent node position/height instead of the image since it's in a container that has a border, but
// if your container has no extra height, you can simply get the image position/height
function percentageSeen(element) {
const parent = element.parentNode;
const viewportHeight = window.innerHeight;
const scrollY = window.scrollY;
const elPosY = parent.getBoundingClientRect().top + scrollY;
const borderHeight = parseFloat(getComputedStyle(parent).getPropertyValue('border-bottom-width')) + parseFloat(getComputedStyle(element).getPropertyValue('border-top-width'));
const elHeight = parent.offsetHeight + borderHeight;
if (elPosY > scrollY + viewportHeight) {
// If we haven't reached the image yet
return 0;
} else if (elPosY + elHeight < scrollY) {
// If we've completely scrolled past the image
return 100;
} else {
// When the image is in the viewport
const distance = scrollY + viewportHeight - elPosY;
let percentage = distance / ((viewportHeight + elHeight) / 100);
percentage = Math.round(percentage);
return percentage;
}
}
}
scrollZoom();
body {
margin: 0;
}
.background {
align-items: center;
background: #04FEBA;
display: flex;
height: 100%;
justify-content: center;
width: 100vw;
}
.container {
align-items: center;
display: flex;
flex-direction: column;
height: 600vh;
justify-content: space-around;
text-align: center;
text-transform: uppercase;
width: 100vmin;
p {
font-family: Merriweather, sans-serif;
font-size: 20px;
line-height: 1.5;
max-width: 70vmin;
text-align: justify;
text-transform: none;
}
}
.image {
background: white;
box-shadow: 3px 10px 10px rgba(0, 0, 0, 0.25);
border: 15px solid white;
border-width: 1vmin 1vmin 10vmin 1vmin;
height: 70vmin;
overflow: hidden;
width: 70vmin;
img {
height: 100%;
object-fit: cover;
width: 100%;
}
}
<link href="https://fonts.googleapis.com/css?family=Merriweather&amp;display=swap" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment