反应原生规模动画滑动列表项

时间:2021-06-14 13:33:53

标签: javascript android react-native animation react-native-reanimated

enter image description here

正如您从 gif 中看到的,我必须确保当您在一行上从右向左滑动时,该行上的图标具有缩放类型的动画。

喜欢:

当您从小处打开图标时,它会变得越来越大。 (从右向左移动)

当您从大图标关闭时,它会变得越来越小。 (从左向右移动,然后关闭)

链接:https://snack.expo.io/sAXMmE2dZ

代码:

import React, { useState, useEffect } from 'react';
import {
  Text,
  View,
  StyleSheet,
  FlatList,
  LayoutAnimation,
  TouchableOpacity,
  Platform,
  UIManager,
} from 'react-native';
import Animated from 'react-native-reanimated';
import SwipeableItem, {
  UnderlayParams,
  OpenDirection,
} from 'react-native-swipeable-item';
import DraggableFlatList, {
  RenderItemParams,
} from 'react-native-draggable-flatlist';
const { multiply, sub } = Animated;
import { MaterialIcons } from '@expo/vector-icons';

if (Platform.OS === 'android') {
  UIManager.setLayoutAnimationEnabledExperimental &&
    UIManager.setLayoutAnimationEnabledExperimental(true);
}
const OVERSWIPE_DIST = 20;
const NUM_ITEMS = 20;

type Item = {
  key: string,
  text: string,
  taken: boolean,
  height: number,
};

const initialData: Item[] = [...Array(NUM_ITEMS)].fill(0).map((d, key) => {
  return {
    text: `Item ${key}`,
    key,
    taken: false,
    height: 50,
  };
});

export default function App() {
  const [state, setState] = useState(initialData);
  const [key, setKey] = useState(1);

  let itemRefs = new Map();

  /*useEffect(() => {
    let id;
    if (key < state.length)
      id = setInterval(() => {
        const item = state.filter((d) => d.key === key)[0];
        takenItem(item);
        setKey((prev) => prev + 2);
      }, 3000);
    else clearInterval(id);
    return () => clearInterval(id);
  }, [key]);*/

  const takenItem = (item: Item) => {
    item.taken = !item.taken;
    if (item.taken) {
      const els = state.filter((el) => el.key !== item.key);
      setState([...els, item]);
    } else {
      const noTaken = state.filter((el) => !el.taken);
      const prev = noTaken.filter((el) => el.key < item.key);
      const next = noTaken.filter((el) => el.key > item.key);
      const taken = state
        .filter((el) => el.key !== item.key)
        .filter((el) => el.taken);
      setState([...prev, item, ...next, ...taken]);
      console.log('el', item);
      console.log('prev', prev);
      console.log('next', next);
      console.log('taken', taken);
      console.log('u', [...prev, item, ...next, ...taken]);
    }
    /*setState((prev) =>
      prev.map((el) => (el.key === item.key ? { ...el, taken: !el.taken } : el))
    );*/
    [...itemRefs.entries()].forEach(([key, ref]) => {
      if (key === item.key && ref) ref.close();
    });
  };

  const deleteItem = (item: Item) => {
    const updatedData = state.filter((d) => d !== item);
    LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
    setState(updatedData);
  };

  const renderUnderlayRight = ({ item, percentOpen }: UnderlayParams<Item>) => (
    <Animated.View
      style={[
        styles.underlayRight,
        {
          opacity: percentOpen,
          transform: [{ translateX: multiply(sub(1, percentOpen), -100) }],
        },
      ]}>
      <TouchableOpacity onPressOut={() => takenItem(item)}>
        <MaterialIcons
          name={item.taken ? 'check-box' : 'check-box-outline-blank'}
          size={24}
          color="#fff"
        />
      </TouchableOpacity>
    </Animated.View>
  );

  const renderUnderlayLeft = ({ item, percentOpen }: UnderlayParams<Item>) => {
    console.log(percentOpen, multiply(sub(1, percentOpen), -100));
    return (
      <Animated.View
        style={[
          styles.underlayLeft,
          {
            opacity: percentOpen,
          },
        ]}>
        <TouchableOpacity onPressOut={() => deleteItem(item)}>
          <MaterialIcons
            name="delete"
            size={24}
            color="#fff"
            style={{
              scaleX: 0.5,
              scaleY: 0.5,
            }}
          />
        </TouchableOpacity>
      </Animated.View>
    );
  };
  const renderItem = ({ item, index, drag }: RenderItemParams<Item>) => {
    return (
      <SwipeableItem
        key={item.key}
        item={item}
        ref={(ref) => {
          if (ref && !itemRefs.get(item.key)) {
            itemRefs.set(item.key, ref);
          }
        }}
        onChange={({ open }) => {
          if (open) {
            [...itemRefs.entries()].forEach(([key, ref]) => {
              if (key !== item.key && ref) ref.close();
            });
          }
        }}
        overSwipe={100}
        renderUnderlayRight={renderUnderlayRight}
        renderUnderlayLeft={renderUnderlayLeft}
        snapPointsRight={[50]}
        snapPointsLeft={[50]}>
        <View
          style={[
            styles.row,
            {
              backgroundColor: item.taken ? '#4caf50' : '#2979ff',
              height: item.height,
            },
          ]}>
          <TouchableOpacity onPressIn={drag}>
            <MaterialIcons name="drag-handle" size={24} color="#fff" />
          </TouchableOpacity>
          <Text style={styles.text}>{item.text}</Text>
        </View>
      </SwipeableItem>
    );
  };

  return (
    <View style={styles.container}>
      <DraggableFlatList
        scrollEnabled={true}
        keyExtractor={(item) => item.key}
        data={state}
        renderItem={renderItem}
        onDragEnd={({ data }) => setState(data)}
        activationDistance={20}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: 35,
    backgroundColor: '#ecf0f1',
  },
  row: {
    flexDirection: 'row',
    flex: 1,
    alignItems: 'center',
  },
  text: {
    fontWeight: 'bold',
    color: 'white',
    fontSize: 15,
  },
  underlayRight: {
    flex: 1,
    backgroundColor: '#0d47a1',
    justifyContent: 'flex-start',
    alignItems: 'center',
    flexDirection: 'row',
    paddingLeft: 5,
  },
  underlayLeft: {
    flex: 1,
    backgroundColor: '#0d47a1',
    justifyContent: 'flex-end',
    alignItems: 'center',
    flexDirection: 'row',
    paddingRight: 5,
  },
});

0 个答案:

没有答案