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.
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-shadow
La 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:
- 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.
- 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. - 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.