1

再アニメーション化されたトグル機能を使用してカスタム スイッチ コンポーネントを作成しました。トグル機能は正常に動作し、onPress 関数をトグルして呼び出し、true または false を交互に返します。

initiStateが状態によって、または親からの新しい小道具によって変更されたときにトグル効果をアニメーション化したいと思います。助けてください、ありがとう。

Switch.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