0

I want to achieve an effect than when the user hovers over a div, it's background color turns into a gradient that smoothly switches from one corner to the other, repeatedly, using purely CSS.

The effect I want to achieve, trying to put it more into words, is that the darker part of the background (the one with 0.9 opacity) slides from one corner to the other repeatedly as long as the cursor remains above the element.

What is actually happening is that the background jumps from one state to the other. Where am I going wrong? How can I get this to be an animated, smooth effect?

Here is my code:

#test {
  width: 200px;
  height: 200px;
  background-color: rgba(215, 54, 92, 0.7);
}
@keyframes test_hover {
  from {
    background: -webkit-linear-gradient(45deg, rgba(215, 54, 92, 0.9), rgba(215, 54, 92, 0.5));    /* For Safari 5.1 to 6.0 */
    background: -o-linear-gradient(45deg, rgba(215, 54, 92, 0.9), rgba(215, 54, 92, 0.5));    /* For Opera 11.1 to 12.0 */
    background: -moz-linear-gradient(45deg, rgba(215, 54, 92, 0.9), rgba(215, 54, 92, 0.5));    /* For Firefox 3.6 to 15 */
    background: linear-gradient(45deg, rgba(215, 54, 92, 0.9), rgba(215, 54, 92, 0.5));    /* Standard syntax (must be last) */
  }
  to {
    background: -webkit-linear-gradient(45deg, rgba(215, 54, 92, 0.5), rgba(215, 54, 92, 0.9));    /* For Safari 5.1 to 6.0 */
    background: -o-linear-gradient(45deg, rgba(215, 54, 92, 0.5), rgba(215, 54, 92, 0.9));    /* For Opera 11.1 to 12.0 */
    background: -moz-linear-gradient(45deg, rgba(215, 54, 92, 0.5), rgba(215, 54, 92, 0.9));    /* For Firefox 3.6 to 15 */
    background: linear-gradient(45deg, rgba(215, 54, 92, 0.5), rgba(215, 54, 92, 0.9));    /* Standard syntax (must be last) */
  }
}
#test:hover {
  cursor: pointer;
  animation-name: test_hover;
  animation-duration: 4s;
  animation-delay: 1s;
  animation-iteration-count: infinite;
}
<div id="test"></div>

Here is the JSFiddle

This question have been referenced to be a duplicate from here, but I think it is not. For a start, there is no code in that question which makes it difficult to understand. Also, in the answer to that question he uses a transition, but I do not want a transition because it is only applied once, I want my animation to be constantly repeated while the user is hovering the div.

Community
  • 1
  • 1
pablito.aven
  • 1,135
  • 1
  • 11
  • 29
  • Same thing really, animation or transition...it's not transitionable/animatable. You have to do somethig with bg position I think. – Paulie_D Nov 25 '15 at 14:23
  • @pablito.aven He did help and explain. Background-gradients are not transitionable. You could use an image and play with the background-position but that's about it. – CaldwellYSR Nov 25 '15 at 15:27
  • You might achieve it with javascript http://stackoverflow.com/questions/33892656/how-change-css-using-variables-to-make-an-animation – Mi-Creativity Nov 25 '15 at 15:45
  • 1
    While the crux of this question is the same as the other one (that gradients can't be animated), the implementation here is more complex than that one due to the alternations which in my opinion did make it different. That said, I didn't expect my vote to be binding here because I hadn't hammered it in the first place. – Harry Nov 26 '15 at 20:54
  • 1
    @Harry Yes, yours was very complete, thank you. I had upvoted it but forgot to mark as accepted, done now. – pablito.aven Dec 04 '15 at 17:34
  • Does this answer your question? [Use CSS3 transitions with gradient backgrounds](https://stackoverflow.com/questions/6542212/use-css3-transitions-with-gradient-backgrounds) – Mahozad May 15 '22 at 07:09

3 Answers3

8

As already pointed out in comments, linear gradients are not transitionable or animatable unlike colors (background-color) because they are considered as image (background-image). Transitions effects are generally achieved by the UA calculating the value at each intermediate stage and then applying that value to the element. In case of an image, it is not possible to calculate the values at intermediate stages and hence they cannot be transitioned.

You need to transition the position (background-position) instead to achieve the effect. This can be done by using a pseudo-element whose background contains the linear-gradient and animating its position on hover. The gradient should go both ways (that is, it should go from rgba(215,54,92,0.9) to rgba(215,54,92,0.5) and then to rgba(215,54,92,0.9)) because it needs to accommodate both phases of the animation and its background-size also needs to be double (that is 200% 200%). Then by animating the positon from 0% 100% to 100% 0% the required effect can be achieved.

#test {
  position: relative;
  width: 200px;
  height: 200px;
  background-color: rgba(215, 54, 92, 0.7);
  transition: background-color .1s;
}
#test:hover {
  background-color: transparent;
  cursor: pointer;
}
#test:hover:after {
  position: absolute;
  content: '';
  height: 100%;
  width: 100%;
  background: linear-gradient(45deg, rgba(215, 54, 92, 0.9), rgba(215, 54, 92, 0.5), rgba(215, 54, 92, 0.9));
  background-size: 200% 200%;
  background-position: 0% 100%;
  animation-name: test_hover;
  animation-duration: 4s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  animation-direction: alternate;
}
@keyframes test_hover {
  from {
    background-position: 0% 100%;
  }
  to {
    background-position: 100% 0%;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id="test"></div>

Another slightly different approach that can be used is to add two pseudo-elements with each having one stage of animation's gradient and then animate their opacity such that at any point of time only one of them will be visible. This would also produce a similar effect.

#test {
  position: absolute;
  width: 200px;
  height: 200px;
  background-color: rgba(215, 54, 92, 0.7);
  transition: background-color .1s;
}
#test:before,
#test:after {
  position: absolute;
  content: '';
  height: 100%;
  width: 100%;
  z-index: 1;
}
#test:before {
  background: linear-gradient(45deg, rgba(215, 54, 92, 0.9), rgba(215, 54, 92, 0.5));
  opacity: 0;
}
#test:after {
  background: linear-gradient(45deg, rgba(215, 54, 92, 0.5), rgba(215, 54, 92, 0.9));
  opacity: 0;
}
@keyframes test_hover {
  0%, 100% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
}
#test:hover:before,
#test:hover:after {
  cursor: pointer;
  animation-name: test_hover;
  animation-duration: 4s;
  animation-iteration-count: infinite;
  animation-fill-mode: backwards;
}
#test:hover:before {
  animation-delay: 1s;
}
#test:hover:after {
  animation-delay: 3s;
}
#test:hover {
  background-color: transparent;
  transition: background-color 2s 1s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id="test"></div>
Harry
  • 87,580
  • 25
  • 202
  • 214
3

To animate the background, you need to modify the background position.

To make the transition smooth, you need to play with opacity - and since you don't want all the element to fade, you need to use a pseudo element for this

#test{
    width: 200px;
    height: 200px;
    position: relative;
    background-color: rgba(215,54,92,0.7);
    transition: background-color 1s;
}
#test:hover{
  background-color: transparent;
}

#test:after {
  content: "";
  position: absolute;
  left: 0px;
  top: 0px;
  right: 0px;
  bottom: 0px;
  z-index: -1;
    background-image: linear-gradient(45deg, 
                    rgba(215,54,92,0.5), 
                    rgba(215,54,92,0.9),
                    rgba(215,54,92,0.5), 
                    rgba(215,54,92,0.9),
                    rgba(215,54,92,0.5)
                    );
    background-size: 800px 800px;
    background-position: 0px 0px;
    animation: move 2s infinite linear;
    opacity: 0;
    transition: opacity 1s;
}

#test:hover:after {
    opacity: 1;
}
@keyframes move {
  from {background-position: 0px 600px;}
    to {background-position: -800px 600px;}
}
<div id="test"></div>
vals
  • 61,425
  • 11
  • 89
  • 138
2

How to animate gradient colors with keyframes:

// Beggin
#demo {
  width: 200px;
  height: 200px;
  position: absolute;
  background: linear-gradient(to right, #f06 0%, #60f 100%);
  z-index: 2;
}

/* Use a 'Before' element to change the background */
#demo::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(to left, #f06 0%, #60f 100%);
  /* Give it an Opacity of 0 and create your animation property: */
  opacity: 0;
  animation: bg 2800ms ease-in-out 0s infinite alternate-reverse;
  z-index: -1;
}

/* Now call the keyframe-function */
@keyframes bg {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

/* Unnecessary styling: */
h3 {
  color: #fff;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -130%);
  font-family: Helvetica, Arial, sans-serif;
}
<div id="demo"><h3>CWM</h3></div>
codeWithMe
  • 852
  • 12
  • 17