【问题标题】:How to use transition effect on accordion如何在手风琴上使用过渡效果
【发布时间】:2020-10-05 18:36:15
【问题描述】:
【问题讨论】:
标签:
javascript
reactjs
accordion
styled-components
【解决方案1】:
为了获得最佳效果,您需要将 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;
`;