or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

animations.mdcolors.mdcomponents.mdcoordinates.mdindex.mdmath.mdmatrices.mdpaths.mdtransforms.mdtransitions.mdutilities.mdvectors.md

utilities.mddocs/

0

# Utility Functions

1

2

Additional utility functions for physics calculations and array manipulations that support common animation and interaction patterns.

3

4

## Capabilities

5

6

### Physics Utilities

7

8

#### Snap Point Selection

9

10

Calculate the optimal snap point based on gesture velocity and current position.

11

12

```typescript { .api }

13

/**

14

* Select snap point based on value and velocity

15

* @param value - Current position/value

16

* @param velocity - Current velocity

17

* @param points - Array of possible snap points

18

* @returns The optimal snap point

19

*/

20

function snapPoint(

21

value: number,

22

velocity: number,

23

points: ReadonlyArray<number>

24

): number;

25

```

26

27

**Usage Example:**

28

29

```typescript

30

import { snapPoint } from "react-native-redash";

31

import {

32

useSharedValue,

33

useAnimatedGestureHandler,

34

withSpring

35

} from "react-native-reanimated";

36

import { PanGestureHandler } from "react-native-gesture-handler";

37

38

export const SnapScrollView = () => {

39

const translateX = useSharedValue(0);

40

41

// Define snap points (e.g., for horizontal card stack)

42

const snapPoints = [0, -150, -300, -450];

43

44

const gestureHandler = useAnimatedGestureHandler({

45

onStart: () => {

46

// Store initial position if needed

47

},

48

onActive: (event) => {

49

translateX.value = event.translationX;

50

},

51

onEnd: (event) => {

52

// Find optimal snap point based on position and velocity

53

const destination = snapPoint(

54

translateX.value,

55

event.velocityX,

56

snapPoints

57

);

58

59

// Animate to snap point

60

translateX.value = withSpring(destination, {

61

damping: 15,

62

stiffness: 100

63

});

64

}

65

});

66

67

const animatedStyle = useAnimatedStyle(() => ({

68

transform: [{ translateX: translateX.value }]

69

}));

70

71

return (

72

<PanGestureHandler onGestureEvent={gestureHandler}>

73

<Animated.View style={[styles.scrollContainer, animatedStyle]}>

74

{/* Scrollable content */}

75

</Animated.View>

76

</PanGestureHandler>

77

);

78

};

79

```

80

81

**Advanced Snap Point Example:**

82

83

```typescript

84

import { snapPoint } from "react-native-redash";

85

86

export const VerticalSnapCarousel = () => {

87

const translateY = useSharedValue(0);

88

const cardHeight = 200;

89

const cardSpacing = 20;

90

91

// Generate snap points for vertical card stack

92

const snapPoints = Array.from({ length: 5 }, (_, i) =>

93

-(i * (cardHeight + cardSpacing))

94

);

95

96

const gestureHandler = useAnimatedGestureHandler({

97

onEnd: (event) => {

98

const currentPosition = translateY.value;

99

const velocity = event.velocityY;

100

101

// Factor in velocity for more natural snapping

102

const destination = snapPoint(currentPosition, velocity, snapPoints);

103

104

translateY.value = withSpring(destination, {

105

damping: 20,

106

stiffness: 200,

107

mass: 1

108

});

109

}

110

});

111

112

return (

113

<PanGestureHandler onGestureEvent={gestureHandler}>

114

<Animated.View style={animatedStyle}>

115

{/* Cards */}

116

</Animated.View>

117

</PanGestureHandler>

118

);

119

};

120

```

121

122

### Array Utilities

123

124

#### Array Element Moving

125

126

Move elements within an array while maintaining proper indices.

127

128

```typescript { .api }

129

/**

130

* Move array element from one index to another

131

* @param input - Input array to modify

132

* @param from - Source index

133

* @param to - Destination index

134

* @returns New array with moved element

135

*/

136

function move<T>(input: T[], from: number, to: number): T[];

137

```

138

139

**Usage Example:**

140

141

```typescript

142

import { move } from "react-native-redash";

143

import { useSharedValue, runOnJS } from "react-native-reanimated";

144

145

export const ReorderableList = () => {

146

const [items, setItems] = useState(['A', 'B', 'C', 'D', 'E']);

147

const draggedIndex = useSharedValue(-1);

148

149

const moveItem = (fromIndex: number, toIndex: number) => {

150

setItems(current => move(current, fromIndex, toIndex));

151

};

152

153

const gestureHandler = useAnimatedGestureHandler({

154

onStart: (event, context) => {

155

// Determine which item is being dragged

156

const index = Math.floor(event.y / ITEM_HEIGHT);

157

draggedIndex.value = index;

158

context.startIndex = index;

159

},

160

onActive: (event, context) => {

161

const currentIndex = Math.floor(event.y / ITEM_HEIGHT);

162

163

if (currentIndex !== context.startIndex) {

164

// Move item in array

165

runOnJS(moveItem)(context.startIndex, currentIndex);

166

context.startIndex = currentIndex;

167

}

168

},

169

onEnd: () => {

170

draggedIndex.value = -1;

171

}

172

});

173

174

return (

175

<PanGestureHandler onGestureEvent={gestureHandler}>

176

<View>

177

{items.map((item, index) => (

178

<ReorderableItem

179

key={item}

180

item={item}

181

index={index}

182

isDragged={draggedIndex.value === index}

183

/>

184

))}

185

</View>

186

</PanGestureHandler>

187

);

188

};

189

```

190

191

**Animated List Reordering:**

192

193

```typescript

194

import { move } from "react-native-redash";

195

import {

196

useSharedValue,

197

useAnimatedStyle,

198

withSpring,

199

runOnJS

200

} from "react-native-reanimated";

201

202

export const AnimatedReorderableList = () => {

203

const [data, setData] = useState([

204

{ id: '1', title: 'Item 1' },

205

{ id: '2', title: 'Item 2' },

206

{ id: '3', title: 'Item 3' },

207

{ id: '4', title: 'Item 4' }

208

]);

209

210

const positions = useSharedValue(

211

data.map((_, index) => index)

212

);

213

214

const reorderItems = (from: number, to: number) => {

215

// Update both data and positions

216

setData(current => move(current, from, to));

217

positions.value = move(positions.value, from, to);

218

};

219

220

const createGestureHandler = (index: number) =>

221

useAnimatedGestureHandler({

222

onActive: (event) => {

223

const newIndex = Math.floor(event.absoluteY / ITEM_HEIGHT);

224

225

if (newIndex !== index && newIndex >= 0 && newIndex < data.length) {

226

runOnJS(reorderItems)(index, newIndex);

227

}

228

}

229

});

230

231

return (

232

<View>

233

{data.map((item, index) => {

234

const animatedStyle = useAnimatedStyle(() => ({

235

transform: [

236

{ translateY: withSpring(positions.value[index] * ITEM_HEIGHT) }

237

]

238

}));

239

240

return (

241

<PanGestureHandler

242

key={item.id}

243

onGestureEvent={createGestureHandler(index)}

244

>

245

<Animated.View style={[styles.item, animatedStyle]}>

246

<Text>{item.title}</Text>

247

</Animated.View>

248

</PanGestureHandler>

249

);

250

})}

251

</View>

252

);

253

};

254

```

255

256

### Combined Physics and Array Example

257

258

```typescript

259

import { snapPoint, move } from "react-native-redash";

260

261

export const SnapReorderCarousel = () => {

262

const [items, setItems] = useState(['Card 1', 'Card 2', 'Card 3', 'Card 4']);

263

const translateX = useSharedValue(0);

264

const activeIndex = useSharedValue(0);

265

266

const cardWidth = 250;

267

const cardSpacing = 20;

268

269

// Calculate snap points for each card

270

const snapPoints = items.map((_, index) =>

271

-(index * (cardWidth + cardSpacing))

272

);

273

274

const gestureHandler = useAnimatedGestureHandler({

275

onActive: (event) => {

276

translateX.value = event.translationX;

277

278

// Calculate which card is currently in focus

279

const currentIndex = Math.round(-translateX.value / (cardWidth + cardSpacing));

280

activeIndex.value = Math.max(0, Math.min(items.length - 1, currentIndex));

281

},

282

onEnd: (event) => {

283

// Snap to nearest card

284

const destination = snapPoint(

285

translateX.value,

286

event.velocityX,

287

snapPoints

288

);

289

290

translateX.value = withSpring(destination);

291

292

// Update active index based on final position

293

const finalIndex = Math.round(-destination / (cardWidth + cardSpacing));

294

activeIndex.value = finalIndex;

295

}

296

});

297

298

// Double tap to move card to front

299

const doubleTapHandler = useAnimatedGestureHandler({

300

onEnd: () => {

301

const currentIndex = activeIndex.value;

302

if (currentIndex > 0) {

303

runOnJS(setItems)(current => move(current, currentIndex, 0));

304

translateX.value = withSpring(0);

305

activeIndex.value = 0;

306

}

307

}

308

});

309

310

const animatedStyle = useAnimatedStyle(() => ({

311

transform: [{ translateX: translateX.value }]

312

}));

313

314

return (

315

<PanGestureHandler onGestureEvent={gestureHandler}>

316

<TapGestureHandler numberOfTaps={2} onGestureEvent={doubleTapHandler}>

317

<Animated.View style={[styles.carousel, animatedStyle]}>

318

{items.map((item, index) => (

319

<View key={item} style={styles.card}>

320

<Text>{item}</Text>

321

</View>

322

))}

323

</Animated.View>

324

</TapGestureHandler>

325

</PanGestureHandler>

326

);

327

};

328

```

329

330

**Utility Function Behavior:**

331

332

- `snapPoint`: Uses velocity and position to predict where the user intends to end up

333

- `move`: Handles negative indices by wrapping around the array length

334

- Both functions are optimized for use in gesture handlers and animations

335

- `snapPoint` considers momentum (velocity * 0.2) when calculating the target destination

336

- `move` creates a new array and handles edge cases like moving beyond array bounds