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 --
相关推荐
weixin199701080169 分钟前
《识货商品详情页前端性能优化实战》
前端·性能优化
Forever7_10 分钟前
重磅!Vue3 手势工具正式发布!免费使用!
前端·前端框架·前端工程化
用户8061381665911 分钟前
发布为一个 npm 包
前端·javascript
树上有只程序猿42 分钟前
低代码何时能出个“秦始皇”一统天下?我是真学不动啦!
前端·后端·低代码
TT_哲哲42 分钟前
小程序双模式(文件 / 照片)上传组件封装与解析
前端·javascript
菜果果儿1 小时前
Vue 3 + TypeScript 常用代码示例总结
前端
前端付豪1 小时前
实现多角色模式切换
前端·架构
从文处安1 小时前
「九九八十一难」从回调地狱到异步秩序:深入理解 JavaScript Promise
前端·javascript
要换昵称了1 小时前
Axios二次封装及API 调用框架
前端·vue.js
进击的尘埃1 小时前
Node.js 子进程管理:child_process 模块的正确打开方式
javascript