早期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)。

相关推荐
努力往上爬de蜗牛40 分钟前
react学习1.搭建react环境
javascript·学习·react.js
唐某人丶2 小时前
如何优化 React 组件?
前端·react.js·前端框架
Shawn5902 小时前
如何使用useCallback优化React性能?
前端·react.js
机器瓦力3 小时前
开发突围:该换电脑了
flutter·react.js·程序员
Honeysea_703 小时前
React 和 Vue 框架概念及区别
前端·vue.js·react.js
崔璨5 小时前
实现一个精简React -- 实现useEffect(10)
前端·react.js
Au_ust5 小时前
React类的生命周期
前端·react.js·前端框架
摘笑9 小时前
vue3 和 react 的 自定义hooks 有什么区别
前端·vue.js·react.js
公谨9 小时前
如何用Monaco和Babel打造高性能React-Playground
前端·react.js
随笔记9 小时前
如何用vite构建工具搭建react项目
react.js·typescript·vite