问题:
我有一个递归渲染嵌套列表的 React 功能组件。那部分工作正常。
我正在努力的部分是让包含嵌套列表的 div 在嵌套列表(不同大小)出现和消失时具有扩展动画。
因为内容的数量是未知的,所以简单地为 max-height 属性设置动画是行不通的。例如,当渲染一级列表时,框架的高度可能会扩大到 100 像素。但是,如果您将最大高度设置为 100 像素,则 div 稍后将无法扩展以容纳越来越多的嵌套列表。
需要动画的div是:
<div className="frame"> [nested ordered lists] </div>
...并且不起作用的函数是名为“collapseFrame”的函数。
同样,我之前尝试在 max-height 属性上使用 CSS 过渡,但这远不是一个理想的解决方案,因为它在不可预测的高度上效果不佳。所以,如果可能的话,我不想那样做。
我遵循了一个教程,并在没有 React 的情况下使用 vanilla JavaScript(部分代码被注释掉),但是当将它翻译成 React 时,我不确定为什么我不能让代码工作。
目前,展开动画正在工作,但如果移除边框或将其更改为白色,它将停止工作。我不知道为什么。??
** 此外,折叠动画根本不起作用。'transitioned' 事件并不总是在 ref 对象上触发,有时会在它应该被删除后触发。
有人能帮我指出正确的方向吗?谢谢!
这是我的代码笔。
(我试图将它转移到 JS Fiddle,但它不起作用,所以请不要对此投反对票)。
https://codepen.io/maiya-public/pen/ZEzoqjW
(对 codepen 的另一次尝试,供参考。嵌套列表出现和消失,但没有过渡)。 https://codepen.io/maiya-public/pen/MWgGzBE
这是原始代码:(我认为在 codepen 上更容易理解,但粘贴在这里以获得良好的实践)。
索引.html
<div id="root">
</div>
样式.css
.frame {
overflow:hidden;
transition: all 0.5s ease-out;
height:auto;
// for some reason,without the border, not all of them will transition AND it can't be white !??
border: solid purple 1px;
}
button {
margin: 0.25rem;
}
虚拟数据:(嵌套列表将基于此对象呈现)
let data = {
text: 'list',
children: [
{
text: "groceries",
children: [
{
text: "sandwich",
children: [
{
text: "peanut butter",
children: [{text: 'peanuts', children: [{text: 'nut family'},{text: 'plant', children: [{text: 'earth'}]}] }]
},
{
text: "jelly",
children: [
{ text: "strawberries", children: null },
{ text: "sugar", children: null }
]
}
]
}
]
},
{
text: "flowers",
children: [
{
text: "long stems",
children: [
{
text: "daisies",
children: null
},
{
text: "roses",
children: [
{ text: "pink", children: null },
{ text: "red", children: null }
]
}
]
}
]
}
] };
反应代码:index.js
// component recursively renders nested lists. Every list item is a list.
const ListItem = ({item, depth}) => {
// depth prop allows me to give a className depending on how deeply it is nested, and do CSS style based on that
let { text, children } = item
let [showChildren, setShowChildren] = React.useState(false)
let frame = React.useRef()
const expandFrame = (frame) => {
//let frameHeight = frame.style.height //was using this at one point, but not anymore b/c not working
// was supposed to have frame.style.height = frameHeight + 'px'
frame.style.height = 'auto'
frame.addEventListener('transitionend', () =>{
frame.removeEventListener('transitionend', arguments.callee)
frame.style.height = null
})
}
const collapseFrame = (frame) => {
let frameHeight = frame.scrollHeight;
// temporarily disable all css transitions
let frameTransition = frame.style.transition;
frame.style.transition = ''
requestAnimationFrame(function() {
frame.style.height = frameHeight + 'px';
frame.style.transition = frameTransition;
requestAnimationFrame(function() {
frame.style.height = 0 + 'px';
})
})
}
return(
<ol>
<button onClick={(e)=>{
if(!showChildren) {
// console.log('children not showing/ expand')
setShowChildren(true)
expandFrame(frame.current)
} else {
// console.log('children showing/ collapse')
setShowChildren(false)
collapseFrame(frame.current)
}
}}
>
{text}-{depth}
{ children && <i className="fas fa-sort-down"></i> || children && <i className="fas fa-sort-up"></i>}
</button>
{/*THIS IS THE ELEMENT BEING ANIMATED:*/}
<div className={`frame depth-${depth}`} ref={frame}>
{showChildren && children && children.map(item => {
return (
<li key={uuid()}>
<ListItem item={item} depth={depth + 1}/>
</li>)
})
}
</div>
</ol>
)
}
class App extends React.Component {
render() {
return (
<div>
<ListItem key={uuid()} item={data} depth={0} />
</div>
);
}
}
function render() {
ReactDOM.render(<App />, document.getElementById("root"));
}
render();