手机文件夹动效组件
开发技术:react
, ts
将一个列表类似手机文件夹那样包裹起来,点击展开/收起。
实现思路
首先组件分为4个部分,如图中红色框起来的4个,而第4个里面是用于存放前三个之外的剩余的 item
的;
最终只会看到 3 + 4 = 7
个 item
, 而实际会把所有的 item
都渲染出来;第4个部分采用 overflow: hidden
,隐藏起其他 item
(绿色框起来的部分)
展开
当点击第四项时,需要弹出并展开所有的 item
首先会创建一个弹出层,弹出层中渲染所有跟 item
同样大小的盒子(即下图中绿色的背景部分)以及完成对应的css布局;
然后采用 getBoundingClientRect
方法获取它们的 left
和 top
值。再通过计算给组件中的各 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 |
-- |