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

相关推荐
FrontAI17 小时前
Next.js从入门到实战保姆级教程:环境配置与项目初始化
react.js·typescript·学习方法
用户31532477954520 小时前
React19项目中 FormEdit / FormEditModal 组件封装设计说明
前端·react.js
Ruihong1 天前
Vue3 转 React:组件透传 Attributes 与 useAttrs 使用详解|VuReact 实战
vue.js·react.js
橘子编程1 天前
React 19 全栈开发实战指南
前端·react.js·前端框架
弓.长.1 天前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-svg(CAPI) — 矢量图形组件
react native·react.js·harmonyos
局i1 天前
从零搭建 Vite + React 项目:从环境准备到干净项目的完整指南
前端·react.js·前端框架
Lazy_zheng1 天前
SDD 实战:用 Claude Code + OpenSpec,把 AI 编程变成“流水线”
前端·react.js·ai编程
光影少年1 天前
如何实现RN应用的离线功能、数据缓存策略?
react native·react.js·掘金·金石计划
弓.长.1 天前
ReactNative for OpenHarmony项目鸿蒙化三方库:rn-placeholder — 骨架屏占位组件
react native·react.js·harmonyos
whuhewei1 天前
React性能优化
前端·react.js·性能优化