Detect colisions between two elements using React Native.

Detect collision is very useful if we want to make small games with React Native even using Expo.
First, we will take reference from our element of the tutorial at Allow dragging elements on screen in React Native.
DragComponent.tsx
import React, { useRef, ReactNode, useState } from 'react';import { Animated, PanResponder, StyleProp, ViewStyle } from 'react-native';interface DragableProps { children: ReactNode; style?: StyleProp<ViewStyle>;}const DragComponent: React.FC<DraggableProps> = ({ children, style }) => { const [offset, setOffset] = useState({ x: 0, y: 0 }); const pan = useRef(new Animated.ValueXY()).current; const panResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: (_, gesture) => { pan.setValue({ x: gesture.dx + offset.x, y: gesture.dy + offset.y }); }, onPanResponderRelease: (_, gesture) => { setOffset({ x: gesture.dx + offset.x, y: gesture.dy + offset.y }); }, }); return ( <Animated.View {...panResponder.panHandlers} style={[ { transform: [{ translateX: pan.x }, { translateY: pan.y }], }, style, ]} > {children} </Animated.View> );};export default DragComponent;
import { LayoutRectangle } from "react-native";export interface ElementDrag { id: string; layout: LayoutRectangle;}
Pantalla.tsx
const [otherLayouts, setOtherLayouts] = useState<ElementDrag[]>([]); // Referencias a los otros elementos const ref1 = useRef<View>(null); const ref2 = useRef<View>(null); useEffect(() => { ref1.current?.measureInWindow((x, y, width, height) => { setOtherLayouts((layouts) => [...layouts, { id: 'elemento1', layout: { x, y, width, height } }]); }); ref2.current?.measureInWindow((x, y, width, height) => { setOtherLayouts((layouts) => [...layouts, { id: 'elemento2', layout: { x, y, width, height } }]); }); }, []);
We need to assign the reference to the element or elements:
<View ref={ref1} style={{ width: 80, height: 120, backgroundColor: 'red' }} > </View>...<View ref={ref2} style={{ width: 80, height: 120, backgroundColor: 'green' }} > </View>
We are now going to add the collision logic to our draggable element:
import React, { useRef, ReactNode, useState } from 'react'; import { Animated, PanResponder, StyleProp, ViewStyle } from 'react-native'; interface DragableProps { children: ReactNode; style?: StyleProp<ViewStyle>; onCollision?: (a: ElementDrag, b: React.Dispatch<React.SetStateAction<{ x: number; y: number; }>>) => void; otherLayouts?: ElementDrag[];} const DragComponent: React.FC<DraggableProps> = ({ children, style }) => { const [offset, setOffset] = useState({ x: 0, y: 0 }); const pan = useRef(new Animated.ValueXY()).current; function detectarColision(gestureState: PanResponderGestureState) { if (layout.current && otherLayouts) { const x = gestureState.moveX - layout.current.width / 2; const y = gestureState.moveY - layout.current.height / 2; const currentLayout = { ...layout.current, x, y }; otherLayouts.forEach((elemento: ElementDrag) => { const collision = checkCollision(currentLayout, elemento.layout); if (collision && onCollision) { onCollision(elemento, setNuevoPan); } }); } } const panResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: (_, gesture) => { pan.setValue({ x: gesture.dx + offset.x, y: gesture.dy + offset.y }); detectarColision(gesture); }, onPanResponderRelease: (_, gesture) => { setOffset({ x: gesture.dx + offset.x, y: gesture.dy + offset.y }); }, }); return ( <Animated.View {...panResponder.panHandlers} style={[ { transform: [{ translateX: pan.x }, { translateY: pan.y }], }, style, ]} > {children} </Animated.View> );};export default DragComponent;
Initialization of state and references:
Collision Detection Function (detectarColision
):
PanResponder:
Rendering:
The code detects collisions by continuously checking if the draggable component overlaps with other elements on the screen during a drag gesture. If a collision is detected, it calls a provided function to handle it.
And now we will add the function to detect collisions to the main element:
>) => { console.log('Collision detected with the element ' + elemento.id + ' !'); }} otherLayouts={otherLayouts} >
When a collision is detected it will appear in the console:

