Collision Detection
Configure how draggable items detect collisions with drop zones using different algorithms.
Overview
Collision detection determines when a draggable item is considered to be "over" a drop zone. The library provides three built-in algorithms to suit different use cases:
- intersect: Any overlap between draggable and droppable (default)
- center: Center point of draggable must be over droppable
- contain: Entire draggable must be within droppable bounds
Key Features
- Multiple Algorithms: Choose the best detection method for your use case
- Visual Feedback: Clear indication when collision is detected
- Performance Optimized: Efficient calculations for smooth interactions
- Customizable: Per-draggable configuration options
- Real-time Updates: Immediate feedback during drag operations
Basic Implementation
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { DropProvider, Draggable, Droppable } from 'react-native-reanimated-dnd';
interface DraggableItemData {
id: string;
label: string;
color: string;
}
export function CollisionDetectionExample() {
const [activeZone, setActiveZone] = useState<string | null>(null);
const items: DraggableItemData[] = [
{ id: '1', label: 'Center', color: '#ff6b6b' },
{ id: '2', label: 'Intersect', color: '#4ecdc4' },
{ id: '3', label: 'Contain', color: '#45b7d1' },
];
return (
<GestureHandlerRootView style={styles.container}>
<DropProvider>
<View style={styles.content}>
<Text style={styles.title}>Collision Detection</Text>
<Text style={styles.subtitle}>
Different algorithms for detecting when items are over drop zones
</Text>
{/* Drop Zones */}
<View style={styles.dropZonesContainer}>
<Droppable<DraggableItemData>
droppableId="center-zone"
onDrop={(data) => console.log(`${data.label} dropped on center zone`)}
onActiveChange={(isActive) => setActiveZone(isActive ? 'center-zone' : null)}
style={[
styles.dropZone,
styles.centerZone,
activeZone === 'center-zone' && styles.activeZone
]}
>
<Text style={styles.zoneTitle}>Center Detection</Text>
<Text style={styles.zoneDescription}>
Center point must be over zone
</Text>
</Droppable>
<Droppable<DraggableItemData>
droppableId="intersect-zone"
onDrop={(data) => console.log(`${data.label} dropped on intersect zone`)}
onActiveChange={(isActive) => setActiveZone(isActive ? 'intersect-zone' : null)}
style={[
styles.dropZone,
styles.intersectZone,
activeZone === 'intersect-zone' && styles.activeZone
]}
>
<Text style={styles.zoneTitle}>Intersect Detection</Text>
<Text style={styles.zoneDescription}>
Any overlap triggers detection
</Text>
</Droppable>
<Droppable<DraggableItemData>
droppableId="contain-zone"
onDrop={(data) => console.log(`${data.label} dropped on contain zone`)}
onActiveChange={(isActive) => setActiveZone(isActive ? 'contain-zone' : null)}
style={[
styles.dropZone,
styles.containZone,
activeZone === 'contain-zone' && styles.activeZone
]}
>
<Text style={styles.zoneTitle}>Contain Detection</Text>
<Text style={styles.zoneDescription}>
Entire item must be inside zone
</Text>
</Droppable>
</View>
{/* Draggable Items */}
<View style={styles.draggableItemsArea}>
<Draggable<DraggableItemData>
data={items[0]}
collisionAlgorithm="center"
style={[styles.draggable, { backgroundColor: items[0].color }]}
>
<View style={styles.itemContent}>
<Text style={styles.itemLabel}>{items[0].label}</Text>
<Text style={styles.itemHint}>Center detection</Text>
</View>
</Draggable>
<Draggable<DraggableItemData>
data={items[1]}
collisionAlgorithm="intersect"
style={[styles.draggable, { backgroundColor: items[1].color }]}
>
<View style={styles.itemContent}>
<Text style={styles.itemLabel}>{items[1].label}</Text>
<Text style={styles.itemHint}>Intersect detection</Text>
</View>
</Draggable>
<Draggable<DraggableItemData>
data={items[2]}
collisionAlgorithm="contain"
style={[styles.draggable, { backgroundColor: items[2].color }]}
>
<View style={styles.itemContent}>
<Text style={styles.itemLabel}>{items[2].label}</Text>
<Text style={styles.itemHint}>Contain detection</Text>
</View>
</Draggable>
</View>
{/* Info */}
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>How it works:</Text>
<View style={styles.infoItem}>
<View style={[styles.infoIndicator, { backgroundColor: '#ff6b6b' }]} />
<Text style={styles.infoText}>
<Text style={styles.bold}>Center:</Text> Precise targeting, center point must be over zone
</Text>
</View>
<View style={styles.infoItem}>
<View style={[styles.infoIndicator, { backgroundColor: '#4ecdc4' }]} />
<Text style={styles.infoText}>
<Text style={styles.bold}>Intersect:</Text> Easy dropping, any overlap triggers detection
</Text>
</View>
<View style={styles.infoItem}>
<View style={[styles.infoIndicator, { backgroundColor: '#45b7d1' }]} />
<Text style={styles.infoText}>
<Text style={styles.bold}>Contain:</Text> Strict placement, entire item must fit inside
</Text>
</View>
</View>
</View>
</DropProvider>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000000',
},
content: {
flex: 1,
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#FFFFFF',
textAlign: 'center',
marginBottom: 8,
},
subtitle: {
fontSize: 15,
color: '#8E8E93',
textAlign: 'center',
marginBottom: 30,
lineHeight: 22,
},
dropZonesContainer: {
gap: 16,
marginBottom: 40,
},
dropZone: {
height: 100,
borderRadius: 12,
borderWidth: 2,
borderStyle: 'dashed',
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
centerZone: {
borderColor: '#ff6b6b',
backgroundColor: 'rgba(255, 107, 107, 0.1)',
},
intersectZone: {
borderColor: '#4ecdc4',
backgroundColor: 'rgba(78, 205, 196, 0.1)',
},
containZone: {
borderColor: '#45b7d1',
backgroundColor: 'rgba(69, 183, 209, 0.1)',
},
activeZone: {
borderColor: '#FFFFFF',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
transform: [{ scale: 1.02 }],
},
zoneTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#FFFFFF',
marginBottom: 4,
},
zoneDescription: {
fontSize: 12,
color: '#8E8E93',
textAlign: 'center',
},
draggableItemsArea: {
flexDirection: 'row',
justifyContent: 'space-around',
marginBottom: 40,
gap: 12,
},
draggable: {
width: 100,
height: 80,
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 6,
elevation: 8,
},
itemContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 8,
},
itemLabel: {
fontSize: 14,
fontWeight: 'bold',
color: '#FFFFFF',
marginBottom: 2,
},
itemHint: {
fontSize: 10,
color: 'rgba(255, 255, 255, 0.8)',
textAlign: 'center',
},
infoContainer: {
backgroundColor: '#1a1a1a',
borderRadius: 12,
padding: 16,
borderLeftWidth: 4,
borderLeftColor: '#58a6ff',
},
infoTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#FFFFFF',
marginBottom: 12,
},
infoItem: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
marginBottom: 8,
},
infoIndicator: {
width: 12,
height: 12,
borderRadius: 6,
},
infoText: {
fontSize: 14,
color: '#8E8E93',
flex: 1,
lineHeight: 20,
},
bold: {
fontWeight: 'bold',
color: '#FFFFFF',
},
});
Collision Algorithms
Center Detection
The center point of the draggable must be over the droppable:
<Draggable
data={itemData}
collisionAlgorithm="center"
>
{/* Content */}
</Draggable>
Use cases:
- Precise targeting required
- Small drop zones
- Grid-based layouts
- Slot-based interfaces
Intersect Detection (Default)
Any overlap between draggable and droppable triggers detection:
<Draggable
data={itemData}
collisionAlgorithm="intersect"
>
{/* Content */}
</Draggable>
Use cases:
- General drag and drop
- Large drop zones
- User-friendly interactions
- File management
Contain Detection
The entire draggable must be within the droppable bounds:
<Draggable
data={itemData}
collisionAlgorithm="contain"
>
{/* Content */}
</Draggable>
Use cases:
- Strict placement requirements
- Container-based layouts
- Puzzle games
- Form validation
Advanced Examples
Mixed Algorithm Interface
function MixedAlgorithmExample() {
return (
<DropProvider>
<View style={styles.container}>
{/* Precise targeting zone */}
<Droppable droppableId="precise-zone">
<Text>Precise Zone (use center algorithm)</Text>
</Droppable>
{/* Easy drop zone */}
<Droppable droppableId="easy-zone">
<Text>Easy Zone (use intersect algorithm)</Text>
</Droppable>
{/* Strict placement zone */}
<Droppable droppableId="strict-zone">
<Text>Strict Zone (use contain algorithm)</Text>
</Droppable>
{/* Items with different algorithms */}
<Draggable data={{ id: '1' }} collisionAlgorithm="center">
<Text>Precise Item</Text>
</Draggable>
<Draggable data={{ id: '2' }} collisionAlgorithm="intersect">
<Text>Easy Item</Text>
</Draggable>
<Draggable data={{ id: '3' }} collisionAlgorithm="contain">
<Text>Strict Item</Text>
</Draggable>
</View>
</DropProvider>
);
}
Dynamic Algorithm Switching
function DynamicAlgorithmExample() {
const [algorithm, setAlgorithm] = useState<'center' | 'intersect' | 'contain'>('intersect');
const [precision, setPrecision] = useState('normal');
const getAlgorithm = () => {
switch (precision) {
case 'high': return 'center';
case 'low': return 'intersect';
case 'strict': return 'contain';
default: return 'intersect';
}
};
return (
<DropProvider>
<View style={styles.container}>
<View style={styles.controls}>
<Text>Precision:</Text>
{['high', 'normal', 'low', 'strict'].map((level) => (
<TouchableOpacity
key={level}
onPress={() => setPrecision(level)}
style={[
styles.button,
precision === level && styles.activeButton
]}
>
<Text>{level}</Text>
</TouchableOpacity>
))}
</View>
<Draggable
data={{ id: 'dynamic' }}
collisionAlgorithm={getAlgorithm()}
>
<Text>Dynamic Algorithm Item</Text>
</Draggable>
</View>
</DropProvider>
);
}
Visual Feedback Patterns
Algorithm-Specific Feedback
function AlgorithmFeedback({ algorithm, isActive }) {
const getFeedbackStyle = () => {
const baseStyle = styles.dropZone;
if (!isActive) return baseStyle;
switch (algorithm) {
case 'center':
return [baseStyle, styles.centerActive];
case 'intersect':
return [baseStyle, styles.intersectActive];
case 'contain':
return [baseStyle, styles.containActive];
default:
return baseStyle;
}
};
return (
<View style={getFeedbackStyle()}>
<Text>{algorithm} detection</Text>
{isActive && <Text>✓ Collision detected</Text>}
</View>
);
}
Real-time Collision Indicator
function CollisionIndicator({ draggableId, droppableId, algorithm }) {
const [isColliding, setIsColliding] = useState(false);
return (
<View style={styles.indicator}>
<Text>Algorithm: {algorithm}</Text>
<View style={[
styles.status,
isColliding ? styles.colliding : styles.notColliding
]}>
<Text>{isColliding ? 'Colliding' : 'Not Colliding'}</Text>
</View>
</View>
);
}
Performance Considerations
Optimizing Collision Detection
// Use appropriate algorithm for your use case
const getOptimalAlgorithm = (useCase: string) => {
switch (useCase) {
case 'file-manager':
return 'intersect'; // Easy dropping
case 'grid-layout':
return 'center'; // Precise placement
case 'container-puzzle':
return 'contain'; // Strict fitting
default:
return 'intersect';
}
};
// Minimize collision checks for better performance
<Draggable
data={itemData}
collisionAlgorithm={getOptimalAlgorithm('file-manager')}
// Other optimizations handled by library
>
{/* Content */}
</Draggable>
Common Use Cases
File Management
// Easy file dropping with intersect
<Draggable collisionAlgorithm="intersect">
<FileIcon />
</Draggable>
Grid Layouts
// Precise grid placement with center
<Draggable collisionAlgorithm="center">
<GridItem />
</Draggable>
Container Interfaces
// Strict container fitting with contain
<Draggable collisionAlgorithm="contain">
<ContainerItem />
</Draggable>
Best Practices
- Choose Appropriate Algorithm: Match algorithm to use case requirements
- Provide Visual Feedback: Show users when collision is detected
- Consider User Experience: Balance precision with ease of use
- Test Different Scenarios: Verify behavior with various item sizes
- Performance Optimization: Use the most efficient algorithm for your needs
Troubleshooting
Common Issues
Collision not detected:
- Check if algorithm matches your expectations
- Verify drop zone size is appropriate
- Ensure proper positioning
Too sensitive/not sensitive enough:
- Switch between algorithms
- Adjust drop zone sizes
- Consider user feedback
Performance issues:
- Use simpler algorithms when possible
- Optimize component re-renders
- Check for unnecessary calculations
Next Steps
- Learn about Drop Zones for advanced zone configurations
- Explore Visual Feedback for better user experience
- Check out Custom Animations for collision feedback