47

I want to move my gradient that has multiple colors smoothly but the problem is that the animation is not smooth. It just changes its position at every step.

<style>
  .animated {
    width: 300px;
    height: 300px;
    border: 1px solid black;
    animation: gra 5s infinite;
    animation-direction: reverse;
    -webkit-animation: gra 5s infinite;
    -webkit-animation-direction: reverse;
    animation-timing-function: linear;
    -webkit-animation-timing-function: linear;
  }
  
  @keyframes gra {
    0% {
      background: -webkit-gradient(linear, left top, right bottom, color-stop(0%, #ff670f), color-stop(21%, #ff670f), color-stop(56%, #ffffff), color-stop(88%, #0eea57));
      background: -webkit-linear-gradient(-45deg, #ff670f 0%, #ff670f 21%, #ffffff 56%, #0eea57 88%);
      background: linear-gradient(135deg, #ff670f 0%, #ff670f 21%, #ffffff 56%, #0eea57 88%);
    }
    50% {
      background: -webkit-gradient(linear, left top, right bottom, color-stop(0%, #ff670f), color-stop(10%, #ff670f), color-stop(40%, #ffffff), color-stop(60%, #0eea57));
      background: -webkit-linear-gradient(-45deg, #ff670f 0%, #ff670f 10%, #ffffff 40%, #0eea57 60%);
      background: linear-gradient(135deg, #ff670f 0%, #ff670f 10%, #ffffff 40%, #0eea57 60%);
    }
    100% {
      background: -webkit-gradient(linear, left top, right bottom, color-stop(0%, #ff670f), color-stop(5%, #ff670f), color-stop(10%, #ffffff), color-stop(40%, #0eea57));
      background: -webkit-linear-gradient(-45deg, #ff670f 0%, #ff670f 5%, #ffffff 10%, #0eea57 40%);
      background: linear-gradient(135deg, #ff670f 0%, #ff670f 5%, #ffffff 10%, #0eea57 40%);
    }
  }
</style>
<div class="animated">
  <h1>Hello</h1>
</div>

Is it possible to accomplish without using jQuery?

My jsfiddle link is https://jsfiddle.net/bAUK6

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
tehTerminator
  • 607
  • 1
  • 6
  • 10
  • 1
    Well, I think you may want to try your demo on IE 11 (not sure about IE 10), it works as you expected. All the other browsers currently don't support background animation, instead you can try animating the `background-position` **but** of course this won't have all the effects by animating the whole background. – King King May 03 '14 at 06:33
  • 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:08

5 Answers5

56

Please try this code:

#gradient
{
    height:300px;
    width:300px;
    border:1px solid black;
    font-size:30px;
    background: linear-gradient(130deg, #ff7e00, #ffffff, #5cff00);
    background-size: 200% 200%;

    -webkit-animation: Animation 5s ease infinite;
    -moz-animation: Animation 5s ease infinite;
    animation: Animation 5s ease infinite;
}

@-webkit-keyframes Animation {
    0%{background-position:10% 0%}
    50%{background-position:91% 100%}
    100%{background-position:10% 0%}
}
@-moz-keyframes Animation {
    0%{background-position:10% 0%}
    50%{background-position:91% 100%}
    100%{background-position:10% 0%}
}
@keyframes Animation { 
    0%{background-position:10% 0%}
    50%{background-position:91% 100%}
    100%{background-position:10% 0%}
}
<html>
<div id="gradient">
  Hello
</div>
</html>
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
RP The Designer
  • 576
  • 6
  • 3
14

Dynamic implementation of Dave's answer:

:root{
    --overlay-color-1: #ff0000;
    --overlay-color-2: #0000ff;
    --anim-duration: 2s;
}

#gradient {
    opacity: 0.8;
    background: none;
}

#gradient:after,
#gradient:before {
    content: '';
    display: block;
    position: absolute;
    top: 0; bottom: 0; left: 0; right: 0;
}

#gradient:before {
    background: linear-gradient(135deg, var(--overlay-color-2) 0%, var(--overlay-color-1) 100%);
    animation: OpacityAnim var(--anim-duration) ease-in-out 0s infinite alternate;
}

#gradient:after {
    background: linear-gradient(135deg, var(--overlay-color-1) 0%, var(--overlay-color-2) 100%);
    animation: OpacityAnim var(--anim-duration) ease-in-out calc(-1 * var(--anim-duration)) infinite alternate;
}

@keyframes OpacityAnim {
    0%{opacity: 1.0}
    100%{opacity: 0.0}
}
<div id="gradient"></div>
isherwood
  • 58,414
  • 16
  • 114
  • 157
User Rebo
  • 3,056
  • 25
  • 30
  • Adding `position:fixed; z-index:-1;` to `#gradient` will get it to stay below other elements and be usable as a background. I dont know css but this is what worked for me, if theres another way Id love to hear it. – PAEz Jan 02 '23 at 15:48
13

Using CSS variables it's now a trivial task.

Here is a basic example (hover to see the result)

@property --a{
  syntax: '<angle>';
  inherits: false;
  initial-value: 90deg;
}
@property --l{
  syntax: '<percentage>';
  inherits: false;
  initial-value: 10%;
}
@property --c{
  syntax: '<color>';
  inherits: false;
  initial-value: red;
}

.box {
  /*  needed for firefox to have a valid output */
  --a:80deg;
  --l:10%;
  --c:red;
  /**/
  cursor:pointer;
  height:200px;
  transition:--a 0.5s 0.1s,--l 0.5s,--c 0.8s;
  background:linear-gradient(var(--a), var(--c) var(--l),blue,var(--c) calc(100% - var(--l)));
}
.box:hover {
  --a:360deg;
  --l:40%;
  --c:green;
}
<div class="box"></div>

More details here: https://dev.to/afif/we-can-finally-animate-css-gradient-kdk

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 3
    FYI, gradient transitions do not work on Firefox or Safari yet, which implies it doesn't work on iPhones and iPads either. – M - Jun 18 '21 at 19:04
  • 2
    More specifically it's the @property part that doesn't work in Safari and Firefox (https://developer.mozilla.org/en-US/docs/Web/CSS/@property) – Simon_Weaver Oct 09 '22 at 03:19
2

Here is another way. The following has the static gradient containing all phases of the animation, which is then moved inside the outer element. This allows to perform animation smoothly (as the topic suggests), because the only animation here is the element position.

Please note that for the sake of performance the gradient element left unchanged. Although the question was to animate the gradient, moving the background does practically the same thing, while the performance wins!

.animated {
    width: 300px;
    height: 300px;
    overflow: hidden;
    position: relative;
    border: 1px solid black;
}
.innerGradient {
    z-index: -1;
    width: 300%;
    height: 300%;
    position: absolute;
    animation: gra 5s infinite;
    -webkit-animation: gra 5s infinite;
    background: linear-gradient(135deg, #ff670f 0%, #ff670f 20%, #ffffff 50%, #0eea57 80%, #0eea57 100%);
    background: -webkit-linear-gradient(135deg, #ff670f 0%, #ff670f 20%, #ffffff 50%, #0eea57 80%, #0eea57 100%);
}
@keyframes gra {
    0% { left: -200%; top: -200%; }
    50% { left: 0%; top: 0%; }
    100% { left: -200%; top: -200%; }
}
<div class="animated">
    <h1>Hello</h1>
    <div class="innerGradient"></div>
</div>
0

How about this:

Set the body margin and padding to 0. Set an html rule to 100% height (higher than 100% may be required).

Set the body to the end state for the gradient.

Create an empty div with a background which is the start state for the gradient. Give the empty div 100% height.

Give both the body and the empty div a background-attachment: fixed;

Create a wrapper for your body content.

Set the empty div to position: fixed; Set the wrapper to position: relative; Give both a z-index, the wrapper being higher.

Create an animation that will change the opacity of the empty div from 1 to 0 over the desired time. Add animation-fill-mode:forwards; to the div rule so the animation stays where it ends.

It's not as sexy as a real animated gradient shift, but it's as simple as you can get with CSS only and keyframes, I think.