Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ runs:

- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --immutable
run: yarn install --no-immutable
shell: bash
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18
v22.14.0
541 changes: 0 additions & 541 deletions .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs

This file was deleted.

28 changes: 0 additions & 28 deletions .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs

This file was deleted.

11 changes: 5 additions & 6 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
nodeLinker: node-modules
compressionLevel: mixed

enableGlobalCache: false

nmHoistingLimits: workspaces

plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-3.6.1.cjs
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-gesture-handler": "^2.22.1",
"react-native-gesture-handler": ">=2.20.0",
"react-native-reanimated": "^3.16.7"
},
"workspaces": [
Expand Down Expand Up @@ -155,5 +155,8 @@
"eslintIgnore": [
"node_modules/",
"lib/"
]
],
"dependencies": {
"color-alpha": "^2.0.0"
}
}
36 changes: 25 additions & 11 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
import Animated, {
useSharedValue,
useAnimatedStyle,
runOnJS,
withTiming,
} from 'react-native-reanimated';
import alpha from 'color-alpha';

let Haptics: any = null;
try {
Expand Down Expand Up @@ -42,7 +42,6 @@ interface JoystickData {
}

const getDirection = (angleDeg: number): string => {
'worklet';
if (
(angleDeg >= 337.5 && angleDeg <= 360) ||
(angleDeg >= 0 && angleDeg < 22.5)
Expand Down Expand Up @@ -76,6 +75,16 @@ const debounce = (func: (...args: any[]) => void, delay: number) => {
};
};

/**
* Applies alpha transparency to a color using the color-alpha library
* @param color - Color string (named color, hex, rgb, etc.)
* @param opacity - Alpha value between 0 and 1
* @returns RGBA color string
*/
const withAlpha = (color: string, opacity: number): string => {
return alpha(color, opacity);
};

export const Joystick: React.FC<JoystickProps> = React.memo(
({
onMove = () => {},
Expand All @@ -92,6 +101,12 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
const JOYSTICK_RADIUS = size / 6;
const MAX_DISTANCE = BOUNDARY_RADIUS + JOYSTICK_RADIUS / 2;

// Pre-compute colors to avoid calling color-alpha inside worklets
const boundaryColorActive = withAlpha(color, 0.2);
const boundaryColorInactive = color;
const backgroundColorBoundary = withAlpha(color, 0.2);
const backgroundColorJoystick = withAlpha(color, 0.6);

const debouncedOnMove = useCallback(
(data: JoystickData) => {
debounce(() => {
Expand All @@ -104,14 +119,13 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
const panGesture = useMemo(
() =>
Gesture.Pan()
.runOnJS(true)
.onBegin(() => {
'worklet';
if (haptics && Haptics) {
runOnJS(Haptics.impactAsync)(Haptics.ImpactFeedbackStyle.Heavy);
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
}
})
.onUpdate((event: { translationX: any; translationY: any }) => {
'worklet';
const rawX = event.translationX;
const rawY = event.translationY;
const distance = Math.sqrt(rawX ** 2 + rawY ** 2);
Expand All @@ -136,7 +150,7 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
const normalizedX = (newX / BOUNDARY_RADIUS) * size;
const normalizedY = -(newY / BOUNDARY_RADIUS) * size;

runOnJS(debouncedOnMove)({
debouncedOnMove({
position: {
x: Math.max(-size, Math.min(size, normalizedX)),
y: Math.max(-size, Math.min(size, normalizedY)),
Expand All @@ -155,7 +169,7 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
translateX.value = withTiming(0, { duration: 300 });
translateY.value = withTiming(0, { duration: 300 });
if (onEnd) {
runOnJS(onEnd)();
onEnd();
}
}),
[
Expand All @@ -181,8 +195,8 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
return {
borderColor:
Math.abs(translateX.value) > 0 || Math.abs(translateY.value) > 0
? `${color}33`
: color,
? boundaryColorActive
: boundaryColorInactive,
};
});

Expand All @@ -198,7 +212,7 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
width: size,
height: size,
borderRadius: BOUNDARY_RADIUS,
backgroundColor: `${color}33`,
backgroundColor: backgroundColorBoundary,
// borderColor: color,
},
boundaryStyle, // Applying the animated style here
Expand All @@ -213,7 +227,7 @@ export const Joystick: React.FC<JoystickProps> = React.memo(
width: JOYSTICK_RADIUS * 2,
height: JOYSTICK_RADIUS * 2,
borderRadius: JOYSTICK_RADIUS,
backgroundColor: `${color}99`,
backgroundColor: backgroundColorJoystick,
},
animatedStyle,
]}
Expand Down
Loading
Loading