问题描述
我使用React和样式化的组件创建了简单的手风琴。我在制作效果时遇到问题,以便下部可以缓慢滑出而不会立即弹出。我使用了过渡,但我不知道为什么在我的情况下它不起作用。我的代码有一部分:https://codesandbox.io/s/sweet-blackwell-v78wt?file=/src/App.js
解决方法
为获得最佳效果,您需要将max-height
从0px
转换为内容的高度,反之亦然。您需要使用useRef
和useEffect
钩子来选择元素并手动设置其样式。
您还需要一个知道内容高度的内部包装器。这将是在折叠容器上设置max-height
的参考。
使用onTransitionEnd
事件侦听器,您可以在展开手风琴时删除max-height
属性。这样一来,元素便可以根据内容进行响应式调整大小。
内容的高度由getBoundingClientRect()
函数确定。它返回具有元素的高度,宽度(及更多)的对象。这样,您可以确定内容容器的高度。
下面的示例是一个基于您的代码的可行示例。
import React,{ useState,useRef,useEffect } from "react";
import "./styles.css";
import styled from "styled-components";
const Accordion = () => {
const [isExpanded,setIsExpanded] = useState(false);
const [isAnimating,setIsAnimating] = useState(false);
const collapseRef = useRef(null);
const contentRef = useRef(null);
const handleClick = () => {
/**
* isAnimating prevents the accordion from changing
* state when it is in the middle of a transition.
*/
if (!isAnimating) {
setIsAnimating(true);
setIsExpanded((isExpanded) => !isExpanded);
}
};
const handleTransition = () => {
if (isExpanded) {
/**
* Removing the max-height when expanded makes sure
* that the element still resizes responsively correct.
*/
collapseRef.current.style.maxHeight = "";
}
setIsAnimating(false);
};
useEffect(() => {
const { height } = contentRef.current.getBoundingClientRect();
collapseRef.current.style.maxHeight = `${height}px`;
if (!isExpanded) {
/**
* This makes sure that the element first is expanded
* and in the next frame paint is transitioned to 0px.
* Otherwise the 0px height would overwrite to dynamic height
* and there'd be no transition.
*/
requestAnimationFrame(() => {
collapseRef.current.style.maxHeight = "0px";
});
}
},[isExpanded]);
return (
<ContentWrapper onClick={handleClick}>
<TitleCard>
<h2>Example text</h2>
</TitleCard>
<DescriptionCard
ref={collapseRef}
isVisible={isExpanded}
style={{ maxHeight: "0px" }}
onTransitionEnd={handleTransition}
>
<DescriptionContent ref={contentRef}>
<p>Example text 2</p>
<p>Now it expands based on the height of the</p>
<h3>content.</h3>
<p>Now it expands based on the height of the</p>
<h3>content.</h3>
</DescriptionContent>
</DescriptionCard>
</ContentWrapper>
);
};
export default function App() {
return (
<div className="App">
<Accordion />
</div>
);
}
const DescriptionContent = styled.div`
display: flex;
flex-direction: column;
`;
const DescriptionCard = styled.div`
background: grey;
overflow: hidden;
align-items: center;
justify-content: flex-start;
transition: max-height 0.35s;
`;
const TitleCard = styled.div`
cursor: pointer;
height: 4.8rem;
background: #ffffff;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
padding: 0 1.5em;
`;
const ContentWrapper = styled.div`
border-radius: 16px;
border: 2px solid black;
overflow: hidden;
`;