React Native Sectionlist item移除动画 【react|React Native Sectionlist item移除动画】最近碰到一个需求,后端返回一组数据,以sectionlist形式呈现,对每一个section中的item(对一行行的简称为item)点击后都可以移除。在iOS中UITableView可以处理cell消失的动画。看了一边RN官方文档之后发现没有提供类似的API。于是在搜了一下,发现有类似的,不过是基于Flatlist实现的。
原文在这里: aboutreact.com/add-or-remo…根据原文的思路,对sectionlist实现了一遍,需要注意这几个点:
- Animated 处理item颜色渐变、位移动画
- LayoutAnimation 处理sectionlist子组件(item)移除后,整体布局更新
- 数据源更新,动画结束后,删除对应的数据并更新data
interface RenderItemProp {item: {itemTitle: stringkey: string}removeCallback: (key: string) => void
}const RenderItem = ({ item, removeCallback }: RenderItemProp) => {const opacityRef = useRef(new Animated.Value(1)).currentconst positionRef = useRef(new Animated.ValueXY()).currentconst removeSelf = () => {Animated.parallel([Animated.timing(opacityRef, {duration: 500,toValue: 0,useNativeDriver: false}),Animated.timing(positionRef, {duration: 500,toValue: {x: Dimensions.get('screen').width,y: 0},useNativeDriver: false})]).start(() => {removeCallback(item.key)})}return ({item.itemTitle} )
}
使用opacityRef、positionRef作为item的透明度、位移值引用,添加removeSelf响应用户点击,点击后执行一个并行动画,parallel会对数组中的多个动画同时执行。这里是在500ms时间内,将透明度从1渐变至0(完全不透明->完全透明),位置向右移动整个屏幕宽度距离(x,y代表水平和垂直方向偏移量),子组件动画完成后需要通过removeCallback回调对sectionlist整体做一个布局动画。
2. 父组件动画
const AnimatedSeclist = () => {// 设置数据源const [data, setData] = useState(mockData)// 子组件动画完成回调const removeCallback = (key: string) => {LayoutAnimation.configureNext({duration: 400,update: {duration: 400,type: LayoutAnimation.Types.easeInEaseOut,property: 'scaleXY'}})// 更新数据源const nextData = https://www.it610.com/article/produce(data, (draft) => {let secIndex: number, rowIndex: numberdraft.forEach((secItem, sectionIndex) => {secItem.data.forEach((rowItem, rowItemIndex) => {if (rowItem.key === key) {secIndex = sectionIndexrowIndex = rowItemIndex}})})draft[secIndex!].data.splice(rowIndex!, 1) // delete rowItemif (draft[secIndex!].data.length === 0) {draft.splice(secIndex!, 1) // if section contains empty data, remove it in sectionlist}})setData(nextData)}return ( ( )}renderSectionHeader={({ section: { title } }) => {return {title} }}stickySectionHeadersEnabled={false}keyExtractor={(item) => item.key} // key一定不能重复sections={data}/>)
子组件动画完成后,对父组件执行一个 LayoutAnimation 布局更新动画,可以指定时长duration,动画效果type,动画属性property(具体可以看下官方文档)。
3. 数据源更新
// 更新数据源const nextData = https://www.it610.com/article/produce(data, (draft) => {let secIndex: number, rowIndex: numberdraft.forEach((secItem, sectionIndex) => {secItem.data.forEach((rowItem, rowItemIndex) => {if (rowItem.key === key) {secIndex = sectionIndexrowIndex = rowItemIndex}})})draft[secIndex!].data.splice(rowIndex!, 1) // delete rowItemif (draft[secIndex!].data.length === 0) {draft.splice(secIndex!, 1) // if section contains empty data, remove it in sectionlist}})setData(nextData)
父组件动画完成后更新数据:删除所点击的子组件数据、更新数据源。通过子组件点击的回调函数传递该子组件数据所对应的唯一key,在数据源中根据该key查找到数据位置(我把它叫做sectionIndex,rowItemIndex,iOS开发的同学应该很熟悉)并删除,sectionlist数据源格式为多个对象构成的数组,每个对象又含有一个数组data字段,data中每个对象才为子组件数据。
如果我们直接在源数据上修改并setData是无效的,因为源数据的引用并没有变,这里我们可以使用immer框架提供的produce方法在修改数据源的同时复制一个新的data对象,这里把它叫做nextData。(其实也可以使用JSON.parse(JSON.stringify(data))直接拷贝一个对象,在其上做修改)。
删除点击的子组件数据需要注意是的,sectionlist含有多个section,每个section是有header显示的,当section一个子组件都没有时,我们应该把该section的数据全部删除,即
draft.splice(secIndex!, 1)
,否则会单独显示一个header。推荐阅读
- React|React 错误边界之 react-error-boundary
- OC|iOS 设计模式 浅析MVC、MVP、MVVM
- 转载|MVC、MVVM、MVP 框架设计模式浅析
- react|react native常用插件
- react.js|React(9)—— Hooks - LazyLoad - Context - 组件优化 - 错误边界 - 组件通信方式总结
- react|react-router-dom v6 使用
- React|React 组件三大属性
- React|react-router-dom V6
- React|React组件三大核心属性(二)——props