1

我使用 reanimated 切换功能创建了一个自定义开关组件,它工作正常,它切换并调用 onPress 函数,该函数交替返回 true 或 false。

我想在initiState通过状态或父级的新道具更改时为切换效果设置动画,请帮助,谢谢。

开关.tsx

import React, { useMemo } from 'react';
import { StyleSheet, Dimensions } from 'react-native';
import { timing, bInterpolate } from 'react-native-redash';
import { TapGestureHandler, State } from 'react-native-gesture-handler';
import Animated, { Easing } from 'react-native-reanimated';

interface SwitchProps {
  onPress: (data: any) => void;
  color: string;
  background: string;
  initState: boolean;
  change: any;
}
const { height } = Dimensions.get('window');
const size = {
  width: height >= 812 ? 60 : 50,
  height: height >= 812 ? 35 : 30,
};

const {
  Value,
  useCode,
  block,
  cond,
  eq,
  set,
  Clock,
  clockRunning,
  event,
  sub,
  stopClock,
  onChange,
  call,
  and,
  not,
} = Animated;


const Switch = ({
  color,
  onPress,
  background: backgroundColor,
  initState,
}: SwitchProps) => {
  const { state, isOn, animate, shouldAnimate, width, clock } = useMemo(
    () => ({
      state: new Value(State.UNDETERMINED),
      isOn: new Value(initState ? 1 : 0),
      animate: new Value(initState ? 1 : 0),
      clock: new Clock(),
      shouldAnimate: new Value(0),
      width: new Value(0),
    }),
    []
  );

  useCode(() => {
    return block([
      cond(eq(state, State.END), set(shouldAnimate, 1)),
      onChange(
        state,
        cond(
          and(eq(state, State.END), not(clockRunning(clock))),
          cond(
            eq(isOn, 1),
            call([], () => onPress(false)),
            call([], () => onPress(true))
          )
        )
      ),
      cond(and(eq(shouldAnimate, 1), eq(isOn, 0)), [
        set(
          animate,
          timing({
            from: animate,
            to: 1,
            duration: 100,
            easing: Easing.linear,
            clock,
          })
        ),
        cond(not(clockRunning(clock)), [set(shouldAnimate, 0), set(isOn, 1)]),
      ]),
      cond(and(eq(shouldAnimate, 1), eq(isOn, 1)), [
        set(
          animate,
          timing({
            from: animate,
            to: 0,
            duration: 100,
            easing: Easing.linear,
            clock,
          })
        ),
        cond(not(clockRunning(clock)), [set(shouldAnimate, 0), set(isOn, 0)]),
      ]),
    ]);
  }, [initState]);

  const translateX = bInterpolate(animate, 0, sub(size.height - 10, width));
  const backgroundWidth = bInterpolate(animate, 0, size.width);

  return (
    <TapGestureHandler
      onHandlerStateChange={event([{ nativeEvent: { state } }])}>
      <Animated.View
        onLayout={event([
          {
            nativeEvent: { width },
          },
        ])}
        style={{
          ...size,
          borderRadius: size.height / 2,
          overflow: 'hidden',
          backgroundColor: 'white',
        }}>
        <Animated.View
          style={{
            position: 'absolute',
            left: 0,
            top: 0,
            right: 0,
            bottom: 0,
            backgroundColor,
          }}
        />
        <Animated.View
          style={{
            flex: 1,
            backgroundColor: color,
            width: backgroundWidth,
          }}
        />

        <Animated.View
          style={[styles.circle, { transform: [{ translateX }] }]}
        />
      </Animated.View>
    </TapGestureHandler>
  );
};

const styles = StyleSheet.create({
  circle: {
    width: size.height - 2,
    height: size.height - 2,
    position: 'absolute',
    left: 1,
    top: 1,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 1,
    },
    shadowOpacity: 0.22,
    shadowRadius: 2.22,

    elevation: 3,
    backgroundColor: 'white',
    borderRadius: size.height - 2 / 2,
  },
});

Switch.defaultProps = {
  color: '#00000',
  onPress: () => {},
  initState: false,
  background: '#8c8c8c',
  change: [],
};

export default Switch;

4

0 回答 0