With Reanimated ScrollView
The other way to settle the scroll conflict: keep Animated.ScrollView and turn its scrollEnabled off from the UI thread while a slider is being dragged. Each slider gets its own Gesture.Pan() — one gesture cannot be shared between components.
Code
import React, { useState } from 'react';import { Platform, Text, View } from 'react-native';import { Gesture } from 'react-native-gesture-handler';import Animated, { useAnimatedProps, useSharedValue } from 'react-native-reanimated';
import type { ColorFormatsObject } from 'reanimated-color-picker';import ColorPicker, { HSLSaturationSlider, HueSlider, LuminanceSlider, OpacitySlider } from 'reanimated-color-picker';
import { useContainerBackgroundColor } from './components/BaseContainer';import { colorPickerStyle } from './components/colorPickerStyle';
/* * Using react-native-reanimated ScrollView and cancelling the scroll while using the color picker */export default function Example() { const [resultColor, setResultColor] = useState('#f00');
const currentColor = useContainerBackgroundColor('#f00');
// runs on the ui thread on color change const onColorChange = (color: ColorFormatsObject) => { 'worklet'; currentColor.value = color.hex; };
// runs on the js thread on color pick const onColorPick = (color: ColorFormatsObject) => { setResultColor(color.hex); };
const isScrollEnabled = useSharedValue<boolean | undefined>(true);
//? NOTE: // - You cannot use the same gesture for multiple color picker components. // - Either pass a new gesture or deep clone the gesture constructor. const [hueGesture, saturationGesture, lumGesture, opacityGesture] = Array.from({ length: 4 }, () => Gesture.Pan() .onBegin(() => (isScrollEnabled.value = false)) .onFinalize(() => (isScrollEnabled.value = true)), );
const animatedProps = useAnimatedProps( () => ({ scrollEnabled: isScrollEnabled.value }), Platform.OS === 'web' ? [isScrollEnabled] : undefined, );
return ( <Animated.ScrollView animatedProps={animatedProps} contentContainerStyle={{ height: '150%', justifyContent: 'center' }}> <Text style={colorPickerStyle.title}>Color Picker in a Animated.ScrollView</Text>
<View style={colorPickerStyle.pickerContainer}> <ColorPicker value={resultColor} sliderThickness={30} thumbSize={30} onChange={onColorChange} onCompleteJS={onColorPick} style={colorPickerStyle.picker} boundedThumb > <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> <View style={{ alignItems: 'center' }}> <Text style={colorPickerStyle.sliderTitle}>H</Text> <HueSlider gestures={[hueGesture]} style={colorPickerStyle.sliderVerticalStyle} vertical reverse /> </View>
<View style={{ alignItems: 'center' }}> <Text style={colorPickerStyle.sliderTitle}>S</Text> <HSLSaturationSlider gestures={[saturationGesture]} style={colorPickerStyle.sliderVerticalStyle} vertical reverse /> </View>
<View style={{ alignItems: 'center' }}> <Text style={colorPickerStyle.sliderTitle}>L</Text> <LuminanceSlider gestures={[lumGesture]} style={colorPickerStyle.sliderVerticalStyle} vertical reverse /> </View>
<View style={{ alignItems: 'center' }}> <Text style={colorPickerStyle.sliderTitle}>A</Text> <OpacitySlider gestures={[opacityGesture]} style={colorPickerStyle.sliderVerticalStyle} vertical reverse /> </View> </View> </ColorPicker> </View> </Animated.ScrollView> );}The colorPickerStyle object is shared by every example. Divider is a 1px line, and useContainerBackgroundColor only tints the modal of the example app.
import { Platform, StyleSheet } from "react-native";
const shadow = Platform.select({ web: { boxShadow: "rgba(0, 0, 0, 0.3) 0px 0px 2px" }, default: { shadowColor: "#000", shadowOffset: { width: 0, height: 1, }, shadowOpacity: 0.2, shadowRadius: 1.41,
elevation: 2, },});
export const colorPickerStyle = StyleSheet.create({ title: { textAlign: "center", fontFamily: "Quicksand", fontWeight: "bold", marginVertical: 20, color: "gray", }, picker: { gap: 20, }, pickerContainer: { alignSelf: "center", width: 300, backgroundColor: "#eee", padding: 20, borderRadius: 20, ...shadow, }, panelStyle: { borderRadius: 16, ...shadow, }, sliderStyle: { borderRadius: 20, ...shadow, }, sliderVerticalStyle: { borderRadius: 20, height: 300, ...shadow, }, sliderTitle: { color: "#000", fontWeight: "bold", marginBottom: 5, paddingHorizontal: 4, fontFamily: "Quicksand", }, previewStyle: { height: 40, borderRadius: 14, }, previewTxt: { color: "#707070", fontFamily: "Quicksand", }, inputStyle: { color: "#707070", paddingVertical: 2, borderColor: "#707070", fontSize: 12, marginLeft: 5, }, swatchesContainer: { alignItems: "center", flexWrap: "nowrap", gap: 10, }, swatchStyle: { borderRadius: 20, height: 30, width: 30, margin: 0, marginBottom: 0, marginHorizontal: 0, marginVertical: 0, },});