Captivating Image Animation on Link Hover

An captivating image animation effects that activate when users hover over your list item links.




In this tutorial, we’ll demonstrate how to implement a unique image animation effect on list item link hover using HTML, CSS, and JavaScript. This effect involves changing images dynamically as the user hovers over different list items, creating an engaging and interactive portfolio display.

This interaction can significantly enhance your portfolio’s visual appeal, help you lasting impression on your visitors.

Let’s start by setting up the HTML structure for our portfolio list and floating image container:

HTML List Item

<ul class="list">
  <li><a dd-portfolio="project1" href="javascript:;">Garage</a></li>
  <li><a dd-portfolio="project2" href="javascript:;">Event Planner</a></li>
  <li><a dd-portfolio="project3" href="javascript:;">Restaurent</a></li>
  <li><a dd-portfolio="project4" href="javascript:;">Decorations</a></li>
  <li><a dd-portfolio="project5" href="javascript:;">Healthcare</a></li>
</ul>

In the above snippet we are using the “dd-portfolio” attribute for unique identification. Next, we will create a floating div that will float on the dom with the mouse position.

HTML Structure

<div class="floater dn" id="floater">
  <img id="project1" src="images/pexels-mike-190537.jpg" />
  <img id="project2" src="images/pexels-alex-andrews-1983046.jpg" />
  <img id="project3" src="images/pexels-chan-walrus-941861.jpg" />
  <img id="project4" src="images/pexels-fox-750843.jpg" />
  <img id="project5" src="images/pexels-miguel-á-padriñán-3936358.jpg" />
</div>

Then, apply the CSS to list item and floating div.

:root {
  --font: "Poppins", sans-serif;
  --floater_width: 700px;
  --floater_height: 400px;
  --transition: all 0.75s;
}
body {
  background-color: #000000;
}
/* List items */
.list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.list li {
  display: flex;
}
.list li a {
  display: flex;
  width: 100%;
  justify-content: center;
  border-bottom: 1px solid rgba(255, 255, 255, 0.25);
  outline: none;
  padding: 2rem;
  font-family: var(--font);
  font-weight: 900;
  font-size: 5rem;
  text-decoration: none;
  color: transparent;
  transition: var(--transition);
  -webkit-text-stroke: 2px #fff;
}
.list li a:hover {
  color: #fff;
  -webkit-text-stroke: 2px transparent;
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.25);
}
/* Floater div */
.floater.dn {
  visibility: hidden;
  opacity: 0;
}
.floater {
  position: fixed;
  display: inline-block;
  width: var(--floater_width);
  height: var(--floater_height);
  visibility: visible;
  opacity: 1;
  z-index: -1;
  transition: var(--transition) cubic-bezier(0, 0.2, 0.6, 1);
}
/* Portfolio Image */
.floater img {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  filter: blur(0px) hue-rotate(180deg);
  transition: var(--transition);
}
.floater img.active {
  opacity: 1;
  filter: blur(0) hue-rotate(0);
  box-shadow: none;
}

The magic happens with JavaScript. We’ll use event listeners to detect when a user hovers over a list item and update the images accordingly:

The Javascript

// Create floater const
const floater = document.getElementById("floater");
// Get floater width & height
const floater_width = floater.clientWidth;
const floater_height = floater.clientHeight;
// Fetch all the portfolio images.
const portfolio_images = document.querySelectorAll("#floater img");
// List of the items.
const list_items = document.querySelectorAll(".list li a");
list_items.forEach((item) => {
  // Mouse move event
  item.addEventListener("mousemove", fnMouseMove);
  // Mouse leave event.
  item.addEventListener("mouseleave", fnMouseOut);
});
// Declate initial x,y pos at 0,0
let x = 0,
  y = 0;
// To detect the first mouse move.
let first_move = false;
// Get window half size(to change the animation direction).
let half_win_size = window.innerWidth / 2;
function fnMouseMove(e) {
  if (!first_move) {
    // Remove display none on first mouse move.
    floater.classList.remove("dn");
    first_move = true;
  }
  // Get x,y mouse co-ordinates
  x = e.clientX || e.pageX;
  y = e.clientY || e.pageY;
  // X direction animation/pos
  let x_dir = x / window.innerWidth;
  let portfolio_attr = this.getAttribute("dd-portfolio");
  // Animate image on x axis
  x > half_win_size ? (x_dir = -(x_dir * 5)) : (x_dir = (1 - x_dir) * 5);
  floater.style.transform = "perspective(400px) rotateY(" + x_dir + "deg)";
  // Activate the current hovered portfolio/image
  let current_portfolio = document.getElementById(portfolio_attr);
  portfolio_images.forEach((image) => {
    image.classList.remove("active");
  });
  current_portfolio.classList.add("active");
  floater.style.opacity = 1;
  // Floater div animation
  floater.style.top = y - floater_height / 2 + "px";
  floater.style.left = x - floater_width / 2 + "px";
}
// Mouse leave function
function fnMouseOut(e) {
  // Remove the animation.
  portfolio_images.forEach((image) => {
    image.classList.remove("active");
  });
}

Putting It All Together in an HTML file and witness the magic.

We hope you enjoyed this tutorial.