reanimated-color-picker
Version:
A Pure JavaScript Color Picker for React Native
266 lines (259 loc) • 9.19 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.Panel4 = Panel4;
var _react = _interopRequireDefault(require('react'));
var _reactNative = require('react-native');
var _reactNativeReanimated = _interopRequireWildcard(require('react-native-reanimated'));
var _AppContext = _interopRequireDefault(require('../../AppContext'));
var _PanelCore = require('./PanelCore');
var _styles = require('../../styles');
var _Thumb = _interopRequireDefault(require('../Thumb/Thumb'));
var _utils = require('../../utils');
function _interopRequireWildcard(e, t) {
if ('function' == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ('object' != typeof e && 'function' != typeof e)) return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
'default' !== t &&
{}.hasOwnProperty.call(e, t) &&
((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
/** @see [Panel4](https://alabsi91.github.io/reanimated-color-picker/api/panels/panel4/) */
function Panel4({ reverseHue = false, reverseHorizontalChannels = false, gestures = [], style = {}, ...props }) {
const { hueValue, saturationValue, brightnessValue, onGestureChange, onGestureEnd, ...ctx } = (0, _AppContext.default)();
const thumbShape = props.thumbShape ?? ctx.thumbShape;
const thumbSize = props.thumbSize ?? ctx.thumbSize;
const thumbColor = props.thumbColor ?? ctx.thumbColor;
const boundedThumb = props.boundedThumb ?? ctx.boundedThumb;
const renderThumb = props.renderThumb ?? ctx.renderThumb;
const thumbStyle = props.thumbStyle ?? ctx.thumbStyle ?? {};
const thumbScaleAnimationValue = props.thumbScaleUpValue ?? ctx.thumbScaleAnimationValue;
const thumbScaleAnimationDuration = props.thumbScaleUpDuration ?? ctx.thumbScaleAnimationDuration;
const thumbInnerStyle = props.thumbInnerStyle ?? ctx.thumbInnerStyle ?? {};
const borderRadius = (0, _utils.getStyle)(style, 'borderRadius') ?? 5;
const heightStyle = (0, _utils.getStyle)(style, 'height') ?? 200;
const width = (0, _reactNativeReanimated.useSharedValue)(0);
const height = (0, _reactNativeReanimated.useSharedValue)(0);
const handleScale = (0, _reactNativeReanimated.useSharedValue)(1);
// combined brightness and saturation mapped to 0–200
const brightnessPlusSaturation = (0, _reactNativeReanimated.useDerivedValue)(() => {
const base = (2 - saturationValue.value / 100) * brightnessValue.value;
return reverseHorizontalChannels ? base : 200 - base;
}, [brightnessValue, saturationValue, reverseHorizontalChannels]);
const thumbAnimatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
const length = {
x: width.value - (boundedThumb ? thumbSize : 0),
y: height.value - (boundedThumb ? thumbSize : 0),
};
const thumbOffset = boundedThumb ? 0 : thumbSize / 2;
// brightness and saturation
const brightnessAndSaturation = (((2 - saturationValue.value / 100) * (brightnessValue.value / 100)) / 2) * 100;
const posPercentX = (brightnessAndSaturation / 100) * length.x;
const posX = (reverseHorizontalChannels ? posPercentX : length.x - posPercentX) - thumbOffset;
// hue
const percentY = (hueValue.value / 360) * length.y;
const posY = (reverseHue ? percentY : length.y - percentY) - thumbOffset;
return {
transform: [
{
translateX: posX,
},
{
translateY: posY,
},
{
scale: handleScale.value,
},
],
};
}, [
width,
height,
saturationValue,
brightnessValue,
hueValue,
handleScale,
boundedThumb,
thumbSize,
reverseHorizontalChannels,
reverseHue,
]);
const panelImageStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
return {
// Width and height are intentionally swapped to correct dimensions after the rotation
width: height.value,
height: width.value,
transform: [
{
scaleY: reverseHue ? -1 : 1,
},
{
rotate: '270deg',
},
{
translateX: ((width.value - height.value) / 2) * (reverseHue ? -1 : 1),
},
{
translateY: ((width.value - height.value) / 2) * (_utils.isRtl ? -1 : 1),
},
],
};
}, [height, width, reverseHue]);
const onBegin = () => {
'worklet';
handleScale.value = (0, _reactNativeReanimated.withTiming)(thumbScaleAnimationValue, {
duration: thumbScaleAnimationDuration,
});
};
const onUpdate = (newXValue, newYValue) => {
'worklet';
const maxX = 200; // saturation + brightness
const newSaturationValue = (0, _utils.clamp)(reverseHorizontalChannels ? maxX - newXValue : newXValue, 100);
const newBrightnessValue = (0, _utils.clamp)(reverseHorizontalChannels ? newXValue : maxX - newXValue, 100);
if (
hueValue.value === newYValue &&
saturationValue.value === newSaturationValue &&
brightnessValue.value === newBrightnessValue
) {
return;
}
hueValue.value = newYValue;
saturationValue.value = newSaturationValue;
brightnessValue.value = newBrightnessValue;
onGestureChange();
};
const onGestureUpdate = ({ x, y }) => {
'worklet';
const maxY = 360; // hue
const maxX = 200; // saturation + brightness
const lengthX = width.value - (boundedThumb ? thumbSize : 0);
const lengthY = height.value - (boundedThumb ? thumbSize : 0);
const posX = (0, _utils.clamp)(x - (boundedThumb ? thumbSize / 2 : 0), lengthX);
const posY = (0, _utils.clamp)(y - (boundedThumb ? thumbSize / 2 : 0), lengthY);
const valueX = (posX / lengthX) * maxX;
const valueY = (posY / lengthY) * maxY;
const newHueValue = reverseHue ? valueY : maxY - valueY;
onUpdate(valueX, newHueValue);
};
const onEnd = () => {
'worklet';
handleScale.value = (0, _reactNativeReanimated.withTiming)(1, {
duration: thumbScaleAnimationDuration,
});
onGestureEnd();
};
return /*#__PURE__*/ _react.default.createElement(
_PanelCore.PanelCore,
{
style: [
_styles.styles.panelContainer,
style,
{
position: 'relative',
height: heightStyle,
borderWidth: 0,
padding: 0,
},
],
label: props.accessibilityLabel ?? 'Hue, saturation, and brightness 2D slider',
hint: props.accessibilityHint ?? 'Double tap to switch between hue and saturation/brightness',
labelX: 'Saturation/Brightness',
currentXValue: brightnessPlusSaturation,
maxXValue: 200, // saturation + brightness
labelY: 'Hue',
currentYValue: hueValue,
maxYValue: 360,
// reverseX={reverseHorizontalChannels} // Handled in onUpdate
reverseY: reverseHue,
width: width,
height: height,
gestures: gestures,
onGestureUpdate: onGestureUpdate,
onBegin: onBegin,
onUpdate: onUpdate,
onEnd: onEnd,
},
/*#__PURE__*/ _react.default.createElement(
_reactNative.View,
{
style: {
flex: 1,
borderRadius,
overflow: 'hidden',
},
'aria-hidden': true,
},
/*#__PURE__*/ _react.default.createElement(_reactNativeReanimated.default.Image, {
source: require('../../assets/Hue.png'),
style: [_styles.styles.panelImage, panelImageStyle],
resizeMode: 'stretch',
}),
),
/*#__PURE__*/ _react.default.createElement(
_reactNative.View,
{
style: [
_styles.styles.panelImage,
{
borderRadius,
flexDirection: _utils.isRtl ? 'row-reverse' : 'row',
transform: [
{
scaleX: reverseHorizontalChannels ? -1 : 1,
},
],
},
],
'aria-hidden': true,
},
/*#__PURE__*/ _react.default.createElement(_reactNative.Image, {
source: require('../../assets/blackGradient.png'),
style: {
flex: 1,
},
tintColor: '#fff',
resizeMode: 'stretch',
}),
/*#__PURE__*/ _react.default.createElement(_reactNative.Image, {
source: require('../../assets/blackGradient.png'),
style: {
flex: 1,
transform: [
{
scaleX: -1,
},
],
},
resizeMode: 'stretch',
}),
),
/*#__PURE__*/ _react.default.createElement(_Thumb.default, {
thumbShape: thumbShape,
thumbSize: thumbSize,
thumbColor: thumbColor,
renderThumb: renderThumb,
innerStyle: thumbInnerStyle,
thumbAnimatedStyle: thumbAnimatedStyle,
style: thumbStyle,
}),
);
}