Создание кастомного React Native компонента Switch с помощью библиотек Reanimated и Gesture Handler (Часть 2)

от автора

Всех приветствую! Это вторая и заключительная часть моего цикла статей по созданию кастомного компонента Switch с помощью библиотек Reanimated и Gesture Handler. Здесь мы рассмотрим реализацию логики пропса disabled, добавим пару новых фич и напишем обработку изменения состояния value вне компонента. Ознакомиться с первой частью можно здесь.

Пропс Disabled

Иногда бывают ситуации, когда нам необходимо дизейблить любые взаимодействия с компонентом, например чтобы не плодить кучу запросов на сервер и ждать выполнение асинхронной функции. В этом нам поможет специальный метод enabled, который мы добавим к нашим константам pan и tap:

 const tap = Gesture.Tap()    .onEnd(() => {      translateX.value = withTiming(value ? 0 : TRACK_CIRCLE_WIDTH);      runOnJS(onValueChange)(!value);    })    .enabled(!disabled);  const pan = Gesture.Pan()    .onUpdate(({ translationX }) => {      const translate = value        ? TRACK_CIRCLE_WIDTH + translationX        : translationX;      const currentTranslate = () => {        if (translate < 0) {          return 0;        }        if (translate > TRACK_CIRCLE_WIDTH) {          return TRACK_CIRCLE_WIDTH;        }        return translate;      };      translateX.value = currentTranslate();    })    .onEnd(({ translationX }) => {      const translate = value        ? TRACK_CIRCLE_WIDTH + translationX        : translationX;      const selectedSnapPoint =        translate > TRACK_CIRCLE_WIDTH / 2 ? TRACK_CIRCLE_WIDTH : 0;      translateX.value = withTiming(selectedSnapPoint, { duration: 100 });      runOnJS(onValueChange)(!!selectedSnapPoint);    })    .enabled(!disabled);

Добавляем наш пропс disabled в тип принимаемых пропсов компонента:

type SwitchProps = {   value: boolean;   onValueChange: (value: boolean) => void;   disabled?: boolean; }; 

Также добавим еще несколько пропсов для управлением цветом нашего компонента, всего их четыре типа:

  • ​​activeColor — цвет компонента в состоянии value = true и disabled = false

  • inactiveColor — цвет компонента в состоянии value = false и disabled = false

  • disabledActiveColor — цвет компонента в состоянии value = true и disabled = true

  • disabledInactiveColor — цвет компонента в состоянии value = true и disabled = true

type SwitchProps = {   value: boolean;   onValueChange: (value: boolean) => void;   disabled?: boolean;   activeColor?: string;   inactiveColor?: string;   disabledActiveColor?: string;   disabledInactiveColor?: string; }; 

По дефолту выставим следующие цвета:

activeColor = ‘darkblue’,

 inactiveColor = ‘darkgray’,

 disabledActiveColor = ‘blue’,

 disabledInactiveColor = ‘gray’,

Немного переделаем стили нашего контейнера:

  const animatedContainerStyle = useAnimatedStyle(() => {     const colors = disabled       ? [disabledInactiveColor, disabledActiveColor]       : [inactiveColor, activeColor];     return {       backgroundColor: interpolateColor(         translateX.value,         [0, TRACK_CIRCLE_WIDTH],         colors       ),     };   });

Здесь в константе colors мы выбираем один из двух массивов нужных нам цветов, необходимых нам для интерполяции, в зависимости от состояния пропса disabled

Теперь мы можем управлять disabled состоянием нашего компонента, путем изменения одноименного пропса:

Пропс shouldCancelWhenOutside

У библиотеки Gesture Handler имеется один интересный метод shouldCancelWhenOutside, который отвечает за прекращение отслеживания свайпа вне границ компонента. Добавим этот метод и одноименный пропс также, как и предыдущий, но только для константы pan:

type SwitchProps = {   value: boolean;   onValueChange: (value: boolean) => void;   disabled?: boolean;   activeColor?: string;   inactiveColor?: string;   disabledActiveColor?: string;   disabledInactiveColor?: string;   shouldCancelWhenOutside?: boolean; }; 
const pan = Gesture.Pan()    .onUpdate(({ translationX }) => {      const translate = value        ? TRACK_CIRCLE_WIDTH + translationX        : translationX;      const currentTranslate = () => {        if (translate < 0) {          return 0;        }        if (translate > TRACK_CIRCLE_WIDTH) {          return TRACK_CIRCLE_WIDTH;        }        return translate;      };      translateX.value = currentTranslate();    })    .onEnd(({ translationX }) => {      const translate = value        ? TRACK_CIRCLE_WIDTH + translationX        : translationX;      const selectedSnapPoint =        translate > TRACK_CIRCLE_WIDTH / 2 ? TRACK_CIRCLE_WIDTH : 0;      translateX.value = withTiming(selectedSnapPoint, { duration: 100 });      runOnJS(onValueChange)(!!selectedSnapPoint);    })    .enabled(!disabled)    .shouldCancelWhenOutside(shouldCancelWhenOutside);

Теперь проверим что у нас получилось:

Обработка изменений состояния value вне компонента

Бывают случаи, когда нам необходимо изменить состояние компонента с помощью другого компонента. В текущей реализации при изменении value ничего не произойдет. Нам необходимо отследить изменение состояния и привести наш круг к нужной точке. Для этого нам понадобится хук useEffect и немного кода:

useEffect(() => {    const currentCircle = !value ? 0 : TRACK_CIRCLE_WIDTH;    if (!!currentCircle !== !!translateX.value) {      translateX.value = withTiming(currentCircle);    }  }, [TRACK_CIRCLE_WIDTH, translateX, value]);

Здесь в константе мы заводим координаты, к которым нам необходимо будет прийти. Далее идет проверка, если текущая позиция относительно концов компонента не равно позиции конца компонента (приводим числа к булевому), то переводим наш круг в нужное положение. Не забываем проставить необходимые зависимости в наш хук.

Проверяем конечный результат:

Заключение

Вот и все, мы разобрались в базовых вещах библиотек Reanimated и Gesture Handler и написали свой собственный кастомный компонент, который пригодится нам в наших проектах. Весь код доступен по ссылке. Надеюсь статья была вам полезной, оставляйте свои комментарии с критикой и пожеланиями, о чем вы бы хотели видеть следующую статью.


ссылка на оригинал статьи https://habr.com/ru/articles/793994/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *