What is the best way to add a directional hover effect to a filter?

I am attempting to implement a sliding hover filter effect on an image. The original filter I have is set to grayscale(1).

What I want to achieve is for the filter to transition directionally instead of having a slider overlay effect, in order to change it to colored: "grayscale(0)".

In essence, I would like the filter to be influenced by this transition.

Can this be done?

Thank you in advance.

* {
  box-sizing: border-box;
}

body {
  background: #fefefe;
  color: #333;
  font: 14px/1.5 "Fira Sans", sans-serif;
}

h1 {
  font-size: 2.5rem;
  font-weight: 300;
  margin: 1.5em 0.5rem 1em;
  text-align: center;
}

.container {
  margin: 0 auto;
  padding: 2rem;
  max-width: 1200px;
}

.row {
  display: flex;
}

.col {
  color: #fff;
  flex: 1 1 auto;
  min-height: 260px;
  position: relative;
}

.col h2 {
  font-weight: 300;
  font-size: 1.33333rem;
  line-height: 1.25;
  margin: 0;
  position: absolute;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 0;
}

.col:nth-child(2) {
  min-width: 20%;
}

.col:nth-child(4) {
  min-width: 33%;
}

.col:nth-child(3)+.col:nth-child(3) {
  min-width: 50%;
}

.img-container {
  background: #0f0523 50% 50%/cover;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  transition: 1s;
  transform-origin: bottom right;
  filter: grayscale(1);
}

.img-container::before {
  background: linear-gradient(transparent, rgba(67, 17, 51, 0.5), #000320);
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.col:hover .photo-container {
  transform: scale(1.25);
}

.slide {
  background: rgba(0, 0, 0, 0.4);
  padding: 0 1.5rem;
}


/* THE EFFECT */

.col {
  overflow: hidden;
  position: relative;
}

.slide {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  transition: all 0.275s ease-in-out, visibility 0s 0.275s;
  visibility: hidden;
  will-change: transform;
  transform: translateY(100%);
}

.row:hover~.row .slide {
  transform: translateY(-100%);
}

.row:hover .slide {
  transform: translateX(100%);
}

.row:hover .col:hover~.col .slide {
  transform: translateX(-100%);
}

.row:hover .col:hover .slide {
  transform: none;
  visibility: visible;
  transition-delay: 0s;
}
<div class="container">
  <div class="row">
    <div class="col">
      <div class="img-container" style="background-image:url(https://source.unsplash.com/600x250/?sig=80);"></div>
      <h2>1 </h2>
      <div class="slide"></div>
    </div>
  </div>
  <div class="row">
    <div class="col">
      <div class="img-container" style="background-image:url(https://source.unsplash.com/600x250/?sig=212);"></div>
      <h2>2 </h2>
      <div class="slide"></div>
    </div>
    <div class="col">
      <div class="img-container" style="background-image:url(https://source.unsplash.com/600x250/?sig=242);"></div>
      <h2>3 </h2>
      <div class="slide"></div>
    </div>

Answer №1

In this method, we utilize the mask property to animate the mask-position. By utilizing a pseudo-element for the image, we eliminate the need for additional elements. The technique involves layering two images on top of each other and adjusting the masking on the top to reveal the bottom image.

I have omitted the text content to focus solely on the code related to the filter effect:

* {
  box-sizing: border-box;
}

body {
  background: #fefefe;
}

.container {
  margin: 0 auto;
  padding: 2rem;
  max-width: 1200px;
}

.row {
  display: flex;
}

.col {
  color: #fff;
  flex: 1 1 auto;
  min-height: 260px;
  position: relative;
}

.img-container {
  background-size:0 0;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.img-container::before,
.img-container::after {
  content: "";
  background-image: inherit;
  background-size:cover;
  background-position:center;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;

}
.img-container::before {
  filter: grayscale(1);
}

.img-container::after {
  transition: all 0.275s ease-in-out, visibility 0s 0.275s;
  visibility: hidden;
  -webkit-mask: linear-gradient(#fff,#fff);
  -webkit-mask-size: 200% 200%;
  -webkit-mask-position:left 0 bottom 200%;
  -webkit-mask-repeat: no-repeat;
  mask: linear-gradient(#fff,#fff);
  mask-size: 200% 200%;
  mask-position:left 0 bottom 200%;
  mask-repeat: no-repeat;
}


.row:hover~.row .img-container::after {
  -webkit-mask-position:left 0 top 200%;
  mask-position:left 0 top 200%;
}

.row:hover .img-container::after {
  -webkit-mask-position:right 200% top 0;
  mask-position:right 200% top 0;

}

.row:hover .col:hover~.col .img-container::after {
  -webkit-mask-position:left 200% top 0;
  mask-position:left 200% top 0;
}

.row:hover .col:hover .img-container::after {
  visibility: visible;
  transition-delay: 0s;
  -webkit-mask-position:0 0%;
  mask-position:0 0%;
  
}
<div class="container">
  <div class="row">
    <div class="col">
      <div class="img-container" style="background-image:url(https://picsum.photos/id/1012/800/800);"></div>
    </div>
  </div>
  <div class="row">
    <div class="col">
      <div class="img-container" style="background-image:url(https://picsum.photos/id/1014/800/800);"></div>
    </div>
    <div class="col">
      <div class="img-container" style="background-image:url(https://picsum.photos/id/16/800/800);"></div>
    </div>
  </div>
</div>

The usage of mask in this scenario mimics that of a background, providing details on the calculation process which can be found in this question/answer reference: Using percentage values with background-position on a linear gradient

To showcase the underlying trick, let's switch from using a mask to a background:

* {
  box-sizing: border-box;
}

body {
  background: grey;
}

.container {
  margin: 0 auto;
  padding: 2rem;
  max-width: 1200px;
}

.row {
  display: flex;
}

.col {
  color: #fff;
  flex: 1 1 auto;
  min-height: 260px;
  position: relative;
}


.col:nth-child(2) {
  min-width: 20%;
}

.col:nth-child(4) {
  min-width: 33%;
}

.col:nth-child(3)+.col:nth-child(3) {
  min-width: 50%;
}

.img-container {
  background-position:center;
  background-size:0 0;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  transition: 1s;
  transform-origin: bottom right;
}

.img-container::before {
  background: inherit;
  background-size:cover;
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  filter: grayscale(1);
}

.img-container::after {
  content:"";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  transition: all 0.275s ease-in-out, visibility 0s 0.275s;
  visibility: hidden;
  background: linear-gradient(#fff,#fff);
  background-size: 200% 200%;
  background-position:left 0 bottom 200%;
  background-repeat: no-repeat;
}


.col {
  overflow: hidden;
  position: relative;
}

.row:hover~.row .img-container::after {
  background-position:left 0 top 200%;
}

.row:hover .img-container::after {
  background-position:right 200% top 0;

}

.row:hover .col:hover~.col .img-container::after {
  background-position:left 200% top 0;
}

.row:hover .col:hover .img-container::after {
  visibility: visible;
  transition-delay: 0s;
  background-position:0 0%;
  
}
<div class="container">
  <div class="row">
    <div class="col">
      <div class="img-container" style="background-image:url(https://picsum.photos/id/1012/800/800);"></div>
    </div>
  </div>
  <div class="row">
    <div class="col">
      <div class="img-container" style="background-image:url(https://picsum.photos/id/1014/800/800);"></div>
    </div>
    <div class="col">
      <div class="img-container" style="background-image:url(https://picsum.photos/id/16/800/800);"></div>
    </div>
  </div>
</div>

Essentially, the white area represents the visible part of the image, hence sliding it reveals our filtered-free image.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are the steps to update your profile picture using Angular?

In my Angular 6 application, I am implementing an image upload feature with the following code: Html: <img [src]="url ? url : 'https://www.w3schools.com/howto/img_avatar.png'"> <br/> <input type='file' (change)="onSelec ...

React and Mui are experiencing a peculiar issue where a background is mysteriously missing from a component in a specific location

Exploring React 16 and functional components with hooks Discussing Mui 4 framework Adding a background pattern to multiple login pages seems simple, right? Here is an example of a login page component: return ( <Box width={1} height={1}> ...

What is the method for aligning a button label to the left and an icon to the right?

I have a list of items and I want to display label text on the left and a check icon on the right. This works fine when the text is short, but when I added the textOverflow property, the icon gets hidden along with the rest of the text. Here is my list ...

Tips for aligning objects within a bootstrap column so that they all have the same starting point on the X axis

To recreate the issue I am facing, I created a basic HTML structure with 2 bootstrap columns. My aim is to have multiple <p> elements in the second column, stacked one below the other. However, the problem arises when these <p> tags contain tex ...

"Stylish form field design with outlined borders that displays a subtle hover

I am attempting to modify the background color of a mat-form-field outlined when hovering with the mouse. .mat-form-field.mat-form-field-appearance-outline.mat-form-field-outline-thick { // HOVER EFFECT background-color: $dark-blue-200; } The above ...

Separate compound words without hyphens at the boundaries of each word

I am currently incorporating the use of Bootstrap for my project. Let's assume that I have text displayed in this way: "Stackoverflow" Is there a method to automatically ensure that it breaks like below if the text exceeds the container: "Stack ...

Text overlaps when li element is nested inside another element

Seeking assistance in creating a CSS/HTML menu bar that triggers list elements on hover. Facing difficulty arranging the list in two columns. Code Sample:http://jsfiddle.net/Km922/1/ <!DOCTYPE html> <html lang="en"> <head> <meta ...

Challenges with moving images in Unslider

There seems to be an issue with the unslider-arrows on the unslider banner. The images are not sliding properly when transitioning to the next image without user input. Instead, they resize from small to full size starting at the top of the .banner div. ...

Like button for Facebook located at the bottom of a static webpage

I am facing an issue with my web application where it does not scroll properly, especially when there is a Facebook button at the bottom. The behavior in Chrome and Firefox is inconsistent and causing some trouble. In Chrome, the div repositions correctly ...

Style the "focus" pseudo-class of an input element using Vue programmatically

Currently, I have been utilizing event listeners to manipulate the :focus pseudo-class of an input element: const element = document.getElementById("myElementID"); element.addEventListener("focus", (e) => { e.target.style.borderCo ...

What is the method for adjusting the input text color in a textarea to green?

Is there a way to change the input color for this textarea so I can type green text on a black background? textarea{ background-color: black; } <textarea id="htmlCode" class="1111" placeholder="HTML"></textarea> ...

What causes the position: sticky; property to fail in React implementations?

Currently, I am facing a challenge in making two sidebars sticky so that they will follow as the user scrolls until they reach the bottom of the page. In my current editing project, this implementation seems to be more complex compared to other programs w ...

Submenu failing to appear

After reading a discussion on dropdown menu issues on a popular forum, I am still unable to locate the error on my website: . The submenu on the main top menu is not functioning as it should. My attempt to resolve the problem by changing the overflow sett ...

Utilizing Ant Design Icons without an Internet Connection

In my current project, a reactJS application utilizing ant design for the UI was recently deployed to production on locked down computers. These computers lack internet access, causing ant design icons on modals to appear as empty boxes. Upon investigation ...

Fonts fail to load on Internet Explorer 10

Link to the website in question: In my fonts.css file, I am loading multiple fonts as shown below: @font-face { font-family: GothamBook; src: url('../fonts/Gotham-Book.otf'); src: url( ../fonts/Gotham-Book.otf ); /* IE */ } @font-face ...

Ways to identify when a person has scrolled a specific distance

Is it possible to modify the appearance of my top navigation bar once the page has been scrolled a specific number of pixels? I assume this requires some sort of listener to track the amount of scrolling. I came across element.scrollTop, but it doesn' ...

Adjust tool tip text on the fly

As a beginner, I created a test table and now I want to make the tool tip text change dynamically when I hover over the table with my mouse. Ideally, I would like the tool tip text to correspond to the content of the table cell. Currently, I only have stat ...

`Heart-shaped loading bar in Reactjs`

Looking for help in creating a heart-shaped progress loader for a React project. I attempted using CSS but encountered issues with the progress not filling the entire heart design. Below is the code snippet I used. The progress should start from the bottom ...

Removing an Element in a List Using Jquery

In my JQuery, there is a list named additionalInfo which gets populated using the function below: $('#append').on('click', function () { //validate the area first before proceeding to add information var text = $('#new-email&a ...

The left column is stationary vertically, but can be scrolled horizontally

On a webpage, there are three columns: left, middle, and right. Only the left column is set to position:fixed, while the others are normal. When vertically scrolling, the left column should remain fixed while the other two scroll. However, when the brows ...