React 简单模拟一个手机文件夹动效组件

手机文件夹动效组件

开发技术:react, ts

将一个列表类似手机文件夹那样包裹起来,点击展开/收起。

demo试玩及文档

组件源码

实现思路

首先组件分为4个部分,如图中红色框起来的4个,而第4个里面是用于存放前三个之外的剩余的 item 的;

最终只会看到 3 + 4 = 7item, 而实际会把所有的 item 都渲染出来;第4个部分采用 overflow: hidden,隐藏起其他 item (绿色框起来的部分)

展开

当点击第四项时,需要弹出并展开所有的 item

首先会创建一个弹出层,弹出层中渲染所有跟 item 同样大小的盒子(即下图中绿色的背景部分)以及完成对应的css布局;

然后采用 getBoundingClientRect 方法获取它们的 lefttop 值。再通过计算给组件中的各 item 采用 css 的 transform:translate(?px,?px) 移动到对应位置即可。

js 复制代码
const onShowMore = () => {
  if(isShowMore) return
  document.body.style.overflow = 'hidden';
  // 展开更多 item
  setIsShowMore(true)
  // 清除 overflow: hidden;
  setIsMoreOverflowHidden(false)
  // 延迟,等弹出层渲染完再获取位置信息
  setTimeout(() => {
    const arr = []
    for(let i = 0; i < list?.length; i++) {
      // 获取距离屏幕的 `left` 和 `top` 值
      const itemInfo = document.querySelector(`.${idRef.current} .${classPrefix}-item-${i}`)?.getBoundingClientRect()
      const popItemInfo = document.querySelector(`.${idRef.current} .${classPrefix}-pop-item-${i}`)?.getBoundingClientRect()
      if(!itemInfo || !popItemInfo) continue;
      arr.push({
        // 相减得出实际该位移的值
        x: popItemInfo.left - itemInfo.left,
        y: popItemInfo.top - itemInfo.top, 
      })
    }
    setMoreItems(arr)
  }, 10);
}

收起

将各 item 的位移值清空,让他们归位即可,其中需要注意,等动画进行到差不多时,才重新打开 overflow: hidden,这样才能看到各 item 收起来的样式。

js 复制代码
const onHideMore = () => {
  setMoreItems([])
  setIsShowMore(false)
  // css -> transition: transform 0.4s, opacity .2s;
  // 200ms 后溢出项的 opacity 就为 0 了,300ms 后开启溢出隐藏。
  setTimeout(() => {
    setIsMoreOverflowHidden(true)
    document.body.style.overflow = 'auto'
  }, 300);
}

这是初略的描述,建议查看 完整代码 会看得更清晰;

Demo 使用

js 复制代码
import { MobileFolder, MobileFolderItem } from "lhh-ui"
import React from "react"
const JuejinIcon = 'https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/6c61ae65d1c41ae8221a670fa32d05aa.svg';

export default () => {

  const list: MobileFolderItem[] = [
    {icon: JuejinIcon, title: 'juejin', onClick: () => {window.open('https://github.com/ApeWhoLovesCode/lhh-ui')}},
    {icon: JuejinIcon, title: 'juejin22'},
    {icon: JuejinIcon, title: 'juejin33'},
    {icon: JuejinIcon, title: 'juejin44', onClick: (item, i) => {console.log(item, i)}},
    {icon: JuejinIcon},
    {icon: JuejinIcon},
    {icon: JuejinIcon},
    {icon: JuejinIcon},
    {icon: JuejinIcon},
    {icon: JuejinIcon},
    {icon: JuejinIcon},
  ]

  return (
    <div style={{width: 300}}>
      <MobileFolder list={list} />
    </div>
  )
}

Props

MobileFolderProps

属性名 描述 类型 默认值
list 需要渲染的列表 MobileFolderItem[] (必选)
className 类名 string --
style style样式 {} --
children children节点 ReactNode --

MobileFolderItem

属性名 描述 类型 默认值
icon 渲染的内容为图片 string --
title 标题 string --
children 自定义渲染的内容 ReactNode --
onClick 点击的回调 (item: MobileFolderItem, i: number) => void --
相关推荐
white-persist12 分钟前
Python实例方法与Python类的构造方法全解析
开发语言·前端·python·原型模式
新中地GIS开发老师1 小时前
Cesium 军事标绘入门:用 Cesium-Plot-JS 快速实现标绘功能
前端·javascript·arcgis·cesium·gis开发·地理信息科学
Superxpang1 小时前
前端性能优化
前端·javascript·vue.js·性能优化
左手吻左脸。1 小时前
解决el-select因为弹出层层级问题,不展示下拉选
javascript·vue.js·elementui
李白的故乡1 小时前
el-tree-select名字
javascript·vue.js·ecmascript
Rysxt_1 小时前
Element Plus 入门教程:从零开始构建 Vue 3 界面
前端·javascript·vue.js
隐含1 小时前
对于el-table中自定义表头中添加el-popover会弹出两个的解决方案,分别针对固定列和非固定列来隐藏最后一个浮框。
前端·javascript·vue.js
大鱼前端1 小时前
Turbopack vs Webpack vs Vite:前端构建工具三分天下,谁将胜出?
前端·webpack·turbopack
你的人类朋友1 小时前
先用js快速开发,后续引入ts是否是一个好的实践?
前端·javascript·后端