【问题标题】:How to animate a radial-gradient using CSS?如何使用 CSS 为径向渐变设置动画?
【发布时间】:2019-12-04 16:54:27
【问题描述】:

我正在尝试为 div 框创建径向渐变光泽效果,但我不确定这样做的最佳方式是什么。我没有找到实现我想要实现的目标的资源;只是闪耀会影响看起来像覆盖的效果。

我发现的大多数示例都类似于 http://jsfiddle.net/nqQc7/512/

下面我展示了我正在尝试创建的内容。

#shine-div {
  height: 30vh;
  width: 60vw;
  margin-right: auto;
  margin-left: auto;
  border-radius: 10px;
  /*background: radial-gradient(ellipse farthest-corner at right top, #FFFFFF 0%, #ffb3ff 8%, #ff33ff 25%, #800080 62.5%, #b300b3 100%);*/
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  animation: colorChange 5s infinite;
}

@keyframes colorChange {
  0% {
    background: radial-gradient(ellipse farthest-corner at left top, #FFFFFF 0%, #ffb3ff 8%, #ff33ff 25%, #800080 62.5%, #b300b3 100%)
  }
  50% {
    background: radial-gradient(ellipse farthest-corner at top, #FFFFFF 0%, #ffb3ff 8%, #ff33ff 25%, #800080 62.5%, #b300b3 100%)
  }
  100% {
    background: radial-gradient(ellipse farthest-corner at right top, #FFFFFF 0%, #ffb3ff 8%, #ff33ff 25%, #800080 62.5%, #b300b3 100%)
  }
}
<div id="shine-div">
  Shine
</div>

可以这样做吗?我也想让上面的白色发光从左到右顺利?我的尝试是否走在正确的轨道上?

【问题讨论】:

    标签: css css-animations radial-gradients


    【解决方案1】:

    您可以以不同的方式进行渐变并为位置设置动画。诀窍是将渐变的大小加倍并使颜色值停止其实际值的一半,这样您就可以保持相同的视觉渐变,然后您可以从左到右对其进行动画处理。

    由于最远角的计算,它与您在动画中定义的渐变看起来完全相同

    #shine-div {
      height: 30vh;
      width: 60vw;
      margin-right: auto;
      margin-left: auto;
      border-radius: 10px;
      background: radial-gradient(farthest-corner at top, #FFFFFF 0%, #ffb3ff 4%, #ff33ff 12.25%, #800080 31.25%, #b300b3 50%) top right/200% 200%;
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: bold;
      animation: colorChange 5s infinite alternate;
    }
    
    @keyframes colorChange {
      to {
        background-position:top left;
      }
     }
    <div id="shine-div">
      Shine
    </div>

    为了更接近您的渐变,您还必须为background-size 设置动画(有关计算详细信息,请参见下文)

    #shine-div {
      height: 30vh;
      width: 60vw;
      margin-right: auto;
      margin-left: auto;
      border-radius: 10px;
      background: radial-gradient(farthest-corner at top, #FFFFFF 0%, #ffb3ff 8%, #ff33ff 24.5%, #800080 62.5%, #b300b3 100%);
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: bold;
      animation: colorChange 5s infinite alternate linear;
    }
    
    @keyframes colorChange {
      from { /* radial-gradient(farthest-corner at top right, ..) */
        background-position:left top;
        background-size:200% 100%;
      
      }
      49.9% {
        background-position:left top;  
      }
      50% { /* radial-gradient(farthest-corner at top center, ..) */
        background-size:100% 100%;
      }
      50.1% {
        background-position:right top; 
      }
      to { /* radial-gradient(farthest-corner at top left, ..) */
        background-position:right top;
        background-size:200% 100%;
      }
     }
    <div id="shine-div">
      Shine
    </div>

    你也可以考虑伪元素和转换来做同样的动画以获得更好的性能:

    #shine-div {
      height: 30vh;
      width: 60vw;
      margin-right: auto;
      margin-left: auto;
      border-radius: 10px;
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: bold;
      overflow:hidden;
      position:relative;
      z-index:0;
    }
    #shine-div:before {
      content:"";
      position:absolute;
      z-index:-1;
      top:0;
      left:0;
      width:400%;
      height:200%;
      background: radial-gradient(farthest-corner at top, #FFFFFF 0%, #ffb3ff 4%, #ff33ff 12.25%, #800080 31.25%, #b300b3 50%);
      animation: colorChange 5s infinite alternate linear;
    }
    
    @keyframes colorChange {
      from {
        transform:translateX(-50%);
      }
      50% {
        transform:scaleX(0.75) translateX(-50%)
      }
      to {
        transform:translateX(-25%);
      }
     }
    <div id="shine-div">
      Shine
    </div>


    更深入

    为了使答案更通用,我将详细说明如何从两个不同位置对任何类型的渐变进行动画处理。主要技巧是以不同的方式编写渐变以使其定义为常量(radial-gradient(&lt;constant_definition&gt;))并为background-position(在某些情况下为background-size)设置动画

    假设我们的渐变为background:radial-gradient(Rh Rv at X Y, color1 p1, color2 p2),其中RhRy 分别是椭圆的水平半径和垂直半径(如果两者相等或仅使用一个值,则为圆形)。

    首先,我们将渐变的大小加倍。这个技巧将允许我们使用百分比值轻松调整渐变的位置(此处解释:Using percentage values with background-position on a linear-gradient

    如果半径是用像素值定义的,我们保留它,但如果它是用百分比值定义的,我们将它除以 2,因为它与他增加的大小有关。如果两个半径都是百分比,我们可以将两者除以 2,或者保留它们并将色标除以 2。

    其次,我们删除了at X Y,这将使渐变在中心,因此我们需要使用background-position来纠正位置。很明显,如果梯度在0 0,我们需要使用background-position:100% 100%

    绿色框是我们的背景,比元素(黑框)大两倍,红色圆圈是我们的渐变。通过调整背景位置,我们在视觉上将渐变定位在0 0

    对于任何XY 值,我们逻辑上都会有background-position:calc(100% - X) calc(100% - Y)

    如果X,Y是像素值,我们也可以使用background-position: right -X bottom -Y(注意它是-X而不是- X,我们使用负值)

    例子:

    有像素值

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(20% 100px at 20px 30px,red 30%,blue 60%);"></div>
    <div class="box" style="background:radial-gradient(10% 100px,red 30%,blue 60%) right -20px bottom -30px/200% 200%;"></div>
    <br>
    <div class="box" style="background:radial-gradient(40% 40% at 40px 50px,yellow 30%,blue);"></div>
    <div class="box" style="background:radial-gradient(40% 40%,yellow 15%,blue 50%) right -40px bottom -50px/200% 200%;"></div>
    <div class="box" style="background:radial-gradient(20% 20%,yellow 30%,blue) right -40px bottom -50px/200% 200%;"></div>

    有百分比值

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(20% 100px at 50% 10%,red 30%,blue 60%);"></div>
    <div class="box" style="background:radial-gradient(10% 100px,red 30%,blue 60%) calc(100% - 50%) calc(100% - 10%)/200% 200%;"></div>
    <br>
    <div class="box" style="background:radial-gradient(40% 40% at 30% 70%,yellow 30%,blue);"></div>
    <div class="box" style="background:radial-gradient(40% 40%,yellow 15%,blue 50%) calc(100% - 30%) calc(100% - 70%)/200% 200%;"></div>
    <div class="box" style="background:radial-gradient(20% 20%,yellow 30%,blue) calc(100% - 30%) calc(100% - 70%)/200% 200%;"></div>

    因此,如果我们想从以下位置为 gadient 设置动画:

    radial-gradient(Rh Rv at X Y, color1 p1, color2 p2)
    

    radial-gradient(Rh Rv at X1 Y2, color1 p1, color2 p2)
    

    我们以不同的方式编写它并为background-position 设置动画:

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    .first {
      background:radial-gradient(10% 100px,red 30%,blue 60%) calc(100% - 50%) calc(100% - 10%)/200% 200%;
      animation:change1 2s linear infinite alternate;
    }
    .second {
      background:radial-gradient(20% 20%,yellow 30%,blue)right -50px bottom 0/200% 200%;
      animation:change2 2s linear infinite alternate;
    }
    
    @keyframes change1 {
      to {
        background-position:calc(100% + 10%) calc(100% - 80%);
      }
    }
    
    @keyframes change2 {
      to {
        background-position:right -100px bottom -100px;
      }
    }
    <div class="box first" ></div>
    <div class="box second"></div>

    现在让我们考虑更棘手的情况,例如我们最初的示例,使用farthest-side 来定义大小。我们将做同样的逻辑并转换

    radial-gradient(farthest-side at X Y, color1 p1, color2 p2);
    

    radial-gradient(farthest-side, color1 p1, color2 p2) Px Py/Sx Sy no-repeat;
    

    我将解释一个轴(X),同样适用于另一个轴

    farthest-side 将半径定义为从渐变中心到渐变框最远边的距离(渐变框默认为元素本身,因为我们没有定义任何大小)。如果X 是一个百分比值,那么半径是X100% - X 之间的最大值,并且在转换后的梯度中,半径将为50%,因为我们位于中心。所以我们需要将第一个半径与50%*Sx匹配

    如果X50%,那么Sx 应该是100%,如果X0100%,那么Sx 应该是200%

    公式为Sx = max(X,100% - X)*2

    在这种情况下,由于形状应该接触一侧的渐变的性质,位置更容易

    • 如果X[0 50%[Px 应该是100% (right)
    • 如果X50%,则Px 的任何值都将有效,因为Sx=100%
    • 如果X]50% 100%]Px 应该是0% (left)

    相关问题:Using percentage values with background-position on a linear-gradient

    例子:

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(farthest-side at 20% 60%, red 20%, blue 100%, yellow 100%)" ></div>
    <div class="box" style="background:radial-gradient(farthest-side, red 20%, blue 100%, yellow 50%) 100% 0/calc(80%*2) calc(60%*2)"></div>
    <br>
    <div class="box" style='background:radial-gradient(farthest-side at 22% 100%,red 40%, blue 100%,yellow 100%)'></div>
    <div class="box" style="background:radial-gradient(farthest-side,red 40%, blue 100%,yellow 100%) 100% 0/calc(78%*2) calc(100%*2)"></div>

    对于farthest-corner,我们完全一样:

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(farthest-corner at 20% 60%, red 20%, blue 50%, yellow 60%)" ></div>
    <div class="box" style="background:radial-gradient(farthest-corner, red 20%, blue 50%, yellow 60%) 100% 0%/calc(80%*2) calc(60%*2)"></div>
    <br>
    <div class="box" style="background:radial-gradient(farthest-corner at 40% 100%, red 20%, blue 50%, yellow 60%)" ></div>
    <div class="box" style="background:radial-gradient(farthest-corner, red 20%, blue 50%, yellow 60%) 100% 0%/calc(60%*2) calc(100%*2)"></div>

    我们也可以将farthest-side(或farthest-corner)转换为Rh Rv并进行前面的计算,但这对动画没有用处,因为我们将有两个不同半径的渐变,而我们需要相同的渐变.

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(farthest-side at 20% 60%, red 20%, blue 100%, yellow 100%)" ></div>
    <div class="box" style="background:radial-gradient(80% 60% at 20% 60%, red 20%, blue 100%, yellow 100%)" ></div>
    <div class="box" style="background:radial-gradient(80% 60%, red 10%, blue 50%, yellow 50%) 80% 40%/200% 200%"></div>

    如果X 是像素值,我们有两种情况:

    • 元素具有固定宽度:在这种情况下,我们可以简单地将X 的像素值转换为宽度的百分比,并执行与上述相同的逻辑。
    • 元素具有可变宽度:在这种情况下,转换渐变会很棘手(可能不可能),因为形状会根据宽度而变化。当width-X &gt; X 时,我们将有一个变量 半径,而当width-X &lt; X 我们将有一个固定 半径。我认为我们无法使用background-sizebackground-position 来表达这一点。示例:

    body {
      margin:0;
      height:100vh;
      background:radial-gradient(farthest-side at 400px 200px,blue 40%,yellow 50%);
    }

    考虑到Sx=min(X,100% - X)*2closest-side 将执行相同的逻辑,但我们应该添加no-repeat 和等于渐变中最后一个颜色的background-color,因为大小小于100%

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(closest-side at 20% 60%, red 20%, blue 100%, yellow 100%)" ></div>
    <div class="box" style="background:radial-gradient(closest-side, red 20%, blue 100%, yellow 100%) 0 100%/calc(20%*2) calc(40%*2)"></div>
    <div class="box" style="background:radial-gradient(closest-side, red 20%, blue 100%, yellow 100%) 0 100%/calc(20%*2) calc(40%*2) no-repeat,yellow"></div>
    <br>
    <div class="box" style='background:radial-gradient(closest-side at 22% 10%,red 40%, blue 100%,yellow 100%)'></div>
    <div class="box" style="background:radial-gradient(closest-side,red 40%, blue 100%,yellow 100%) 0 0/calc(22%*2) calc(10%*2)"></div>
    <div class="box" style="background:radial-gradient(closest-side,red 40%, blue 100%,yellow 100%) 0 0/calc(22%*2) calc(10%*2) no-repeat,yellow"></div>

    我们可以对closest-corner 做同样的事情,但我们会遇到一些问题,因为渐变会溢出渐变框。

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(closest-corner at 20% 60%, red 20%, blue 100%, yellow 100%)" ></div>
    <div class="box" style="background:radial-gradient(closest-corner, red 20%, blue 100%, yellow 100%) 0 100%/calc(20%*2) calc(40%*2)"></div>
    <div class="box" style="background:radial-gradient(closest-corner, red 20%, blue 100%, yellow 100%) 0 100%/calc(20%*2) calc(40%*2) no-repeat,yellow"></div>

    为了解决这个问题,我们可以将色标除以 2,以确保将整个渐变保留在内部。然后我们将尺寸放大两倍,并纠正位置

    .box {
      height:150px;
      width:150px;
      border:1px solid;
      display:inline-block;
    }
    <div class="box" style="background:radial-gradient(closest-corner at 20% 60%, red 20%, blue 100%, yellow 100%)" ></div>
    <div class="box" style="background:radial-gradient(closest-corner, red 10%, blue 50%, yellow 50%) -100% 33%/calc(20%*4) calc(40%*4)"></div>
    <div class="box" style="background:radial-gradient(closest-corner, red 10%, blue 50%, yellow 50%) -100% 33%/calc(20%*4) calc(40%*4) no-repeat,yellow"></div>
    <br>
    <div class="box" style='background:radial-gradient(closest-corner at 22% 10%,red 40%, blue 100%,yellow 100%)'></div>
    <div class="box" style="background:radial-gradient(closest-corner,red 20%, blue 50%,yellow 50%) -100% 0%/calc(22%*4) calc(10%*4)"></div>
    <div class="box" style="background:radial-gradient(closest-corner,red 20%, blue 50%,yellow 50%) -164% -18%/calc(22%*4) calc(10%*4) no-repeat,yellow"></div>

    即使没有动画,也更支持不带at X Y 的渐变语法。部分浏览器如 Safari 不支持 at (How to make radial gradients work in Safari?)

    【讨论】:

      【解决方案2】:

      使用 CSS 变量和新的@property,我们可以轻松地为radial-gradient(或任何类型的渐变)设置动画。 The support 目前只介绍 Chrome 和 Edge。

      @property --x {
        syntax: '<percentage>';
        inherits: false;
        initial-value: 0%;
      }
      
      #shine-div {
        height: 30vh;
        width: 60vw;
        margin: auto;
        border-radius: 10px;
        background: radial-gradient(ellipse farthest-corner at var(--x) 0%, #FFFFFF 0%, #ffb3ff 8%, #ff33ff 25%, #800080 62.5%, #b300b3 100%);
        animation: colorChange 5s infinite alternate;
      }
      
      @keyframes colorChange {
        0% {
          --x:0%;
        }
        50% {
          --x:50%;
        }
        100% {
          --x:100%;
        }
      }
      &lt;div id="shine-div"&gt;&lt;/div&gt;

      我们所要做的就是使用变量--x 定义位置,该变量将使用百分比值,然后我们为该变量设置动画。就这么简单!

      【讨论】:

        【解决方案3】:

        SVG 解决方案

        作者并没有要求使用SVG 解决他的问题。但是以多种方式解决一个问题可能会很有用。
        渐变属性值取自 @Temani Afif 响应。
        本题的 SVG 径向渐变公式:

        <radialGradient id="radGrad"  fx="0%" fy="5%" r="200%">
             <stop offset="0%" stop-color ="#FFFFFF" />
              <stop offset="4%" stop-color ="#ffb3ff" />
               <stop offset="12.25%" stop-color ="#ff33ff" />
                <stop offset="31.25%" stop-color ="#800080" />
                  <stop offset="50%" stop-color ="#b300b3" /> 
        
           </radialGradient>
        

        要为渐变设置动画,您可以使用公式中包含的任何属性。
        下面的示例将使用属性fxfy

        • 水平渐变运动的动画

        点击矩形后动画开始

        svg {
         width:50%;
         height:50%;
         }
         .txt {
         font-family:sans-serif;
         font-size:28px;
         font-weight:bold;
         text-anchor:middle;
         fill:#FFDD00;
          }
        <div id="shine-div">
           <svg xmlns="http://www.w3.org/2000/svg" 
            xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 100">
           <defs>
          <radialGradient id="radGrad"  fx="0%" fy="0%" r="200%">
              <stop offset="0%" stop-color ="#FFFFFF" />
        	    <stop offset="4%" stop-color ="#ffb3ff" />
        	    <stop offset="12.25%" stop-color ="#ff33ff" />
        	    <stop offset="31.25%" stop-color ="#800080" />
        	    <stop offset="50%" stop-color ="#b300b3" /> 		 
          </radialGradient>
           </defs> 
            <g id="gr1" > 
              <rect id="rect1" fill="url(#radGrad)" x="5%" y="5%" width="95%" height="95%" rx="10%"/> 
               <text class="txt" x="50%" y="60%"> Sun shine </text>
        	</g>  
            <animate xlink:href="#radGrad"
        	  attributeName="fx"
        	  dur="3s"begin="gr1.click"
        	  values="0%;100%;0%"
        	  
        	  repeatCount="1"
        	  restart="whenNotActive" />
          </svg>
        </div>
        • 垂直渐变运动的动画。

        svg {
         width:50%;
         height:50%;
         }
         .txt {
         font-family:sans-serif;
         font-size:28px;
         font-weight:bold;
         text-anchor:middle;
         fill:#FFDD00;
          }
        <div id="shine-div">
           <svg xmlns="http://www.w3.org/2000/svg" 
            xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 100">
           <defs>
          <radialGradient id="radGrad"  fx="48%" fy="0%" r="200%">
                <stop offset="0%" stop-color ="#FFFFFF" />
        	    <stop offset="4%" stop-color ="#ffb3ff" />
        	    <stop offset="12.25%" stop-color ="#ff33ff" />
        	    <stop offset="31.25%" stop-color ="#800080" />
        	    <stop offset="50%" stop-color ="#b300b3" /> 		 
          </radialGradient>
           </defs> 
            <g id="gr1" > 
              <rect id="rect1" fill="url(#radGrad)" x="5%" y="5%" width="95%" height="95%" rx="10%"/> 
               <text class="txt" x="50%" y="60%"> Sun shine </text>
        	</g>  
            <animate xlink:href="#radGrad"
        	  attributeName="fy"
        	  dur="2s"begin="gr1.click"
        	  values="0%;50%;50%;100%;50%;50%;0%"
        	  keyTimes="0;0.1;0.5;0.6;0.7;0.9;1"
        	  repeatCount="1"
        	  restart="whenNotActive" />
          </svg>
        </div>
        • 沿对角线移动渐变

        两个属性同时动画:fxfy

        svg {
         width:50%;
         height:50%;
         }
         .txt {
         font-family:sans-serif;
         font-size:28px;
         font-weight:bold;
         text-anchor:middle;
         fill:#FFDD00;
          }
        <div id="shine-div">
           <svg xmlns="http://www.w3.org/2000/svg" 
            xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 100">
           <defs>
          <radialGradient id="radGrad"  fx="0%" fy="0%" r="200%">
                <stop offset="0%" stop-color ="#FFFFFF" />
        	    <stop offset="4%" stop-color ="#ffb3ff" />
        	    <stop offset="12.25%" stop-color ="#ff33ff" />
        	    <stop offset="31.25%" stop-color ="#800080" />
        	    <stop offset="50%" stop-color ="#b300b3" /> 		 
          </radialGradient>
           </defs> 
            <g id="gr1" > 
              <rect id="rect1" fill="url(#radGrad)" x="5%" y="5%" width="95%" height="95%" rx="10%"/> 
               <text class="txt" x="50%" y="60%"> Sun shine </text>
        	</g>  
            <animate xlink:href="#radGrad"
        	  attributeName="fy"
        	  dur="2s"begin="gr1.click"
        	  values="0%;50%;50%;100%;0%"
        	  keyTimes="0;0.1;0.5;0.9;1"
        	  repeatCount="1"
        	  restart="whenNotActive" />
        	  
        	     <animate xlink:href="#radGrad"
        			  attributeName="fx"
        			  dur="2s"begin="gr1.click"
        			  values="0%;50%;50%;100%;0%"
        			  keyTimes="0;0.1;0.5;0.9;1"
        			  repeatCount="1"
        			  restart="whenNotActive" />
          </svg>
        </div>

        【讨论】:

        • 我猜对于 SVG,您可以将半径保持在 100% 并使用 OP 提供的初始值。动画仍然有效,您不会遇到我在使用 CSS 时遇到的问题,即当背景为 100% 时我无法移动背景
        猜你喜欢
        • 1970-01-01
        • 2018-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多