Quiero replicar la animación GIF a continuación con CSS puro, ¿es posible y cómo? También estoy abierto a otros enfoques apropiados.

El fragmento a continuación es lo que tengo hasta ahora, es solo una cara estática.

body {
  background: #fff;
  margin: 50px;
}

.loader {
  position: relative;
  border-radius: 50%;
  box-sizing: border-box;
  width: 80px;
  height: 80px;
  background: linear-gradient(to bottom, #fff 50%, #51cf66 50%);
}

.loader:before {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  top: 10px;
  bottom: 10px;
  border-radius: 50%;
  background: #fff;
}

.dot {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #51cf66;
}

.dot:first-child {
  left: 10px;
  top: 10px;
}

.dot:last-child {
  right: 10px;
  top: 10px;
}
<div class="loader">
  <div class="dot"></div>
  <div class="dot"></div>
</div>

Rotating smiley face animation

9
Stickers 12 ene. 2018 a las 22:30

3 respuestas

La mejor respuesta

Aquí está la solución y debajo de la explicación paso a paso :)

.smile {
  margin: 30px;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background:radial-gradient(circle at center,#42bd56 10%,transparent 11%) 43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center,#42bd56 10%,transparent 11%) -43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center, #fff 35px, transparent 35px) 0 0/100% 100% no-repeat, 
  linear-gradient(to bottom, #fff 50%, transparent 50%) 0 0/100% 100% no-repeat, #42bd56;
  position: relative;
}

.smile:before,.smile:after{
      content: "";
    position: absolute;
    width: 100%;
    top: 50%;
    margin-top: -7px;
    height: 15px;
    background: 
    radial-gradient(circle at center,#42bd56 15%,transparent 16%) 43px 0px/100% 100% no-repeat;
    transform:rotate(-50deg);
    
}
.smile:after {
  background:radial-gradient(circle at center,#42bd56 15%,transparent 16%) -43px 0px/100% 100% no-repeat;
  transform:rotate(50deg);
}


.smile:hover {
  animation: rotateS 1.5s linear;
}

.smile:hover::before {
  animation: rotateL 1.5s linear;
}
.smile:hover::after {
  animation: rotateR 1.5s linear;
}

@keyframes rotateS {
  0% {
    transform: rotate(0deg);
    background-size: auto,auto,auto ,100% 100%, auto;
  }
  50% {
    transform: rotate(360deg);
    background-size: auto,auto,auto, 100% 50%, auto;
  }
  100% {
    transform: rotate(720deg);
    background-size: auto,auto,auto, 100% 100%, auto;
  }
}

@keyframes rotateR {
  0% {
    transform:rotate(50deg);
  }
  30%,70% {
    transform:rotate(-40deg);
  }
  100% {
    transform:rotate(50deg);
  }
}

@keyframes rotateL {
  0% {
    transform:rotate(-50deg);
  }
  30%,70% {
    transform:rotate(-180deg);
  }

  100% {
    transform:rotate(-50deg);
  }
}
Hover to animate
<div class="smile">
</div>

En esta solución, usaré solo un elemento . Voy a confiar en múltiples fondos con degradado lineal y degradado radial y también usaré pseudo-elemento.

La sonrisa

Usaré un degradado lineal y un degradado radial con un color de fondo para crear la forma principal, luego agregaré 2 pequeños degradados radiales para la esquina redondeada:

.smile {
  margin: 50px;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background:radial-gradient(circle at center,#42bd56 10%,transparent 11%) 43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center,#42bd56 10%,transparent 11%) -43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center, #fff 35px, transparent 35px) 0 0/100% 100% no-repeat, 
  linear-gradient(to bottom, #fff 50%, transparent 50%) 0 0/100% 100% no-repeat, #42bd56;
  position: relative;
}
<div class="smile">
</div>

Ahora, para rotar la sonrisa, usaré la rotación y también cambiaré el tamaño del gradiente lineal. Como puede ver en el GIF, durante la rotación, la curva aumenta, por lo que al cambiar el tamaño del gradiente lineal, la aumentaré.

enter image description here

Como puede observar, el pequeño gradiente radial utilizado para la esquina redondeada se oculta después del aumento, por lo que también podemos considerar moverlos al mismo tiempo, pero no lo haré para que el código no se complique .

Aquí está la animación (pase el cursor para activar):

.smile {
  margin: 50px;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background:radial-gradient(circle at center,#42bd56 10%,transparent 11%) 43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center,#42bd56 10%,transparent 11%) -43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center, #fff 35px, transparent 35px) 0 0/100% 100% no-repeat, 
  linear-gradient(to bottom, #fff 50%, transparent 50%) 0 0/100% 100% no-repeat, #42bd56;
  position: relative;
}

.smile:hover {
  animation: rotateS 1.5s linear;
}

@keyframes rotateS {
  0% {
    transform: rotate(0deg);
    background-size: auto,auto,auto ,100% 100%, auto;
  }
  50% {
    transform: rotate(360deg);
    background-size: auto,auto,auto, 100% 50%, auto;
  }
  100% {
    transform: rotate(720deg);
    background-size: auto,auto,auto, 100% 100%, auto;
  }
}
<div class="smile">
</div>

Los ojos

Para los ojos, usaré un pseudo elemento y un gradiente radial para crear el círculo. No crearé simplemente un círculo con un radio de borde, ya que también necesito rotarlos en el mismo eje que la sonrisa.

Entonces, la idea es hacerlos de ancho completo y usar el gradiente radial para tener el círculo a la derecha / izquierda. Al hacer esto, puedo controlar fácilmente su posición con rotación y asegurarme de que se mantengan en la posición necesaria en cualquier grado de rotación.

enter image description here

.smile {
  margin: 50px;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background:radial-gradient(circle at center,#42bd56 10%,transparent 11%) 43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center,#42bd56 10%,transparent 11%) -43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center, #fff 35px, transparent 35px) 0 0/100% 100% no-repeat, 
  linear-gradient(to bottom, #fff 50%, transparent 50%) 0 0/100% 100% no-repeat, #42bd56;
  position: relative;
}

.smile:before,.smile:after{
      content: "";
    position: absolute;
    width: 100%;
    top: 50%;
    margin-top: -7px;
    height: 15px;
    background: 
    radial-gradient(circle at center,#42bd56 15%,transparent 16%) 43px 0px/100% 100% no-repeat;
    transform:rotate(-50deg);
    
}
.smile:after {
  background:radial-gradient(circle at center,#42bd56 15%,transparent 16%) -43px 0px/100% 100% no-repeat;
  transform:rotate(50deg);
}
<div class="smile">
</div>

Ahora para la animación, mantendré la primera rotación que también rotará los ojos, pero agregaré un poco de animación a los ojos para hacer que se muevan en la dirección opuesta y crear la ilusión de la sonrisa que va por encima de ellos. Por lo tanto, simplemente agregaré una rotación negativa a los ojos para que parezcan moverse más lentamente y así ir por encima de la sonrisa y crear el efecto necesario:

Aquí está nuevamente la animación completa :)

.smile {
  margin: 50px;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background:radial-gradient(circle at center,#42bd56 10%,transparent 11%) 43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center,#42bd56 10%,transparent 11%) -43px 0px/100% 100% no-repeat,
  radial-gradient(circle at center, #fff 35px, transparent 35px) 0 0/100% 100% no-repeat, 
  linear-gradient(to bottom, #fff 50%, transparent 50%) 0 0/100% 100% no-repeat, #42bd56;
  position: relative;
}

.smile:before,.smile:after{
      content: "";
    position: absolute;
    width: 100%;
    top: 50%;
    margin-top: -7px;
    height: 15px;
    background: 
    radial-gradient(circle at center,#42bd56 15%,transparent 16%) 43px 0px/100% 100% no-repeat;
    transform:rotate(-50deg);
    
}
.smile:after {
  background:radial-gradient(circle at center,#42bd56 15%,transparent 16%) -43px 0px/100% 100% no-repeat;
  transform:rotate(50deg);
}


.smile:hover {
  animation: rotateS 1.5s linear;
}

.smile:hover::before {
  animation: rotateL 1.5s linear;
}
.smile:hover::after {
  animation: rotateR 1.5s linear;
}

@keyframes rotateS {
  0% {
    transform: rotate(0deg);
    background-size: auto,auto,auto ,100% 100%, auto;
  }
  50% {
    transform: rotate(360deg);
    background-size: auto,auto,auto, 100% 50%, auto;
  }
  100% {
    transform: rotate(720deg);
    background-size: auto,auto,auto, 100% 100%, auto;
  }
}

@keyframes rotateR {
  0% {
    transform:rotate(50deg);
  }
  30%,70% {
    transform:rotate(-40deg);
  }
  100% {
    transform:rotate(50deg);
  }
}

@keyframes rotateL {
  0% {
    transform:rotate(-50deg);
  }
  30%,70% {
    transform:rotate(-180deg);
  }

  100% {
    transform:rotate(-50deg);
  }
}
<div class="smile">
</div>

En esta solución, utilicé una transición lineal para simplificar, pero se puede cambiar a cualquier función de facilidad para tener una transición similar a la del GIF

7
Temani Afif 17 ene. 2018 a las 23:09

Aunque esta animación sonriente se puede lograr utilizando solo CSS, como han dicho otros, SVG es claramente una mejor opción, principalmente por estos motivos:

  • animar gradientes no es el mejor rendimiento sabio
  • Hacer la ronda a cada lado de la sonrisa será muy difícil de lograr sin agregar un marcado semántico
  • SVG es una herramienta hecha para dibujar este tipo de formas y animarlas también. Los hace más fáciles de mantener y el código es más fácil de leer y entender

Dicho esto, hice un ejemplo de su animación de smiley .
Esto es lo que parece:

Smiley animation with CSS and SVG

Y aquí está el código (simplificado del codepen demo):

svg {
  width:100px;
  height:auto;
  display:block;
  transform:rotateZ(0deg);
  margin:0 auto;
}
.smile, .eyes {
  stroke:teal;
  stroke-width:1.3;
  stroke-linecap:round;
  fill:transparent;
}
svg:hover {animation:rotate 1.2s cubic-bezier(0.65, 0.000, 0.75, 1.000);}
svg:hover .smile{animation: smile 1s cubic-bezier(0.2, 0.000, 0.8, 1.000);}
svg:hover .eyes{animation: eyes 1s cubic-bezier(.7, 0.000, 0.4, 1.000);}

@keyframes rotate { to { transform:rotateZ(720deg); } }
@keyframes smile { 50% { stroke-dasharray:20,5.1327;} }
@keyframes eyes  { 70% { stroke-dasharray:1,0,.5,23.6327;} }
 
 h1 {text-align:center;color:teal;}
<svg viewbox="0 0 10 10">
  <circle class="smile" cx="5" cy="5" r="4" stroke-dashoffset="-.5" stroke-dasharray="11.5,13.6327" />
  <circle class="eyes" cx="5" cy="5" r="4" stroke-dashoffset="-15.5" stroke-dasharray="0,6.6327,0,17.5" />
</svg>
<h1>Hover me !</h1>

¿Cómo se hacen el smiley y la animación?

Este smiley está hecho con dos elementos de círculo SVG (uno para los ojos y uno para la sonrisa) y el stroke- dasharray atributo para hacer los ojos y la sonrisa.

Animación:
El svg se gira al pasar el mouse usando animación CSS y el atributo stroke-dasharray se anima para hacer que los ojos "desaparezcan" en la sonrisa. La longitud de la sonrisa también se cambia a aproximadamente 3/4 de círculo.

Función de aceleración:
El efecto suave se realiza con las funciones de alivio de la curva bezier. Los tiempos de animación también se ajustan para acercarse a la animación de smiley deseada.

5
web-tiki 17 ene. 2018 a las 16:34

Hmmm para ese tipo de animación, sería preferible ajustar un SVG de CSS.

La técnica que está utilizando para dibujar un smiley CSS completo se basa en un fondo de línea para ajustar la "longitud" de la línea, lo que sería ridículamente intensivo en recursos al tratar de animar (aumentar la longitud) mientras gira.

Lo más cerca que pude ajustar tu código con CSS puro y sin SVG es esto, espero que ayude

body {
  background: #fff;
  margin: 50px;
}

.smiley{
  position:relative;
  width: 70px;
  height: 70px;
}

.loader {
  position: relative;
  border-radius: 50%;
  box-sizing: border-box;
  width: 70px;
  height: 70px;
  background: linear-gradient(to bottom, #fff 50%, #51cf66 50%);
  animation: rotate 2s infinite;
}

.loader:before {
  content: "";
  position: absolute;
  left: 8px;
  right: 8px;
  top: 8px;
  bottom: 8px;
  border-radius: 50%;
  background: #fff;
}

.dot-left, .dot-right {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #51cf66;
}

.dot-left {
  left: 4px;
  top: 10px;
  animation: dissapearL 2s infinite;
}

.dot-right {
  right: 6px;
  top: 10px;
  animation: dissapearR 2s infinite;
}

@keyframes rotate{
  from{
    transform:rotate(0);
  }
  to{
    transform:rotate(720deg);
  }
}

@keyframes dissapearL{
  0%{
    transform:scale(1);
  }
  10%{
    transform:scale(1);
  }
  15%{
    transform:scale(0);
  }
  45%{
    transform:scale(0);
  }
  50%{
    transform:scale(1);
  }
}

@keyframes dissapearR{
  0%{
    transform:scale(1);
  }
  15%{
    transform:scale(1);
  }
  20%{
    transform:scale(0);
  }
  65%{
    transform:scale(0);
  }
  70%{
    transform:scale(1);
  }
}
<div class="smiley ">
  <div class="loader">
  </div>
  <div class="dot-left"></div>
  <div class="dot-right"></div>
</div>  
4
Facundo Corradini 12 ene. 2018 a las 20:34
48232891