react|React Native Sectionlist item移除动画

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
1. 子组件动画
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。

    推荐阅读