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 --
相关推荐
呀啊~~26 分钟前
【前端框架与库】「React 全面解析」:从 JSX 语法到高阶组件,深度剖析前端开发中的核心概念与最佳实践
前端·javascript·学习·react.js·前端框架
sulingliang35 分钟前
【react项目引入tailwindcss不生效解决方案】
前端·react.js
无限大.1 小时前
前端知识速记:浏览器缓存机制 - 强缓存与协商缓存
前端·缓存
林啾啾1 小时前
解决 keep-alive 缓存组件中定时器干扰问题
前端·vue.js·缓存
一只小阿乐2 小时前
JS对象拷贝的几种实现方法以及如何深拷贝(面试题)
开发语言·javascript·ecmascript·浅拷贝·深拷贝
丁总学Java2 小时前
产品详情页中 品牌官网详情 对应后端的字段是 detail
前端·javascript·vue.js
huaqianzkh2 小时前
支持高并发的 Web 应用系统架构中LVS和keepalived是什么?
前端·系统架构·lvs
溜达哥2 小时前
嵌入页面不能正常获取 reponse header : content-disposition
javascript·vue.js·header
觉醒法师2 小时前
HarmonyOS开发 - 记事本实例一(界面搭建)
前端·javascript·华为·harmonyos
小六*^____^*2 小时前
vue3学习之待办事项列表(Todo List)
javascript·学习·list