早期React多级折叠动效选项菜单思考与实现

折叠菜单或者叫多级选项菜单,用流行UI框架下的相关实现改改就好了,为什么要手写?因为手痒 啊,有个简单方法特别想印证一下效果。前面的选项框就不做了,就是你点击了用一个数组去收集id之类的数据。我主要实现一下效果。

1.首先模拟的数据结构:

js 复制代码
let [list, setList] = useState([{
    title: "父标题111",
    child: [{
      title: '子标题1111'
    }, {
      title: '子标题1112',
      child: [
        { title: '子标题11121' },
        {
          title: '子标题11122',
          child: [
            { title: "子标题111221" },
            { title: "子标题111222" },
            { title: "子标题111223" },
            { title: "子标题111224" },
            { title: "子标题111225" },
            { title: "子标题111226" },
          ]
        }
      ]
    }, {
      title: '子标题1113'
    }]
  },
  {
    title: "父标题222",
    child: [{
      title: '子标题2221'
    }]
  },
  { title: "父标题333" }
  ])

2.控制child显示隐藏可不是用display/visibility属性就行了,有动画效果要用transition和控制高度。用到的样式,一些想法都在代码注释里:

css 复制代码
*{
    margin: 0;
    padding: 0;
}
.uuu{  
    transition:0.5s;
    height:0;  //一开始子项的ul列表要隐藏
    overflow: hidden;
}
ul{
    padding-left:20px; 
}

3.视图起始

js 复制代码
function App() {
...//上面的模拟数据
 return (
    <List list={list} />
  )
}

4.List定义,设计上肯定是组件递归。

js 复制代码
function List(props) {
  return <ul className={[props.iso ? 'uuu' : '', 'out'].join(' ')} >
    {props.list.map((t, i) => {
      return <>
        <li key={i} >
          <div onClick={(e) => { t.child && an(e, t.child.length) }}>{t.title}</div>
          {t.child && <List list={t.child} iso={true}></List>}
        </li>
      </>
    })
    }
  </ul>
}

5.想法:点击title的div如果是显示子菜单,除了下一层ul要显示高度,它的所有上层都要增加这个高度,标识它可控外层的条件是增加了'out'这个class,父级没有这个class表示不是菜单本身不需要增加高度。

增加的iso={true}是什么,为什么视图起始没有?这个是控制子菜单一开始隐藏的,而最外层不需要隐藏。

6.核心代码。首先"展开"效果,上面有说,不仅本身高度展开,所有上层都要加这个高度:

js 复制代码
let lout = (e, l) => {
  if (e.className.indexOf('out') > -1) {
    e.style.height = e.clientHeight + l + 'px'
    lout(e.parentNode.parentNode, l)  
  } //点击的是div,它的上级是li,再上级才是ul。
}

7."折叠"效果,不仅折叠本身,它的所有子级都要折叠起来:

js 复制代码
let lin = (e) => {
  e.style.height = 0
  e.childNodes.forEach(_e => {
    if (_e.childNodes.length > 1) {
      lin(_e.childNodes[1])
    }
  });
}

为什么要判断>1并且childNodes取第二项?因为有第一项div层。

8.控制逻辑。

js 复制代码
let nHeight = 21

let an = (e, l) => {
  let dom = e.currentTarget.nextSibling
  let flag = dom.clientHeight == 0 ? false : true
  if (flag) {
    lout(dom, dom.clientHeight * (-1))
    lin(dom)
  } else {
    lout(dom, l * nHeight)
  }
}

如果当前菜单为折叠态,那就展开它和它的所有上层菜单;如果当前菜单是展开态,那不仅要折叠它和它的所有子级菜单,同时要把所有上层菜单也减少这个高度,也就是为什么要*(-1)。

相关推荐
斯文的孙5 小时前
React JSX:每天都在用,但你真的了解它吗?
前端·react.js
张勇8296 小时前
# React状态管理最佳实践:从原理到2025年主流方案
前端·react.js
小肚肚肚肚肚哦9 小时前
React 源码解读 (初版)
react.js
curdcv_po10 小时前
🔥🔥🔥结合 vue 或 react,去写three.js
前端·react.js·three.js
却尘11 小时前
React状态的人格分裂:当Vibe Coding遇上状态污染,坑你就完了。
前端·react.js·vibecoding
想要学好前端的阿毛11 小时前
手写一个简单的react-router6 Part2
react.js
懋学的前端攻城狮15 小时前
从 UI = f(state) 到 Fiber 架构:解构 React 设计哲学的“第一性原理”
前端·react.js·前端框架
前端小咸鱼一条15 小时前
React中的this绑定
前端·javascript·react.js
前端小咸鱼一条16 小时前
React的基本语法和原理
前端·javascript·react.js
十盒半价1 天前
React 牵手 Coze 工作流:打造高效开发魔法
react.js·coze·trae