Borde de animación | Programar Plus

Transición border para un estado flotante. Simple, ¿verdad? Puede que te sorprendas desagradablemente.

El reto

El desafío es simple: construir un botón con un borde expandible al pasar el mouse.

Este artículo se centrará en trucos CSS genuinos que serían fáciles de colocar en cualquier proyecto sin tener que tocar el DOM o usar JavaScript. Los métodos cubiertos aquí seguirán estas reglas

  • Elemento único (no se permiten divs auxiliares, pero se permiten psuedo-elementos)
  • Solo CSS (sin JavaScript)
  • Funciona para cualquier tamaño (no restringido a un ancho, alto o relación de aspecto específicos)
  • Admite fondos transparentes
  • Transición suave y eficaz

Propuse este desafío en Animation at Work Slack y nuevamente en Gorjeo. Aunque no hubo consenso sobre el mejor enfoque, recibí algunas ideas realmente inteligentes de algunos desarrolladores fenomenales.

Método 1: animación border

La forma más sencilla de animar un borde es … bueno, animando border.

.border-button {
  border: solid 5px #FC5185;
  transition: border-width 0.6s linear;
}

.border-button:hover { border-width: 10px; }

Vea el Pen by Shaw (@shshaw) en CodePen.

Agradable y simple, pero hay algunos problemas importantes de rendimiento.

Ya que border ocupa espacio en el diseño del documento, cambiando el border-width activará el diseño. Los elementos cercanos cambiarán debido al nuevo tamaño del borde, lo que hará que el navegador reposicione esos elementos en cada fotograma de la animación, a menos que establezca un tamaño explícito en el botón.

Como si activar el diseño no fuera lo suficientemente malo, la transición en sí se siente “escalonada”. Mostraré por qué en el siguiente ejemplo.

Método 2: mejor border con outline

¿Cómo podemos cambiar el borde sin activar el diseño? Mediante el uso outline ¡en lugar de! Probablemente estés más familiarizado con outline de quitarlo en :focus estilos (aunque no debería), pero outline es una línea exterior que no cambia el tamaño o la posición de un elemento en el diseño.

.border-button {
  outline: solid 5px #FC5185;
  transition: outline 0.6s linear;
  margin: 0.5em; /* Increased margin since the outline expands outside the element */
}

.border-button:hover { outline-width: 10px; }

Vea el Pen by Shaw (@shshaw) en CodePen.

Una comprobación rápida en la pestaña Rendimiento de las herramientas de desarrollo muestra la outline La transición no activa el diseño. Independientemente, el movimiento todavía parece escalonado porque los navegadores están redondeando el border-width y outline-width valores para que no obtenga una representación de subpíxeles entre 5 y 6 o transiciones suaves desde 5.4 a 5.5.

Vea el Pen by Shaw (@shshaw) en CodePen.

Curiosamente, Safari a menudo no representa el outline transición y ocasionalmente deja artefactos locos.

artefacto fronterizo en safari

Método 3: Córtalo con clip-path

Implementado por primera vez por Steve Gardner, este método usa clip-path con calc para recortar el borde hacia abajo, así que al pasar el mouse, podemos hacer la transición para revelar el borde completo.

.border-button {  
  /* Full width border and a clip-path visually cutting it down to the starting size */
  border: solid 10px #FC5185; 
  clip-path: polygon( 
    calc(0% + 5px) calc(0% + 5px), /* top left */
    calc(100% - 5px) calc(0% + 5px), /* top right */
    calc(100% - 5px) calc(100% - 5px), /* bottom right */
    calc(0% + 5px) calc(100% - 5px) /* bottom left */
  );
  transition: clip-path 0.6s linear;
}

.border-button:hover {
  /* Clip-path spanning the entire box so it's no longer hiding the full-width border. */
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

Vea el Pen by Shaw (@shshaw) en CodePen.

clip-path La técnica es el método más suave y eficaz hasta ahora, pero viene con algunas advertencias. Los errores de redondeo pueden causar un pequeño desnivel, dependiendo del tamaño exacto. El borde también debe ser de tamaño completo desde el principio, lo que puede dificultar el posicionamiento exacto.

Desafortunadamente, todavía no hay soporte para IE / Edge, aunque parece estar en desarrollo. Puede y debe alentar al equipo de Microsoft a implementar esas características votando para que se agreguen máscaras / ruta de clip.

Método 4: linear-gradient antecedentes

Podemos simular un borde usando una combinación inteligente de múltiples linear-gradient fondos del tamaño adecuado. En total tenemos cuatro gradientes separados, uno para cada lado. El background-position y background-size las propiedades colocan cada degradado en el lugar correcto y el tamaño correcto, que luego se puede cambiar para hacer que el borde se expanda.

.border-button {
  background-repeat: no-repeat;
  
  /* background-size values will repeat so we only need to declare them once */
  background-size: 
    calc(100% - 10px) 5px, /* top & bottom */
    5px calc(100% - 10px); /* right & left */
  
  background-position: 
    5px 5px, /* top */
    calc(100% - 5px) 5px, /* right */
    5px calc(100% - 5px), /* bottom */
    5px 5px; /* left */
  
  /* Since we're sizing and positioning with the above properties, we only need to set up a simple solid-color gradients for each side */
  background-image: 
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185),
    linear-gradient(0deg, #FC5185, #FC5185);
  
  transition: all 0.6s linear;
  transition-property: background-size, background-position;
}

.border-button:hover {
  background-position: 0 0, 100% 0, 0 100%, 0 0;
  background-size: 100% 10px, 10px 100%, 100% 10px, 10px 100%;
}

Vea el Pen by Shaw (@shshaw) en CodePen.

Este método es bastante difícil de configurar y tiene bastantes diferencias entre navegadores. Firefox y Safari animan el borde falso sin problemas, exactamente el efecto que estamos buscando. La animación de Chrome es desigual e incluso más escalonada que la outline y border transiciones. IE y Edge se niegan a animar el background en absoluto, pero dan el efecto de expansión de la frontera adecuado.

Método 5: Fíngelo con box-shadow

Escondido dentro box-shadowLa especificación es un cuarto valor para spread-radius. Establezca todos los demás valores de longitud en 0px y use el radio de extensión para construir su border alternativa que, como outline, no afectará el diseño.

.border-button {
  box-shadow: 0px 0px 0px 5px #FC5185;
  transition: box-shadow 0.6s linear;
  margin: 0.5em; /* Increased margin since the box-shado expands outside the element, like outline */
}

.border-button:hover { box-shadow: 0px 0px 0px 10px #FC5185; }

Vea el Pen by Shaw (@shshaw) en CodePen.

La transición con box-shadow tiene un rendimiento adecuado y se siente mucho más suave, excepto en Safari, donde se ajusta a valores enteros durante la transición, como border y outline.

Pseudoelementos

Varias de estas técnicas se pueden modificar para usar un pseudoelemento en su lugar, pero los pseudoelementos terminaron causando algunos problemas de rendimiento adicionales en mis pruebas.

Para el box-shadow método, la transición de vez en cuando desencadenaba pintura en un área mucho más grande de lo necesario. Reinier Kaper señaló que un pseudo-elemento puede ayudar a aislar la pintura en un área más específica. Mientras realizaba más pruebas, box-shadow ya no estaba causando pintura en grandes áreas del documento y la complicación del pseudo-elemento terminó siendo menos eficaz. El cambio en la pintura y el rendimiento puede deberse a una actualización de Chrome, así que no dude en probarlo usted mismo.

Tampoco pude encontrar una manera de utilizar pseudo-elementos de una manera que permitiera transform animación basada.

Por qué no transform: scale?

Es posible que esté activando Twitter para sugerir útilmente el uso transform: scale para esto. Ya que transform y opacity Cuáles son las mejores propiedades de estilo para animar para el rendimiento, ¿por qué no usar un pseudo-elemento y hacer que el borde se amplíe hacia arriba y hacia abajo?

.border-button {
  position: relative;
  margin: 0.5em;
  border: solid 5px transparent;
  background: #3E4377;
}

.border-button:after {
  content: '';
  display: block;
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  border: solid 10px #FC5185;
  margin: -15px;
  z-index: -1;
  transition: transform 0.6s linear;
  transform: scale(0.97, 0.93);
}

.border-button:hover::after { transform: scale(1,1); }

Vea el Pen by Shaw (@shshaw) en CodePen.

Hay algunos problemas:

  1. El borde se mostrará a través de un botón transparente. Forcé un fondo en el botón para mostrar cómo se oculta el borde detrás del botón. Si su diseño requiere botones con un fondo completo, entonces esto podría funcionar.
  2. No puede escalar el borde a tamaños específicos. Dado que las dimensiones del botón varían con el texto, no hay forma de animar el borde de exactamente 5px a 10px usando solo CSS. En este ejemplo he hecho algunos números mágicos en el scale para que parezca correcto, pero eso no será universal.
  3. El borde se anima de manera desigual porque la relación de aspecto del botón no es 1: 1. Esto generalmente significa que la izquierda / derecha aparecerá más grande que la parte superior / inferior hasta que se complete la animación. Esto puede no ser un problema dependiendo de qué tan rápida sea su transición, la relación de aspecto del botón y qué tan grande sea su borde.

Si su botón tiene dimensiones establecidas, Cher señaló un forma inteligente de calcular las escalas exactas necesarias, aunque puede estar sujeto a algunos errores de redondeo.

Más allá de CSS

Si aflojamos un poco nuestras reglas, hay muchas formas interesantes de animar las fronteras. Codrops realiza constantemente un trabajo sobresaliente en esta área, generalmente utilizando SVG y JavaScript. Los resultados finales son muy satisfactorios, aunque pueden ser un poco complejos de implementar. Aquí hay algunos que vale la pena consultar:

  • Botones creativos
  • Inspiración de estilos de botones
  • Casillas de verificación animadas
  • Efectos de botón distorsionados
  • Estilos de botones de progreso

Conclusión

Hay más en las fronteras que simplemente border, pero si desea animar un borde, es posible que tenga algunos problemas. Los métodos cubiertos aquí ayudarán, aunque ninguno de ellos es una solución perfecta. Lo que elija dependerá de los requisitos de su proyecto, por lo que he presentado una tabla de comparación para ayudarlo a decidir.

Vea el Pen by Shaw (@shshaw) en CodePen.

Mi recomendación sería utilizar box-shadow, que tiene el mejor equilibrio general de facilidad de implementación, efecto de animación, rendimiento y compatibilidad con el navegador.

¿Tienes otra forma de crear un borde animado? ¿Quizás una forma inteligente de utilizar transformaciones para mover una frontera? Comente a continuación o comuníquese conmigo en Twitter para compartir su solución al desafío.

Agradecimientos especiales a Martín Pitt, Steve Gardner, Cher, Reinier Kaper, Joseph Rex, David Khourshidy la comunidad de Animación en el trabajo.