如何更流畅地为元素内的背景渐变旋转设置动画?

时间:2018-11-27 18:13:46

标签: html css animation

我有一个按钮,我想通过旋转它来为其背景设置动画:

.gradient-button {
  animation: rotate-gradient 1s infinite;
  background-image: linear-gradient(0deg, red, yellow, green);
}

@keyframes rotate-gradient {
  0% {
    background-image: linear-gradient(0deg, red, yellow, green);
  }
  /* Adding a step in the middle */
  20% {
    background-image: linear-gradient(60deg, red, yellow, green);
  }
  40% {
    background-image: linear-gradient(120deg, red, yellow, green);
  }
  60% {
    background-image: linear-gradient(180deg, red, yellow, green);
  }
  80% {
    background-image: linear-gradient(240deg, red, yellow, green);
  }
  100% {
    background-image: linear-gradient(300deg, red, yellow, green);
  }
}
<button id="fill" class="gradient-button">Fill Form</button>

但是只有6个步骤,动画效果不佳,手动添加24帧会适得其反。

或者我尝试了JS:

$(document).ready(function () {
    AnimateRotate(360);
});


function AnimateRotate(d){
    var elem = $("#mybutton");

    $({deg: 0}).animate({deg: d}, {
        duration: 2000,
        step: function(now){
            elem.css({
                 "background-image":"linear-gradient("+now+"deg, red, yellow, green);"
            });
        }
    });
}

但是那什么也没做。 问题:如何使动画流畅? 运行代码:https://codepen.io/anon/pen/OaojNP

3 个答案:

答案 0 :(得分:3)

如果是我,我可能会采取另一种幻想,因为线性渐变很难在不逐步执行每个属性更改的情况下进行平滑的关键帧动画处理。取而代之,作为一种替代方法,对伪元素进行转换可能会得到相同的预期结果?

就这样;

.gradient-button {
  position: relative;
  overflow: hidden;
  background-color: transparent;
}

.gradient-button:after {
  content: '';
  display: block;
  z-index: -1;
  position: absolute;
  top: -2rem; right: 0; bottom: -2rem; left: 0;
  background-image: linear-gradient(red, yellow, green);
  animation: rotate-gradient linear 1s infinite;
}

@keyframes rotate-gradient {
  to { transform: rotate(360deg) }
}
<button id="fill" class="gradient-button">Fill Form</button>

答案 1 :(得分:3)

background-image不具有动画效果。 您可以添加一个绝对定位的(伪)元素,并使用transform对其进行旋转,只要您隐藏溢出并确保该限制永远不可见即可。

但是,如果您真的是 想要旋转的背景(因为它很酷,并且可以),您可以使用<svg>作为背景和SMIL动画来旋转渐变方向:

<svg width="240" height="60" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
      <linearGradient id="Gradient" x1="0" x2="0" y1="0" y2="1">
        <animate attributeName="x1" values="0;0;1;1;0" keyTimes="0;.25;.5;.75;1" dur="1s" repeatCount="indefinite" />
        <animate attributeName="y1" values="0;1;1;0;0" keyTimes="0;.25;.5;.75;1" dur="1s" repeatCount="indefinite" />
        <animate attributeName="x2" values="1;1;0;0;1" keyTimes="0;.25;.5;.75;1" dur="1s" repeatCount="indefinite" />
        <animate attributeName="y2" values="1;0;0;1;1" keyTimes="0;.25;.5;.75;1" dur="1s" repeatCount="indefinite" />
        <stop offset="0%" stop-color="red"/>
        <stop offset="50%" stop-color="yellow"/>
        <stop offset="100%" stop-color="green"/>
      </linearGradient>
  </defs>
  <rect x="0" y="-90" width="240" height="240" fill="url(#Gradient)"/>
  
</svg>

说明:我发布了此答案,因为(我知道)这是唯一实际旋转渐变背景方向而不旋转元素本身的方法。在某些需要将元素转换为其他东西或不想对其进行转换的边缘情况下,这可能会很有用。
可能的用例:复杂的容器形状(思考路径)或需要溢出的元素。

正如ChrisW在评论中指出的那样,IE和Edge仍未添加对SMIL动画的支持。但是,有一个polyfill

这就是说,仅仅因为它在技术上是可能的,并不一定意味着它应该比transform更受青睐。我的直觉是SMIL有点贵(就计算而言),但是我还没有进行任何测试来支持这一点。
另外,transform较不冗长,因此更实用。

答案 2 :(得分:2)

您可以完全更改标记吗?如果是这样,您可以在单独的元素上设置旋转动画。

.gradient-button {
  position: relative;
  overflow: hidden;
  width: auto;
  display: inline-block;
}

.gradient-button>.bg {
  position: absolute;
  width: 100%;
  height: 100%;
  background-position: center center;
  background-image: linear-gradient(90deg, red 45%, yellow 50%, green 55%);
  animation: spin 1s linear infinite;
}

.gradient-button>.label {
  position: relative;
}

@keyframes spin {
  from {
    transform: rotate(0deg) scale(5);
  }
  to {
    transform: rotate(360deg) scale(5);
  }
}
<a id="fill" class="gradient-button">
  <span class="bg"></span>
  <span class="label">Fill Form</span>
</a>

Codepen