Skiddswarmik
Skiddswarmik

Reputation: 248

How do I make my Vertical Image Animation Smoother?

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

Answers (1)

iorgu
iorgu

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

Related Questions