web-dev-qa-db-fra.com

Comment animer un gradient radial en utilisant CSS?

J'essaie de créer un effet d'éclat à gradient radial sur une boîte div et je ne sais pas quelle est la meilleure façon de le faire. Je n'ai trouvé aucune ressource pour réaliser ce que je veux réaliser; juste des effets de brillance qui ressemblent à une superposition.

La plupart des exemples que j'ai trouvés ressemblent à ceci http://jsfiddle.net/nqQc7/512/ .

Ci-dessous, j'ai affiché ce que j'essaie de créer.

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

Est-il possible de faire cela? J'aimerais aussi faire briller le blanc sur le dessus pour aller de gauche à droite en douceur? Suis-je même sur la bonne voie avec ma tentative?

9
Lv007

Vous pouvez faire le dégradé différemment et animer la position. L'astuce consiste à doubler la taille du dégradé et à faire en sorte que la valeur de la couleur s'arrête à la moitié de leurs valeurs réelles afin de conserver le même dégradé visuel, puis de l'animer de gauche à droite.

Il ne sera pas exactement identique au dégradé que vous avez défini dans l'animation en raison du calcul du coin le plus éloigné.

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

Pour vous rapprocher de vos dégradés, vous devez également animer le background-size (voir ci-dessous pour les détails du calcul)

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

Vous pouvez également faire la même animation en considérant le pseudo-élément et la transformation pour avoir de meilleures performances:

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


Plus en profondeur

Pour rendre la réponse plus générique, je vais détailler comment vous pouvez animer tout type de dégradé à partir de deux positions différentes. L'astuce principale est d'écrire le gradient différemment pour que sa définition soit constante (radial-gradient(<constant_definition>)) Et d'animer le background-position (Et le background-size Dans certains cas)

Considérons notre gradient comme background:radial-gradient(Rh Rv at X Y, color1 p1, color2 p2)Rh et Ry sont respectivement le rayon horizontal et le rayon vertical de notre ellipse (si les deux sont égaux ou si une seule valeur est utilisée alors c'est un cercle).

Tout d'abord, nous doublons la taille du dégradé. Cette astuce nous permettra d'ajuster facilement la position du dégradé en utilisant la valeur en pourcentage (expliqué ici: en utilisant des valeurs en pourcentage avec la position d'arrière-plan sur un gradient linéaire )

Si le rayon est défini avec des valeurs en pixels, nous le conservons mais s'il est défini avec une valeur en pourcentage, nous le divisons par 2 car il est relatif à la taille qu'il a augmentée. Si les deux rayons sont en pourcentage, nous pouvons diviser les deux par 2 ou les conserver et diviser les arrêts de couleur par 2.

Deuxièmement, nous supprimons le at X Y Qui apportera le gradient au centre, nous devons donc rectifier la position en utilisant background-position. Il est clair que si le gradient était à 0 0, Nous devons utiliser background-position:100% 100%

enter image description here

La boîte verte est notre arrière-plan deux fois plus grand que l'élément (la boîte noire) et le cercle rouge est notre dégradé. En ajustant la position d'arrière-plan, nous positionnons visuellement le dégradé à 0 0.

Pour toutes les valeurs X, Y, nous aurons logiquement background-position:calc(100% - X) calc(100% - Y)

Si X, Y sont des valeurs en pixels, nous pouvons également utiliser background-position: right -X bottom -Y (Notez que '-X Et non - X, Nous utilisons la valeur négative)

Exemples:

Avec des valeurs de pixels

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

Avec des valeurs en pourcentage

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

Donc, si nous voulons animer un gadient à partir de:

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

à

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

nous l'écrivons différemment et nous animons le 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>

Considérons maintenant des cas plus délicats, comme notre exemple initial, en utilisant farthest-side Afin de définir la taille. Nous ferons la même logique et convertirons

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;

Je vais expliquer pour un axe (X) et la même chose s'applique à l'autre

farthest-side Définit le rayon comme étant la distance entre le centre du dégradé et le côté le plus éloigné de la boîte de dégradé (la boîte de dégradé est par défaut l'élément lui-même puisque nous n'avons défini aucune taille). Si X est une valeur en pourcentage, le rayon est le maximum entre X et 100% - X Et dans le dégradé transformé le rayon sera 50% Puisque nous sommes à le centre. Nous devons donc faire correspondre le premier rayon avec 50%*Sx

Si X est 50% Alors Sx doit être 100% Et si X est 0 Ou 100% alors Sx devrait être 200%.

La formule est Sx = max(X,100% - X)*2

La position est plus facile dans ce cas en raison de la nature du dégradé où la forme doit toucher un côté

  • Si X dans [0 50%[Px doit être 100% (right)
  • Si X est 50%, Toute valeur pour Px fonctionnera puisque Sx=100%
  • Si X dans ]50% 100%]Px devrait être 0% (left)

Question connexe: tilisation de valeurs de pourcentage avec position d'arrière-plan sur un gradient linéaire

Exemples:

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

Pour le farthest-corner Nous faisons exactement la même chose:

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

Nous pouvons également transformer farthest-side (Ou farthest-corner) En Rh Rv Et faire le calcul précédent mais cela ne sera pas utile pour l'animation car nous aurons deux dégradés de rayon différent alors que nous avons besoin du même 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(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>

Si X est une valeur de pixel, nous avons deux cas:

  • L'élément a une largeur fixe: dans ce cas, nous pouvons simplement convertir la valeur en pixels de X en pourcentage de la largeur et nous faisons la même logique que ci-dessus.
  • L'élément a une largeur variable: dans ce cas, il serait difficile de convertir le dégradé (probablement impossible) car la forme changera en fonction de la largeur. Lorsque width-X > X Nous aurons un rayon variable et lorsque width-X < X Nous aurons un rayon fixe . Je ne pense pas que nous puissions l'exprimer en utilisant background-size Et background-position. Exemple:
body {
  margin:0;
  height:100vh;
  background:radial-gradient(farthest-side at 400px 200px,blue 40%,yellow 50%);
}

Pour le closest-side Fera la même logique considérant Sx=min(X,100% - X)*2 MAIS nous devons ajouter no-repeat Et un background-color Égal à la dernière couleur du dégradé depuis la taille est inférieur à 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>

Nous pouvons faire la même chose pour closest-corner Mais nous aurons un problème en raison du fait que le dégradé peut déborder de la zone de dégradé.

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

Pour rectifier cela, nous pouvons diviser l'arrêt de couleur par 2 pour nous assurer que nous conservons tout le dégradé à l'intérieur. Ensuite, nous augmentons la taille deux fois et nous rectifions la position

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

Même sans animation, la syntaxe du dégradé sans le at X Y Est plus prise en charge. Certains navigateurs comme Safari ne prennent pas en charge le at ( Comment faire fonctionner les dégradés radiaux dans Safari? )

15
Temani Afif

Solution SVG

L'auteur n'a pas demandé de solution à son problème en utilisant SVG. Mais il sera probablement utile de résoudre un problème de plusieurs manières.
Les valeurs d'attribut de gradient ont été tirées de la réponse @Temani Afif.
La formule de gradient radial SVG pour cette question:

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

Pour animer le dégradé, vous pouvez utiliser n'importe quel attribut inclus dans la formule.
Les exemples ci-dessous utiliseront les attributs fx et fy

  • Animation du mouvement de dégradé horizontal

L'animation démarre après avoir cliqué sur un rectangle

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>
  • Une animation du mouvement de dégradé vertical.
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>
  • Déplacement du dégradé en diagonale

Deux attributs sont animés simultanément: fx et fy

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>
4
Alexandr_TT