Reputation: 248
I'm trying to make a parallax effect similar to the hover-over effects on the images on this site: https://www.framer.com/gallery/categories/portfolio
and here is the code I have so far:
document.querySelectorAll('.image-container').forEach(item => {
const img = item.querySelector('img');
item.addEventListener('mousemove', (e) => {
const rect = item.getBoundingClientRect();
const mouseY = e.clientY - rect.top; // Mouse Y position relative to the item
const halfHeight = rect.height / 2; // Midpoint of the element
const imgHeight = img.height - rect.height
const mousePercentage = mouseY/imgHeight*100
img.style.transform = `translateY(-${mousePercentage}px)`; // Move up
img.style.transition = "transform 0s ease-out"
});
item.addEventListener('mouseleave', () => {
img.style.transform = 'translateY(0)'; // Reset position when mouse leaves
img.style.transition = " transform 2s ease-out"
});
item.addEventListener('mouseenter', () =>{
img.style.transition = "transform 2s ease-out"
})
});
#display-grid{
display: grid;
width: 80%;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(24%, 1fr));
}
.cell{
border: 1px solid #EEEEEE;
border-radius: 1rem;
width: 100%;
overflow-y: hidden;
position: relative;
overflow: hidden;
border-radius: 12px;
aspect-ratio: 1 / 1.2;
}
.cell .image-container{
width: 100%;
height: 90%;
position: relative;
aspect-ratio: 1 / 1;
border-radius: 1rem;
overflow-y: hidden;
}
.cell img {
width: 100%;
height: auto;
object-fit: cover;
object-position: top;
border-radius: 1rem;
}
<div id="display-grid">
<div class="cell">
<div class="image-container">
<img id="website-preview" src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024">
</div>
<p>Image Title</p>
</div>
<div class="cell">
<div class="image-container">
<img id="website-preview" src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024">
</div>
<p>InfoSwap</p>
</div>
</div>
I have made it as far as having the image scroll within the frame up and down a variable % depending on where the mouse is in the image-container, but I find when the mouse enters the image-container, the image jumps to the coordinates. If I add a transition time to my stylesheet or to the "mousemove" Event Listener, I find the image waits for the mouse to stop moving before slowly moving to where the image needs to be.
What can I do so that the image moves smoothly and consistently on entry, moving, and leaving the container?
Upvotes: 1
Views: 128
Reputation: 3088
You're close to achieving the same effect seen on the provided site. You just need to change the "mouse move" state part, inside the JavaScript, from this:
img.style.transition = "transform 0s ease-out"
to this:
img.style.transition = "transform 2s ease-out"
Like so:
document.querySelectorAll('.image-container').forEach(item => {
const img = item.querySelector('img');
item.addEventListener('mousemove', (e) => {
const rect = item.getBoundingClientRect();
const mouseY = e.clientY - rect.top; // Mouse Y position relative to the item
const halfHeight = rect.height / 2; // Midpoint of the element
const imgHeight = img.height - rect.height
const mousePercentage = mouseY/imgHeight*100
img.style.transform = `translateY(-${mousePercentage}px)`; // Move up
img.style.transition = "transform 2s ease-out" // this is the changed part
});
item.addEventListener('mouseleave', () => {
img.style.transform = 'translateY(0)'; // Reset position when mouse leaves
img.style.transition = " transform 2s ease-out"
});
item.addEventListener('mouseenter', () =>{
img.style.transition = "transform 2s ease-out"
})
});
#display-grid {
display: grid;
width: 80%;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(24%, 1fr));
}
.cell {
border: 1px solid #EEEEEE;
border-radius: 1rem;
width: 100%;
overflow-y: hidden;
position: relative;
overflow: hidden;
border-radius: 12px;
aspect-ratio: 1 / 1.2;
}
.cell .image-container {
width: 100%;
height: 90%;
position: relative;
aspect-ratio: 1 / 1;
border-radius: 1rem;
overflow-y: hidden;
}
.cell img {
width: 100%;
height: auto;
object-fit: cover;
object-position: top;
border-radius: 1rem;
}
<div id="display-grid">
<div class="cell">
<div class="image-container">
<img id="website-preview" src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024">
</div>
<p>Image Title</p>
</div>
<div class="cell">
<div class="image-container">
<img id="website-preview" src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024">
</div>
<p>InfoSwap</p>
</div>
</div>
Known Issue:
Sometimes, when the browser's DevTools is open, the performance throttling kicks in to limit how frequently the mousemove
events are fired, especially if the browser detects excessive rendering work.
Upvotes: 2