Div自动高度切换动画问题排查与解决
Hey there! Let's break down your issues one by one and fix them up.
The main problem here is that you're missing overflow: hidden in your styled component. Here's why that matters:
- When the div is closed (
max-height: 20px), withoutoverflow: hidden, the content inside still spills out (since defaultoverflowisvisible). That means the actual height of the div is already the full height of your content, not 20px. - When you switch to
open: true, themax-heightjumps from 20px to 500px, but since the div's height is already at the content's full height, there's nothing to animate—you don't see any transition because the height doesn't change.
Quick fix for the open animation
Just add overflow: hidden to your Container styles:
const Container = styled.div` margin: 5px; padding: 10px; width: 200px; max-height: ${props => (props.open ? '500px' : '20px')}; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); transition: max-height 1s cubic-bezier(0, 1, 0.5, 1); background-color: #f6f6f6; cursor: pointer; overflow: hidden; // Add this line! `;
Now when closed, the content is clipped, and when opening, the div's height will gradually expand from 20px up to the content's actual height (stopping once it hits that, even if max-height is set to 500px). The animation will work as expected.
Setting a hard max-height: 500px means even if your content is only 100px tall, the transition still takes the full 1s—this feels slow and unnatural. To fix this, we need to dynamically calculate the actual height of the content and adjust both max-height and the transition duration based on that.
Solution: Use JavaScript to get the content's real height
Here's how to implement this with React hooks:
- Use a ref to access the content inside the collapsible div.
- Calculate the content's
scrollHeight(the full height of the content, even if clipped). - Pass this height as a prop to your styled component, and use it to set
max-heightwhen open. - Adjust the transition duration proportionally to the content height (so shorter content animates faster).
Here's the full code example:
import { useState, useRef, useEffect } from 'react'; import styled from 'styled-components'; const Container = styled.div` margin: 5px; padding: 10px; width: 200px; max-height: ${props => (props.open ? `${props.contentHeight}px` : '20px')}; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); transition: max-height ${props => `${props.animationDuration}s`} cubic-bezier(0, 1, 0.5, 1); background-color: #f6f6f6; cursor: pointer; overflow: hidden; `; const CollapsibleSection = () => { const [open, setOpen] = useState(false); const contentRef = useRef(null); const [contentHeight, setContentHeight] = useState(0); const [animationDuration, setAnimationDuration] = useState(1); // Calculate content height on component mount useEffect(() => { if (contentRef.current) { const fullHeight = contentRef.current.scrollHeight; setContentHeight(fullHeight); // Adjust duration: e.g., 1s for 500px tall content, scale down for shorter content setAnimationDuration(Math.min(fullHeight / 500, 1)); // Cap duration at 1s so it doesn't get too slow for very long content } }, []); return ( <Container open={open} onClick={() => setOpen(!open)} contentHeight={contentHeight} animationDuration={animationDuration} > {/* Wrap your content in a div with the ref */} <div ref={contentRef}> Your dynamic content goes here—whether it's short text or a long paragraph, the animation will adapt! </div> </Container> ); }; export default CollapsibleSection;
How this works:
- When the component mounts, we grab the full height of the content using
scrollHeight. - We set
max-heightto that exact height when open, so the div only expands as much as needed. - The animation duration is scaled based on the content height (e.g., a 250px tall content will animate in 0.5s, while a 500px+ content uses the full 1s).
This gives you a smooth, natural-feeling animation that adapts to whatever content you put inside.
内容的提问来源于stack exchange,提问作者RMT

