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.
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>