Skip to content

Commit

Permalink
Optimize thumb and preview performance
Browse files Browse the repository at this point in the history
  • Loading branch information
alabsi91 committed Nov 1, 2023
1 parent f521ef5 commit d72565c
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 60 deletions.
4 changes: 2 additions & 2 deletions src/components/InputWidget/Widgets/HexWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export default function HexWidget({
};

useDerivedValue(() => {
[hueValue, saturationValue, brightnessValue, alphaValue];
[hueValue, saturationValue, brightnessValue, alphaValue]; // track changes on Native
runOnJS(updateText)();
}, [hueValue, saturationValue, brightnessValue, alphaValue]);
}, [hueValue, saturationValue, brightnessValue, alphaValue]); // track changes on WEB

const onTextChange = (text: string) => {
text = text.startsWith('#') ? text : '#' + text;
Expand Down
4 changes: 2 additions & 2 deletions src/components/InputWidget/Widgets/HslWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export default function HslWidget({
};

useDerivedValue(() => {
[hueValue, saturationValue, brightnessValue, alphaValue];
[hueValue, saturationValue, brightnessValue, alphaValue]; // track changes on Native
runOnJS(updateText)();
}, [hueValue, saturationValue, brightnessValue, alphaValue]);
}, [hueValue, saturationValue, brightnessValue, alphaValue]); // track changes on WEB

const onHueChange = (text: string) => {
let hue = +text;
Expand Down
4 changes: 2 additions & 2 deletions src/components/InputWidget/Widgets/HsvWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export default function HsvWidget({
};

useDerivedValue(() => {
[hueValue, saturationValue, brightnessValue, alphaValue];
[hueValue, saturationValue, brightnessValue, alphaValue]; // track changes on Native
runOnJS(updateText)();
}, [hueValue, saturationValue, brightnessValue, alphaValue]);
}, [hueValue, saturationValue, brightnessValue, alphaValue]); // track changes on WEB

const onHueChange = (text: string) => {
let hue = +text;
Expand Down
4 changes: 2 additions & 2 deletions src/components/InputWidget/Widgets/HwbWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export default function HwbWidget({
};

useDerivedValue(() => {
[hueValue, saturationValue, brightnessValue, alphaValue];
[hueValue, saturationValue, brightnessValue, alphaValue]; // track changes on Native
runOnJS(updateText)();
}, [hueValue, saturationValue, brightnessValue, alphaValue]);
}, [hueValue, saturationValue, brightnessValue, alphaValue]); // track changes on WEB

const onHueChange = (text: string) => {
let hue = +text;
Expand Down
4 changes: 2 additions & 2 deletions src/components/InputWidget/Widgets/RgbWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export default function RgbWidget({
};

useDerivedValue(() => {
[hueValue, saturationValue, brightnessValue, alphaValue];
[hueValue, saturationValue, brightnessValue, alphaValue]; // track changes on Native
runOnJS(updateText)();
}, [hueValue, saturationValue, brightnessValue, alphaValue]);
}, [hueValue, saturationValue, brightnessValue, alphaValue]); // track changes on WEB

const onRedChange = (text: string) => {
let red = +text;
Expand Down
34 changes: 16 additions & 18 deletions src/components/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Animated, { runOnJS, useAnimatedStyle, useDerivedValue, useSharedValue }
import colorKit from '@colorKit';
import usePickerContext from '@context';
import { styles } from '@styles';
import { ConditionalRendering, getStyle, isWeb } from '@utils';
import { ConditionalRendering, contrastRatio, getStyle, HSVA2HEX, isWeb } from '@utils';

import type { PreviewProps } from '@types';
import type { ReactNode } from 'react';
Expand All @@ -20,9 +20,9 @@ const ReText = ({ text, style, hash }: { text: () => string; style: StyleProp<Te
};

useDerivedValue(() => {
[hash[0], hash[1], hash[2], hash[3]];
[hash[0], hash[1], hash[2], hash[3]]; // track changes on Native
runOnJS(updateText)();
}, [hash[0], hash[1], hash[2], hash[3]]);
}, [hash[0], hash[1], hash[2], hash[3]]); // track changes on WEB

return <Animated.Text style={[styles.previewInitialText, ...style]}>{color}</Animated.Text>;
};
Expand All @@ -44,31 +44,29 @@ export function Preview({

const initialColorText = useMemo(() => {
const adaptiveTextColor = alphaValue.value > 0.5 ? value : { h: 0, s: 0, v: 70 };
const contrast = colorKit.contrastRatio(adaptiveTextColor, '#fff');
const color = contrast < 4.5 ? '#000' : '#fff';
const contrast = colorKit.contrastRatio(adaptiveTextColor, '#ffffff');
const color = contrast < 4.5 ? '#000000' : '#ffffff';
return { formatted: returnedResults()[colorFormat], color };
}, [value, colorFormat]);

const textColor = useSharedValue('#fff');
const textColor = useSharedValue<'#000000' | '#ffffff'>('#ffffff');
const textColorStyle = useAnimatedStyle(() => ({ color: textColor.value }), [textColor]);
const setTextColor = (color1: { h: number; s: number; v: number; a?: number }) => {
const color = textColor.value === '#ffffff' ? '#000000' : '#ffffff';
const contrast = colorKit.contrastRatio(color1, textColor.value);
textColor.value = contrast < 4.5 ? color : textColor.value;
};

const previewColor = useSharedValue('#fff');
const previewColor = useSharedValue('#ffffff');
const previewColorStyle = useAnimatedStyle(() => ({ backgroundColor: previewColor.value }), [previewColor]);
const setPreviewColor = (color: { h: number; s: number; v: number; a: number }) => {
previewColor.value = colorKit.HEX(color);
};

// When the values of channels change
useDerivedValue(() => {
const currentColor = { h: hueValue.value, s: saturationValue.value, v: brightnessValue.value, a: alphaValue.value };
const adaptiveTextColor = alphaValue.value > 0.5 ? currentColor : { h: 0, s: 0, v: 70 };
runOnJS(setPreviewColor)(currentColor);
runOnJS(setTextColor)(adaptiveTextColor);

previewColor.value = HSVA2HEX(hueValue.value, saturationValue.value, brightnessValue.value, alphaValue.value);

// calculate the contrast ratio
const compareColor1 = alphaValue.value > 0.5 ? currentColor : { h: 0, s: 0, v: 70 };
const compareColor2 = textColor.value === '#000000' ? { h: 0, s: 0, v: 0 } : { h: 0, s: 0, v: 100 };
const contrast = contrastRatio(compareColor1, compareColor2);
const reversedColor = textColor.value === '#ffffff' ? '#000000' : '#ffffff';
textColor.value = contrast < 4.5 ? reversedColor : textColor.value;
}, [hueValue, saturationValue, brightnessValue, alphaValue]);

return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/PreviewText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export function PreviewText({ style = {}, colorFormat = 'hex' }: PreviewTextProp
};

useDerivedValue(() => {
[colorFormat, hueValue, saturationValue, brightnessValue, alphaValue];
[colorFormat, hueValue, saturationValue, brightnessValue, alphaValue]; // track changes on Native
runOnJS(updateText)();
}, [colorFormat, hueValue, saturationValue, brightnessValue, alphaValue]);
}, [colorFormat, hueValue, saturationValue, brightnessValue, alphaValue]); // track changes on WEB

return <Text style={[styles.previewText, style]}>{text}</Text>;
}
49 changes: 19 additions & 30 deletions src/components/Thumb/Thumb.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import { runOnJS, useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated';
import { useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated';

import colorKit from '@colorKit';
import usePickerContext from '@context';
import { styles } from '@styles';
import { contrastRatio, HSVA2HEX } from '@utils';
import BuiltinThumbs from './BuiltinThumbs/index';

import type { BuiltinThumbsProps, ThumbProps } from '@types';
Expand All @@ -25,16 +25,7 @@ export default function Thumb({

const resultColor = useSharedValue('#ffffff');
const solidColor = useAnimatedStyle(() => ({ backgroundColor: resultColor.value }), [resultColor]);
const setResultColor = (color: { h: number; s: number; v: number; a?: number }) => {
resultColor.value = colorKit.HEX(color);
};

const adaptiveColor = useSharedValue('#ffffff');
const setAdaptiveColor = (color1: { h: number; s: number; v: number; a?: number }) => {
const color = adaptiveColor.value === '#ffffff' ? '#000000' : '#ffffff';
const contrast = colorKit.contrastRatio(color1, adaptiveColor.value);
adaptiveColor.value = contrast < 4.5 ? color : adaptiveColor.value;
};
const adaptiveColor = useSharedValue<'#000000' | '#ffffff'>('#ffffff');

/**
* Get the current color and calculate its contrast ratio against white or black,
Expand All @@ -44,32 +35,30 @@ export default function Thumb({
'worklet';
if (adaptSpectrum) {
if (channel === 'a') {
return alphaValue.value > 0.5
? { h: hueValue.value, s: saturationValue.value, v: brightnessValue.value }
: { h: 0, s: 0, v: 70 };
if (alphaValue.value > 0.5) return { h: hueValue.value, s: saturationValue.value, v: brightnessValue.value };
return { h: 0, s: 0, v: 70 };
}
return { h: hueValue.value, s: saturationValue.value, v: brightnessValue.value };
}

switch (channel) {
case 'h':
return { h: hueValue.value, s: 100, v: 100 };
case 'v':
return { h: hueValue.value, s: 100, v: brightnessValue.value };
case 's':
return { h: hueValue.value, s: saturationValue.value, v: 70 };
case 'a':
return { h: hueValue.value, s: alphaValue.value * 100, v: 70 };
default:
return { h: hueValue.value, s: saturationValue.value, v: brightnessValue.value };
}
if (channel === 'h') return { h: hueValue.value, s: 100, v: 100 };
if (channel === 'v') return { h: hueValue.value, s: 100, v: brightnessValue.value };
if (channel === 's') return { h: hueValue.value, s: saturationValue.value, v: 70 };
if (channel === 'a') return { h: hueValue.value, s: alphaValue.value * 100, v: 70 };
return { h: hueValue.value, s: saturationValue.value, v: brightnessValue.value };
};

// When the values of channels change
useDerivedValue(() => {
alphaValue;
runOnJS(setAdaptiveColor)(getColorForAdaptiveColor());
runOnJS(setResultColor)({ h: hueValue.value, s: saturationValue.value, v: brightnessValue.value });
alphaValue.value; // to track alpha changes too;
resultColor.value = HSVA2HEX(hueValue.value, saturationValue.value, brightnessValue.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 = contrastRatio(compareColor1, compareColor2);
const reversedColor = adaptiveColor.value === '#ffffff' ? '#000000' : '#ffffff';
adaptiveColor.value = contrast < 4.5 ? reversedColor : adaptiveColor.value;
}, [alphaValue, hueValue, saturationValue, brightnessValue]);

const thumbProps: BuiltinThumbsProps = {
Expand Down
37 changes: 37 additions & 0 deletions src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,43 @@ export function RGBA2HSVA(r: number, g: number, b: number, a = 1) {
};
}

/** - Convert an `RGB` color to its corresponding `Hex` color */
export function RGB_HEX(r: number, g: number, b: number, a = 1): string {
'worklet';
const red = Math.round(r).toString(16).padStart(2, '0');
const green = Math.round(g).toString(16).padStart(2, '0');
const blue = Math.round(b).toString(16).padStart(2, '0');
const alpha = Math.round(clamp(a * 255, 255))
.toString(16)
.padStart(2, '0');

return `#${red + green + blue + alpha}`;
}

/** - Convert an `HSV` color to its corresponding `Hex` color */
export function HSVA2HEX(h: number, s: number, v: number, a = 1) {
'worklet';
const { r, g, b, a: alpha } = HSVA2RGBA(h, s, v, a);
return RGB_HEX(r, g, b, alpha);
}

/** - Returns the perceived `luminance` of a color, from `0-1` as defined by Web Content Accessibility Guidelines (Version 2.0). */
export function getLuminanceWCAG(h: number, s: number, v: number): number {
'worklet';
const { r, g, b } = HSVA2RGBA(h, s, v);
const a = [r, g, b].map(val => (val / 255 <= 0.03928 ? val / 255 / 12.92 : Math.pow((val / 255 + 0.055) / 1.055, 2.4)));
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}

/** - Calculates the contrast ratio between two colors, useful for ensuring accessibility and readability. */
export function contrastRatio(color1: { h: number; s: number; v: number }, color2: { h: number; s: number; v: number }): number {
'worklet';
const luminance1 = getLuminanceWCAG(color1.h, color1.s, color1.v);
const luminance2 = getLuminanceWCAG(color2.h, color2.s, color2.v);
const contrast = (Math.max(luminance1, luminance2) + 0.05) / (Math.min(luminance1, luminance2) + 0.05);
return Math.round(contrast * 100) / 100;
}

/** - Render children only if the `render` property is `true` */
export function ConditionalRendering(props: { children: React.ReactNode; if: boolean }) {
if (!props.if) return null;
Expand Down

0 comments on commit d72565c

Please sign in to comment.