Estoy tratando de animar un menú de hamburguesa haciendo que la línea inferior y superior se traduzcan al medio y luego giren en una X y quiero invertir la animación cuando se hace clic en la X. Usando jquery, estoy alternando el menú de clase abierto y cerrado. Cuando elimino el CSS para la animación de menú cerrado, funciona bien, pero cuando vuelvo a agregar el CSS, las animaciones saltan al último cuadro. Forma lo que quiero pero simplemente se niega a usar la animación por completo.
CSS
.navbar .mobile-menu.menu-open .line::before { animation: menu-open-top 250ms linear forwards; } .navbar .mobile-menu.menu-open .line { animation: menu-middle 250ms linear forwards; } .navbar .mobile-menu.menu-open .line::after { animation: menu-open-bottom 250ms linear forwards; } .navbar .mobile-menu.menu-closed .line::before { animation: menu-open-top 250ms linear reverse; } .navbar .mobile-menu.menu-closed .line { animation: menu-middle 250ms linear reverse; } .navbar .mobile-menu.menu-closed .line::after { animation: menu-open-bottom 250ms linear reverse; }Animación
@keyframes menu-open-top { 30% { bottom: 0; } 60% { bottom: 0; transform: rotate(0) translate(0); } 100% { transform: rotate(45deg) translate(5px, 5px); visibility: visible; } } @keyframes menu-middle { 40% { visibility: hidden; } to { visibility: hidden; } } @keyframes menu-open-bottom { 30% { top: 0; } 60% { top: 0; transform: rotate(0) translate(0); } 100% { transform: rotate(-45deg) translate(6px, -6px); visibility: visible; } }JS
$(".mobile-menu").click(expandMenu); function expandMenu() { $(".primary-nav").toggleClass("menu-expand"); $(this).toggleClass("menu-open menu-closed"); }Debo estar perdiendo algo o tal vez necesito agregar nuevos fotogramas clave para la animación inversa, pero parece que sería innecesario.
editar: aquí está el html también
HTML
<div class="mobile-menu menu-closed"> <div class="line"></div> </div>A continuación, se explica cómo hacerlo utilizando cambios de valor de prop simples con una sincronización cuidadosa. Supongo que también se puede hacer usando animaciones @keyframe, pero las encuentro más difíciles de seguir/controlar/sincronizar, al menos en este caso, considerando que es (básicamente) una animación de dos pasos.
document.querySelector('.mobile-menu').addEventListener('click', ({ target }) => { target.closest('.mobile-menu').classList.toggle('menu-open'); }) .mobile-menu { --duration: 0.42s; --size: 3rem; --padding: 0.5rem; --color: red; --distance-timing: cubic-bezier(0.5, 0, 0.3, 1); --rotation-timing: cubic-bezier(0.4, 0, 0.2, 1); width: var(--size); height: var(--size); padding: var(--padding); display: flex; flex-direction: column; justify-content: space-between; cursor: pointer; } .mobile-menu * { box-sizing: border-box; } .mobile-menu>div { border: 1px solid var(--color); height: 0; width: 100%; position: relative; transform-origin: 50% 50%; transition: top calc(0.6 * var(--duration)) var(--distance-timing) calc(0.4 * var(--duration)), bottom calc(0.6 * var(--duration)) var(--distance-timing) calc(0.4 * var(--duration)), transform calc(0.8 * var(--duration)) var(--rotation-timing) 0s; } .mobile-menu> :nth-child(1) { top: calc(var(--padding)/2); } .mobile-menu> :nth-child(3) { bottom: calc(var(--padding)/2); } .mobile-menu.menu-open>div { transition: top calc(0.4 * var(--duration)) var(--distance-timing) 0s, bottom calc(0.4 * var(--duration)) var(--distance-timing) 0s, transform calc(0.8 * var(--duration)) var(--rotation-timing) calc(0.2 * var(--duration)); } .mobile-menu.menu-open> :nth-child(1) { top: calc(50% - 1px); transform: rotate(0.125turn); } .mobile-menu.menu-open> :nth-child(2) { transform: rotate(0.125turn); } .mobile-menu.menu-open> :nth-child(3) { bottom: calc(50% - 1px); transform: rotate(-0.125turn); } <div class="mobile-menu"> <div></div> <div></div> <div></div> </div> Lo mismo, en SCSS: https://jsfiddle.net/websiter/dybre2f9/
Extraje los valores en CSS vars, por lo que se pueden reutilizar y modificar con facilidad. Siéntete libre de modificarlo a tu gusto.
Nota: la razón por la que estoy usando bottom y top para animar el movimiento (y no translateY , que es un poco más eficaz) es porque quería que las dos animaciones fueran completamente independientes entre sí, para permitirme jugar con varias superposiciones. valores y funciones de temporización. Lo que se me ocurrió no respeta el requisito al 100% (es decir, superpone ligeramente la rotación con el movimiento, pero lo hago a propósito, ya que no superponerlos parece demasiado mecánico). Cuando se superpone, toda la animación parece más orgánica. Es como si el botón estuviera vivo y feliz de que se le haya pedido que haga la animación. O tal vez estoy un poco loco, no sé...