Expo 44, Reanimated V2.3.1 bug on interpolateColor
See original GitHub issueDescription
The error reads as Exception in HostFunction: Javascript worklet error [native code]
[native code]
the error originates from interpolateColor. I managed to fix the error by using my own implementation of interpolateColor. Note: the same code base worked fine in previous Reanimated V2 versions.
Expected behavior
The interpolateColor should return a color value interpolated between the first and second colors. Instead, it throws Exception in HostFunction: Javascript worklet error [native code]
[native code]
Snack or minimal code example
problematic code
const isActive = useSharedValue(false);
const activeProgress = useDerivedValue(()=>{
return withTiming(isActive.value ? 100 : 0);
}, [isActive.value]);
const rStyle = useAnimatedStyle(()=>{
return {
backgroundColor: interpolateColor(
activeProgress.value,
[0, 100],
[color1, color2]
),
}
}, [activeProgress.value, color1, color2]);
the rStyle is assigned to an Animated.View component as usual.
Quick Fix
until the bug is resolved, fellow programmers can use this code as a replacement for interpolateColor. please note that the below code does not work with color names like “red” and “yellow”. But it would work with both 6 digit, and 8 digit hex values. any rgba and rgb value would also work.
import {interpolate} from "react-native-reanimated";
function hexToRGBA(hw: string){
'worklet';
if (hw.length === 4){
return {
r: parseInt(hw.substr(1, 1), 16),
g: parseInt(hw.substr(2, 1), 16),
b: parseInt(hw.substr(3, 1), 16),
a: 1
}
}
const r = parseInt(hw.slice(1, 3), 16);
const g = parseInt(hw.slice(3, 5), 16);
const b = parseInt(hw.slice(5, 7), 16);
const a = hw.length === 9 ? parseInt(hw.slice(7, 9), 16)/255 : 1;
return {
r,g,b,a
}
}
function colorToRGBA(color: string|{r:number, g: number, b: number}|{r:number, g: number, b: number, a: number}){
'worklet';
if (typeof color === 'string'){
if (color.startsWith("rgba(")){
const colorProcessed = color.split("(")[1].split(")")[0].split(",");
return {
r: parseInt(colorProcessed[0].trim()),
g: parseInt(colorProcessed[1].trim()),
b: parseInt(colorProcessed[2].trim()),
a: parseInt(colorProcessed[3].trim()),
}
}
else if (color.startsWith("rgb(")){
const colorProcessed = color.split("(")[1].split(")")[0].split(",");
return {
r: parseInt(colorProcessed[0].trim()),
g: parseInt(colorProcessed[1].trim()),
b: parseInt(colorProcessed[2].trim()),
a: 1,
}
}
else {
return hexToRGBA(color);
}
} else {
return {
r: color.r,
g: color.g,
b: color.b,
// @ts-ignore
a: Object.keys(color).includes('a') ? color.a : 1,
}
}
}
export default function interpolateColorBugFix(value: number, inputRange: readonly number[], outputRange: readonly (string | number)[]){
'worklet';
const outputRangeProcessed = outputRange.map(i => colorToRGBA(i as string));
const values = {
r: interpolate(
value,
inputRange,
outputRangeProcessed.map(i => i.r)
),
g: interpolate(
value,
inputRange,
outputRangeProcessed.map(i => i.g)
),
b: interpolate(
value,
inputRange,
outputRangeProcessed.map(i => i.b)
),
a: interpolate(
value,
inputRange,
outputRangeProcessed.map(i => i.a)
),
}
return `rgba(${values.r},${values.g},${values.b}, ${values.a})`
}
Issue Analytics
- State:
- Created 2 years ago
- Reactions:17
- Comments:7 (2 by maintainers)
Top GitHub Comments
Unfortunately the problem is not caused by withTiming. The interpolateColor does not work sometimes even if the withTiming function is not used. It is unclear to me if underlay, the interpolateColor uses withTiming in any way shape or form. (I don’t think it is supposed to). The problem is within the interpolateColor. It is worth noting that interpolate works as expected. So in my quick fix I used interpolate and fit fixed the problem.
BTW, if you could please follow the issue template, it would be very helpful.