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 --
相关推荐
掘金一周6 分钟前
Flutter Riverpod 3.0 发布,大规模重构下的全新状态管理框架 | 掘金一周 9.18
前端·人工智能·后端
一涯16 分钟前
页面出现空白区域
前端
spmcor41 分钟前
MinIO本地对象存储部署指南
前端
少年纪1 小时前
前端用 pdf.js 将 PDF 渲染到 Canvas 再转图片,文字消失的坑
前端
RoyLin1 小时前
TypeScript设计模式:复合模式
前端·后端·typescript
我是天龙_绍1 小时前
CSS/JS/图片全挂了,部署后页面白屏/资源加载失败?这两个配置项坑了多少人!
前端
我的小月月1 小时前
SQLFE:网页版数据库(VUE3+Node.js)
前端·后端
小高0071 小时前
🌐ES6 这 8 个隐藏外挂,知道 3 个算我输!
前端·javascript·面试
汤姆Tom1 小时前
Node.js 版本管理、NPM 命令、与 NVM 完全指南
前端·npm·node.js
东坡白菜1 小时前
SSE 实现 AI 对话中的流式输出
javascript·vue.js