Всех приветствую! Это вторая и заключительная часть моего цикла статей по созданию кастомного компонента 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/
Добавить комментарий