Estoy tratando de hacer que un cuadro se expanda (en ancho y alto) y pase de su origen al centro de una pantalla al hacer clic. Esto es lo que tengo hasta ahora:
Me encuentro con dos problemas aquí: cuando hago clic en el cuadro, el DOM cambia automáticamente, porque el elemento en el que se hizo clic tiene su posición cambiada a 'absolute' . El otro problema es que el cuadro no hace la transición desde su origen, hace la transición desde la esquina inferior derecha (tampoco regresa a su posición desde el centro de la pantalla, cuando se hace clic en make inactive ).
¿Qué estoy haciendo mal aquí?
import React from "react"; import styled from "styled-components"; import "./styles.css"; export default function App() { const [clickedBox, setClickedBox] = React.useState(undefined); const handleClick = React.useCallback((index) => () => { console.log(index); setClickedBox(index); }); return ( <Container> {Array.from({ length: 5 }, (_, index) => ( <Box key={index} active={clickedBox === index} onClick={handleClick(index)} > box {index} {clickedBox === index && ( <div> <button onClick={(e) => { e.stopPropagation(); handleClick(undefined)(); }} > make inactive </button> </div> )} </Box> ))} </Container> ); } const Container = styled.div` display: flex; flex-wrap: wrap; align-items: center; height: 100vh; `; const Box = styled.div` flex: 1 0 32%; padding: 0.5rem; cursor: pointer; margin: 1rem; border: 1px solid red; transition: 2s; background-color: white; ${({ active }) => ` ${ active ? ` position: absolute; width: 50vw; height: 50vh; background-color: tomato; top: 50%; left: 50%; transform: translate(-50%, -50%); ` : "" } `} `; Es muy poco probable que puedas lograr eso con css simple. Y seguro imposible lograr una solución versátil.
div del diseño, es casi imposible evitar arruinar el diseño (incluso si puede, siempre habrá algún caso extremo).relative a una fixed . Con el estándar css actual es imposible realizar estas cosas.
La solución es hacer algo de magia javascript . Como está usando React , le desarrollé una solución usando react-spring (un marco de animación). Aquí tienes un componente de envoltura que hará lo que quieras:
import React, { useEffect, useRef, useState } from "react"; import { useSpring, animated } from "react-spring"; export default function Popping(props) { const cont = useRef(null); const [oriSize, setOriSize] = useState(null); const [finalSize, setFinalSize] = useState(null); useEffect(() => { if (props.open && cont.current) { const b = cont.current.getBoundingClientRect(); setOriSize({ diz: 0, opacity: 0, top: b.top, left: b.left, width: b.width, height: b.height }); const w = window.innerWidth, h = window.innerHeight; setFinalSize({ diz: 1, opacity: 1, top: h * 0.25, left: w * 0.25, width: w * 0.5, height: h * 0.5 }); } }, [props.open]); const styles = useSpring({ from: props.open ? oriSize : finalSize, to: props.open ? finalSize : oriSize, config: { duration: 300 } }); return ( <> <animated.div style={{ background: "orange", position: "fixed", display: styles.diz?.interpolate((d) => (d === 0 ? "none" : "initial")) || "none", ...styles }} > {props.popup} </animated.div> <div ref={cont} style={{ border: "2px solid green" }}> {props.children} </div> </> ); } Nota: este código usa dos <div> , uno para envolver su contenido y el segundo siempre está fixed pero oculto. Cuando cambia la visibilidad de la ventana emergente, se mide el div envolvente (obtenemos su tamaño y posición en la pantalla) y el div fixed se anima desde esa posición hasta su posición final. Puede lograr la ilusión que está buscando renderizando el mismo contenido en ambos <div> , pero siempre existe el riesgo de una desalineación menor.
La idea es similar a lo que hizo el novato en su publicación, pero sin bibliotecas adicionales. Podría haber hecho algunas cosas un poco no estándar para evitar el uso de bibliotecas.
import React from "react"; import { StyledBox } from "./App.styles"; export const Box = (props) => { const boxRef = React.useRef(null); const { index, active, handleClick } = props; const handleBoxClick = () => { handleClick(index); }; React.useEffect(() => { const b = boxRef.current; const a = b.querySelector(".active-class"); a.style.left = b.offsetLeft + "px"; a.style.top = b.offsetTop + "px"; a.style.width = b.offsetWidth + "px"; a.style.height = b.offsetHeight + "px"; }); return ( <StyledBox active={active} onClick={handleBoxClick} ref={boxRef}> box {index} <div className="active-class"> box {index} <div> <button onClick={(e) => { e.stopPropagation(); handleClick(undefined); }} > make inactive </button> </div> </div> </StyledBox> ); }; import styled from "styled-components"; export const StyledContainer = styled.div` display: flex; flex-wrap: wrap; align-items: center; height: 100vh; `; export const StyledBox = styled.div` flex: 1 0 32%; padding: 0.5rem; cursor: pointer; margin: 1rem; border: 1px solid red; background-color: white; .active-class { position: absolute; transition: 0.3s all ease-in; background-color: tomato; z-index: -1; ${({ active }) => active ? ` width: 50vw !important; height: 50vh !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%); z-index: 1; opacity: 1; ` : ` z-index: -1; transform: translate(0, 0); opacity: 0; `} } `;primero, la transición con posición o visualización no funciona en css (puede funcionar pero sin transición).
aquí tienes:
flex: 1 0 32%;que es equivalente a:
flex-grow: 1; flex-shrink: 1; flex-basis: 32%;entonces, cuando activo es verdadero , el ancho saltaría a 50vw y la altura a 50vh pero aproximadamente sin transición. entonces la solución es usar una escala como esta:
z-index: 99; transform: scaleY(5) scaleX(2) translate(20%, 20%); background-tomato: tomatoy necesita modificar los valores de scaleY , scaleX y translate (para cada Box) hasta que funcione.
puedes echar un vistazo a lo que hice en este codesandbox: https://codesandbox.io/s/peaceful-payne-ewmxi?file=/src/App.js:1362-1432
aquí también hay un enlace si quieres master flex: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Controlling_Ratios_of_Flex_Items_Along_the_Main_Ax