Examples
Axis Constraints
Restrict dragging to specific axes for controlled directional movement.
Overview
Axis constraints limit draggable items to move only along specific directions (horizontal or vertical). This example demonstrates:
- Horizontal-only dragging
- Vertical-only dragging
- Free movement (both axes)
- Dynamic axis switching
Key Features
- Directional Control: Lock movement to X or Y axis
- Dynamic Switching: Change constraints during interaction
- Visual Guides: Show allowed movement directions
- Smooth Interactions: Natural feel within constraints
Basic Implementation
import React, { useState } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { DropProvider, Draggable } from "react-native-reanimated-dnd";
export function AxisConstraintsExample() {
const [constraint, setConstraint] = useState<"x" | "y" | "both">("x");
return (
<GestureHandlerRootView style={styles.container}>
<DropProvider>
<View style={styles.content}>
<Text style={styles.title}>Axis Constraints</Text>
{/* Constraint Controls */}
<View style={styles.controlsContainer}>
{["x", "y", "both"].map((axis) => (
<TouchableOpacity
key={axis}
style={[
styles.controlButton,
constraint === axis && styles.activeButton,
]}
onPress={() => setConstraint(axis as any)}
>
<Text
style={[
styles.controlText,
constraint === axis && styles.activeText,
]}
>
{axis === "x"
? "Horizontal"
: axis === "y"
? "Vertical"
: "Free"}
</Text>
</TouchableOpacity>
))}
</View>
{/* Drag Area */}
<View style={styles.dragArea}>
<Text style={styles.areaLabel}>Drag Area</Text>
{/* Constraint Guides */}
{constraint === "x" && <View style={styles.horizontalGuide} />}
{constraint === "y" && <View style={styles.verticalGuide} />}
{/* Constrained Draggable */}
<Draggable
data={{ id: "constrained-item", constraint }}
dragAxis={constraint}
style={styles.draggable}
>
<View style={styles.draggableContent}>
<Text style={styles.draggableText}>Drag Me</Text>
<Text style={styles.constraintText}>
{constraint === "x"
? "← →"
: constraint === "y"
? "↑ ↓"
: "↗"}
</Text>
</View>
</Draggable>
</View>
{/* Info */}
<View style={styles.infoContainer}>
<Text style={styles.infoText}>
Current constraint:{" "}
{constraint === "x"
? "Horizontal only"
: constraint === "y"
? "Vertical only"
: "No constraints"}
</Text>
</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: 30,
},
controlsContainer: {
flexDirection: "row",
justifyContent: "center",
marginBottom: 30,
gap: 12,
},
controlButton: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
backgroundColor: "#1a1a1a",
borderWidth: 1,
borderColor: "#333333",
},
activeButton: {
backgroundColor: "#58a6ff",
borderColor: "#58a6ff",
},
controlText: {
color: "#8E8E93",
fontSize: 14,
fontWeight: "500",
},
activeText: {
color: "#FFFFFF",
},
dragArea: {
height: 300,
backgroundColor: "#1a1a1a",
borderRadius: 16,
borderWidth: 2,
borderColor: "#333333",
marginBottom: 20,
position: "relative",
justifyContent: "center",
alignItems: "center",
},
areaLabel: {
position: "absolute",
top: -12,
left: 20,
backgroundColor: "#000000",
color: "#8E8E93",
fontSize: 12,
fontWeight: "600",
paddingHorizontal: 8,
},
horizontalGuide: {
position: "absolute",
top: "50%",
left: 20,
right: 20,
height: 2,
backgroundColor: "#58a6ff",
opacity: 0.5,
},
verticalGuide: {
position: "absolute",
left: "50%",
top: 20,
bottom: 20,
width: 2,
backgroundColor: "#58a6ff",
opacity: 0.5,
},
draggable: {
position: "absolute",
},
draggableContent: {
width: 80,
height: 80,
backgroundColor: "#a2d2ff",
borderRadius: 16,
justifyContent: "center",
alignItems: "center",
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 6,
elevation: 8,
},
draggableText: {
color: "#000000",
fontWeight: "bold",
fontSize: 14,
},
constraintText: {
color: "#333333",
fontSize: 16,
marginTop: 4,
},
infoContainer: {
backgroundColor: "#1a1a1a",
borderRadius: 12,
padding: 16,
borderLeftWidth: 4,
borderLeftColor: "#58a6ff",
},
infoText: {
color: "#8E8E93",
fontSize: 14,
lineHeight: 20,
},
});Constraint Types
Horizontal Constraint
<Draggable data={itemData} dragAxis="x">
{/* Can only move left and right */}
</Draggable>Vertical Constraint
<Draggable data={itemData} dragAxis="y">
{/* Can only move up and down */}
</Draggable>Free Movement
<Draggable data={itemData} dragAxis="both">
{/* Can move in any direction (default) */}
</Draggable>Advanced Examples
Dynamic Constraint Switching
function DynamicConstraintDraggable() {
const [constraint, setConstraint] = useState<"x" | "y" | "both">("x");
return (
<View style={styles.container}>
<View style={styles.controls}>
{["x", "y", "both"].map((axis) => (
<TouchableOpacity
key={axis}
onPress={() => setConstraint(axis as any)}
style={[
styles.controlButton,
constraint === axis && styles.activeButton,
]}
>
<Text>{axis}</Text>
</TouchableOpacity>
))}
</View>
<Draggable data={itemData} dragAxis={constraint}>
<View style={styles.draggableItem}>
<Text>Constraint: {constraint}</Text>
</View>
</Draggable>
</View>
);
}Conditional Constraints
function ConditionalConstraintDraggable({ isLocked }) {
return (
<Draggable data={itemData} dragAxis={isLocked ? "x" : "both"}>
<View style={styles.draggableItem}>
<Text>{isLocked ? "Horizontal Only" : "Free Movement"}</Text>
</View>
</Draggable>
);
}Slider Components
function HorizontalSlider({ value, onValueChange }) {
return (
<View style={styles.sliderContainer}>
<Draggable
data={{ value }}
dragAxis="x"
onDragging={({ x, tx }) => {
// Calculate new value based on position
const newValue = calculateValueFromPosition(x + tx);
onValueChange(newValue);
}}
>
<View style={styles.sliderThumb}>
<Text>{value}</Text>
</View>
</Draggable>
</View>
);
}
function VerticalSlider({ value, onValueChange }) {
return (
<View style={styles.sliderContainer}>
<Draggable
data={{ value }}
dragAxis="y"
onDragging={({ y, ty }) => {
// Calculate new value based on position
const newValue = calculateValueFromPosition(y + ty);
onValueChange(newValue);
}}
>
<View style={styles.sliderThumb}>
<Text>{value}</Text>
</View>
</Draggable>
</View>
);
}Common Use Cases
- Sliders: Horizontal or vertical value controls
- Resizing: Constrain resize handles to specific directions
- Scrolling: Lock scroll direction in custom scroll views
- Games: Restrict character movement to specific paths
- Form Controls: Directional input components
Best Practices
- Visual Feedback: Show constraint guides clearly to indicate allowed movement
- Consistent Behavior: Use the same constraint patterns throughout your app
- User Control: Allow users to change constraints when appropriate
- Accessibility: Provide clear labels for constraint states
- Performance: Axis constraints can improve performance by reducing calculations
Limitations
- Library Constraints: Only supports 'x', 'y', and 'both' axis constraints
- No Custom Angles: The library doesn't support diagonal or custom angle constraints
- No Snap-to-Axis: Automatic snapping functionality is not built-in
Next Steps
- Explore Bounded Dragging for area constraints
- Learn about Custom Animations for constraint feedback
- Check out Sortable Lists for ordered arrangements