Tengo un pequeño problema complicado.
Tengo un contenedor que se está coloreando al hacer clic en él. Al hacer clic en él, se enviarán datos a la matriz, pero ese no es el problema.
Al hacer clic en él, también se animará el cambio de color. La animación se activará hacia adelante al hacer clic y hacia atrás si se vuelve a hacer clic en el contenedor.
El problema es que la animación se activa condicionalmente con el uso de useState (booleano) y, por lo tanto, la segunda animación ya se ejecutará cuando se cargue la página, incluso antes de hacer clic en el contenedor. Me preguntaba sobre el estilo de estado de reproducción de animación, pero no parece ser suficiente.
Aquí está la demostración editable https://qeevsk.csb.app/ ¿Alguna idea, tal vez hay un mejor enfoque?
El código en sí:
const [activeDiv, setDivState] = useState(false); const BigDiv = styled("div")( ({ theme, activeDiv }) => css` min-width: 220px; height: 100%; position: relative; padding: ${theme.spacing(2, 0)}; border-bottom: red 4px solid; background-color: transparent; cursor: pointer; text-align: center; transition-delay: 0.5s; will-change: transform; ::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 100%; background-color: ${activeDiv ? "red" : "transparent"}; animation: ${activeDiv ? setColor("red") : unsetColor("red")} 0.5s ease ; transform-origin: bottom center}; } ` ); const setColor = (colorTo) => keyframes` 0% {transform: scaleY(0); } 100% {transform: scaleY(1); background-color: ${colorTo}} `; const unsetColor = (colorFrom) => keyframes` 0% {transform: scaleY(1); background-color: ${colorFrom}} 100% {transform: scaleY(0); background-color: transparent;} `; const SecondDiv = styled("div")( ({ theme, activeDiv, color }) => css` display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; position: relative; z-index: 1; ` ); return ( <> <BigDiv activeDiv={activeDiv} onClick={(e) => { setDivState(!activeDiv); }} > <SecondDiv> <p>Click to animate</p> </SecondDiv> </BigDiv> </> ); }Ya obtuvo algunas respuestas válidas, pero si está buscando un enfoque diferente, puede administrar el estado de la animación con un tercer valor como "IDLE":
const AnimStatus = { IDLE: -1, INACTIVE: 0, ACTIVE: 1 }; // the status will be handled like this: const [divStatus, setDivStatus] = useState(AnimStatus.IDLE); <BigDiv divStatus={divStatus} onClick={(e) => { setDivStatus((prev) => prev === AnimStatus.INACTIVE || prev === AnimStatus.IDLE ? AnimStatus.ACTIVE : AnimStatus.INACTIVE ); }} /> // in the css animation const BigDiv = styled("div")( ({ theme, divStatus }) => css` /* BigDiv CSS properties */ ::after { /* Other ::after CSS properties */ width: 100%; height: 100%; transform-origin: bottom center; ${ divStatus !== AnimStatus.IDLE ? css` background-color: ${divStatus === AnimStatus.ACTIVE ? "red" : "transparent"}; animation: ${divStatus === AnimStatus.ACTIVE ? setColor("red") : unsetColor("red")} 0.5s ease; ` : "" } }; } ` ); La ventaja de este enfoque es que puede reutilizar este AnimStatus en todas sus animaciones como una interfaz y agregar fácilmente efectos específicos para "IDLE".
Aquí está mi demostración: https://codesandbox.io/s/animate-divs-forked-u2oltp
Puede definir una variable de estado similar a startAnimation y configurarla como falsa de forma predeterminada.
Cuando el usuario haga clic en click it to continue , luego configúrelo en verdadero y aplique la animación en consecuencia
estado definido
const [startAnimation, setStart] = useState(false);Pásalo en componente
<BigDiv activeDiv={activeDiv} startAnimation={startAnimation} onClick={(e) => { setStart(true); setDivState(!activeDiv); }} > <SecondDiv> <p>Click to animate</p> </SecondDiv> </BigDiv> </>Úselo en un componente como este
const BigDiv = styled("div")( ({ theme, activeDiv, startAnimation }) => css` min-width: 220px; height: 100%; position: relative; padding: ${theme.spacing(2, 0)}; border-bottom: red 4px solid; background-color: transparent; cursor: pointer; text-align: center; transition-delay: 0.5s; will-change: transform; ::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 100%; background-color: ${activeDiv ? "red" : "transparent"}; animation: ${ startAnimation ? (activeDiv ? setColor("red") : unsetColor("red")) : "" } 0.5s ease ; transform-origin: bottom center}; } ` );La solución más fácil que se me ocurrió fue establecer el estado inicial de activeDiv en null y cambiar la regla de animación a:
animation: ${activeDiv ? setColor("red") : unsetColor("red")} ${activeDiv === null ? "0" : "0.5s ease"} ;Ahora no habrá ninguna animación al inicio, pero se animará si activeDiv es todo menos nulo.
Aquí está mi bifurcación: https://codesandbox.io/s/animate-divs-forked-gth814