reanimated-color-picker
Version:
A Pure JavaScript Color Picker for React Native
158 lines (150 loc) • 4.39 kB
JavaScript
import React from 'react';
import { useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated';
import colorKit from '../../colorKit/index';
import usePickerContext from '../../AppContext';
import { styles } from '../../styles';
import BuiltinThumbs from './BuiltinThumbs/index';
export default function Thumb({
handleStyle,
innerStyle,
style,
thumbColor,
renderThumb: RenderThumb,
thumbShape = 'ring',
thumbSize,
vertical = false,
adaptSpectrum,
channel,
overrideHSV,
}) {
const { width, height, borderRadius } = {
width: thumbSize,
height: thumbSize,
borderRadius: thumbSize / 2,
};
const { hueValue, saturationValue, brightnessValue, alphaValue, value } = usePickerContext();
const hue = (overrideHSV === null || overrideHSV === void 0 ? void 0 : overrideHSV.hue) ?? hueValue,
saturation = (overrideHSV === null || overrideHSV === void 0 ? void 0 : overrideHSV.saturation) ?? saturationValue,
brightness = (overrideHSV === null || overrideHSV === void 0 ? void 0 : overrideHSV.brightness) ?? brightnessValue,
alpha = (overrideHSV === null || overrideHSV === void 0 ? void 0 : overrideHSV.alpha) ?? alphaValue;
const resultColor = useSharedValue('#ffffff');
const solidColor = useAnimatedStyle(
() => ({
backgroundColor: thumbColor ?? resultColor.value,
}),
[resultColor],
);
const adaptiveColor = useSharedValue('#ffffff');
/**
* Get the current color and calculate its contrast ratio against white or black, depending on the channel and whether
* 'adaptSpectrum' is enabled
*/
const getColorForAdaptiveColor = () => {
'worklet';
if (adaptSpectrum) {
if (channel === 'a') {
if (alpha.value > 0.5)
return {
h: hue.value,
s: saturation.value,
v: brightness.value,
};
return {
h: 0,
s: 0,
v: 70,
};
}
return {
h: hue.value,
s: saturation.value,
v: brightness.value,
};
}
if (channel === 'h')
return {
h: hue.value,
s: 100,
v: 100,
};
if (channel === 'v')
return {
h: hue.value,
s: 100,
v: brightness.value,
};
if (channel === 's')
return {
h: hue.value,
s: saturation.value,
v: 70,
};
if (channel === 'a')
return {
h: hue.value,
s: alpha.value * 100,
v: 70,
};
return {
h: hue.value,
s: saturation.value,
v: brightness.value,
};
};
// When the values of channels change
useDerivedValue(() => {
alpha.value; // to track alpha changes too;
resultColor.value = colorKit.runOnUI().HEX({
h: hue.value,
s: saturation.value,
v: brightness.value,
});
// calculate the contrast ratio
const compareColor1 = getColorForAdaptiveColor();
const compareColor2 =
adaptiveColor.value === '#000000'
? {
h: 0,
s: 0,
v: 0,
}
: {
h: 0,
s: 0,
v: 100,
};
const contrast = colorKit.runOnUI().contrastRatio(compareColor1, compareColor2);
const reversedColor = adaptiveColor.value === '#ffffff' ? '#000000' : '#ffffff';
adaptiveColor.value = contrast < 4.5 ? reversedColor : adaptiveColor.value;
}, [alpha, hue, saturation, brightness]);
const thumbProps = {
width,
height,
borderRadius,
vertical,
solidColor,
adaptiveColor,
handleStyle,
innerStyle,
style,
thumbColor,
};
// render a custom thumb
if (RenderThumb)
return /*#__PURE__*/ React.createElement(RenderThumb, {
positionStyle: [styles.handle, handleStyle],
width: width,
height: height,
initialColor: value,
currentColor: resultColor,
adaptiveColor: adaptiveColor,
});
// normalize 'thumbShape' string to match 'BuiltinThumbs' keys.
const thumb_Shape = thumbShape.toLowerCase().charAt(0).toUpperCase() + thumbShape.slice(1);
if (thumb_Shape in BuiltinThumbs) {
const SelectedThumb = BuiltinThumbs[thumb_Shape];
return /*#__PURE__*/ React.createElement(SelectedThumb, thumbProps);
}
// default to the 'Ring' thumb
return /*#__PURE__*/ React.createElement(BuiltinThumbs.Ring, thumbProps);
}