@leonsilicon/react-native-reanimated-carousel 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +652 -0
  3. package/lib/commonjs/components/Carousel.js +2 -0
  4. package/lib/commonjs/components/Carousel.js.map +1 -0
  5. package/lib/commonjs/components/CarouselLayout.js +2 -0
  6. package/lib/commonjs/components/CarouselLayout.js.map +1 -0
  7. package/lib/commonjs/components/ItemLayout.js +2 -0
  8. package/lib/commonjs/components/ItemLayout.js.map +1 -0
  9. package/lib/commonjs/components/ItemRenderer.js +2 -0
  10. package/lib/commonjs/components/ItemRenderer.js.map +1 -0
  11. package/lib/commonjs/components/LazyView.js +2 -0
  12. package/lib/commonjs/components/LazyView.js.map +1 -0
  13. package/lib/commonjs/components/Pagination/Basic/PaginationItem.js +2 -0
  14. package/lib/commonjs/components/Pagination/Basic/PaginationItem.js.map +1 -0
  15. package/lib/commonjs/components/Pagination/Basic/index.js +2 -0
  16. package/lib/commonjs/components/Pagination/Basic/index.js.map +1 -0
  17. package/lib/commonjs/components/Pagination/Custom/PaginationItem.js +2 -0
  18. package/lib/commonjs/components/Pagination/Custom/PaginationItem.js.map +1 -0
  19. package/lib/commonjs/components/Pagination/Custom/index.js +2 -0
  20. package/lib/commonjs/components/Pagination/Custom/index.js.map +1 -0
  21. package/lib/commonjs/components/Pagination/index.js +2 -0
  22. package/lib/commonjs/components/Pagination/index.js.map +1 -0
  23. package/lib/commonjs/components/ScrollViewGesture.js +2 -0
  24. package/lib/commonjs/components/ScrollViewGesture.js.map +1 -0
  25. package/lib/commonjs/constants/index.js +2 -0
  26. package/lib/commonjs/constants/index.js.map +1 -0
  27. package/lib/commonjs/hooks/useAutoPlay.js +2 -0
  28. package/lib/commonjs/hooks/useAutoPlay.js.map +1 -0
  29. package/lib/commonjs/hooks/useCarouselController.js +2 -0
  30. package/lib/commonjs/hooks/useCarouselController.js.map +1 -0
  31. package/lib/commonjs/hooks/useCheckMounted.js +2 -0
  32. package/lib/commonjs/hooks/useCheckMounted.js.map +1 -0
  33. package/lib/commonjs/hooks/useCommonVariables.js +2 -0
  34. package/lib/commonjs/hooks/useCommonVariables.js.map +1 -0
  35. package/lib/commonjs/hooks/useInitProps.js +2 -0
  36. package/lib/commonjs/hooks/useInitProps.js.map +1 -0
  37. package/lib/commonjs/hooks/useLayoutConfig.js +2 -0
  38. package/lib/commonjs/hooks/useLayoutConfig.js.map +1 -0
  39. package/lib/commonjs/hooks/useOffsetX.js +2 -0
  40. package/lib/commonjs/hooks/useOffsetX.js.map +1 -0
  41. package/lib/commonjs/hooks/useOnProgressChange.js +2 -0
  42. package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -0
  43. package/lib/commonjs/hooks/usePanGestureProxy.js +2 -0
  44. package/lib/commonjs/hooks/usePanGestureProxy.js.map +1 -0
  45. package/lib/commonjs/hooks/usePropsErrorBoundary.js +2 -0
  46. package/lib/commonjs/hooks/usePropsErrorBoundary.js.map +1 -0
  47. package/lib/commonjs/hooks/useSizeResolver.js +2 -0
  48. package/lib/commonjs/hooks/useSizeResolver.js.map +1 -0
  49. package/lib/commonjs/hooks/useUpdateGestureConfig.js +2 -0
  50. package/lib/commonjs/hooks/useUpdateGestureConfig.js.map +1 -0
  51. package/lib/commonjs/hooks/useVisibleRanges.js +2 -0
  52. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -0
  53. package/lib/commonjs/index.js +2 -0
  54. package/lib/commonjs/index.js.map +1 -0
  55. package/lib/commonjs/layouts/index.js +2 -0
  56. package/lib/commonjs/layouts/index.js.map +1 -0
  57. package/lib/commonjs/layouts/normal.js +2 -0
  58. package/lib/commonjs/layouts/normal.js.map +1 -0
  59. package/lib/commonjs/layouts/parallax.js +2 -0
  60. package/lib/commonjs/layouts/parallax.js.map +1 -0
  61. package/lib/commonjs/layouts/stack.js +2 -0
  62. package/lib/commonjs/layouts/stack.js.map +1 -0
  63. package/lib/commonjs/package.json +1 -0
  64. package/lib/commonjs/store/index.js +2 -0
  65. package/lib/commonjs/store/index.js.map +1 -0
  66. package/lib/commonjs/types.js +2 -0
  67. package/lib/commonjs/types.js.map +1 -0
  68. package/lib/commonjs/utils/compute-gesture-translation.js +2 -0
  69. package/lib/commonjs/utils/compute-gesture-translation.js.map +1 -0
  70. package/lib/commonjs/utils/compute-offset-if-data-changed.js +2 -0
  71. package/lib/commonjs/utils/compute-offset-if-data-changed.js.map +1 -0
  72. package/lib/commonjs/utils/compute-offset-if-size-changed.js +2 -0
  73. package/lib/commonjs/utils/compute-offset-if-size-changed.js.map +1 -0
  74. package/lib/commonjs/utils/compute-offset-if-sizes-changed.js +2 -0
  75. package/lib/commonjs/utils/compute-offset-if-sizes-changed.js.map +1 -0
  76. package/lib/commonjs/utils/computed-with-auto-fill-data.js +2 -0
  77. package/lib/commonjs/utils/computed-with-auto-fill-data.js.map +1 -0
  78. package/lib/commonjs/utils/deal-with-animation.js +2 -0
  79. package/lib/commonjs/utils/deal-with-animation.js.map +1 -0
  80. package/lib/commonjs/utils/handleroffset-direction.js +2 -0
  81. package/lib/commonjs/utils/handleroffset-direction.js.map +1 -0
  82. package/lib/commonjs/utils/log.js +2 -0
  83. package/lib/commonjs/utils/log.js.map +1 -0
  84. package/lib/commonjs/utils/sanitize-animation-style.js +2 -0
  85. package/lib/commonjs/utils/sanitize-animation-style.js.map +1 -0
  86. package/lib/commonjs/utils/size-resolver.js +2 -0
  87. package/lib/commonjs/utils/size-resolver.js.map +1 -0
  88. package/lib/module/components/Carousel.js +2 -0
  89. package/lib/module/components/Carousel.js.map +1 -0
  90. package/lib/module/components/CarouselLayout.js +2 -0
  91. package/lib/module/components/CarouselLayout.js.map +1 -0
  92. package/lib/module/components/ItemLayout.js +2 -0
  93. package/lib/module/components/ItemLayout.js.map +1 -0
  94. package/lib/module/components/ItemRenderer.js +2 -0
  95. package/lib/module/components/ItemRenderer.js.map +1 -0
  96. package/lib/module/components/LazyView.js +2 -0
  97. package/lib/module/components/LazyView.js.map +1 -0
  98. package/lib/module/components/Pagination/Basic/PaginationItem.js +2 -0
  99. package/lib/module/components/Pagination/Basic/PaginationItem.js.map +1 -0
  100. package/lib/module/components/Pagination/Basic/index.js +2 -0
  101. package/lib/module/components/Pagination/Basic/index.js.map +1 -0
  102. package/lib/module/components/Pagination/Custom/PaginationItem.js +2 -0
  103. package/lib/module/components/Pagination/Custom/PaginationItem.js.map +1 -0
  104. package/lib/module/components/Pagination/Custom/index.js +2 -0
  105. package/lib/module/components/Pagination/Custom/index.js.map +1 -0
  106. package/lib/module/components/Pagination/index.js +2 -0
  107. package/lib/module/components/Pagination/index.js.map +1 -0
  108. package/lib/module/components/ScrollViewGesture.js +2 -0
  109. package/lib/module/components/ScrollViewGesture.js.map +1 -0
  110. package/lib/module/constants/index.js +2 -0
  111. package/lib/module/constants/index.js.map +1 -0
  112. package/lib/module/hooks/useAutoPlay.js +2 -0
  113. package/lib/module/hooks/useAutoPlay.js.map +1 -0
  114. package/lib/module/hooks/useCarouselController.js +2 -0
  115. package/lib/module/hooks/useCarouselController.js.map +1 -0
  116. package/lib/module/hooks/useCheckMounted.js +2 -0
  117. package/lib/module/hooks/useCheckMounted.js.map +1 -0
  118. package/lib/module/hooks/useCommonVariables.js +2 -0
  119. package/lib/module/hooks/useCommonVariables.js.map +1 -0
  120. package/lib/module/hooks/useInitProps.js +2 -0
  121. package/lib/module/hooks/useInitProps.js.map +1 -0
  122. package/lib/module/hooks/useLayoutConfig.js +2 -0
  123. package/lib/module/hooks/useLayoutConfig.js.map +1 -0
  124. package/lib/module/hooks/useOffsetX.js +2 -0
  125. package/lib/module/hooks/useOffsetX.js.map +1 -0
  126. package/lib/module/hooks/useOnProgressChange.js +2 -0
  127. package/lib/module/hooks/useOnProgressChange.js.map +1 -0
  128. package/lib/module/hooks/usePanGestureProxy.js +2 -0
  129. package/lib/module/hooks/usePanGestureProxy.js.map +1 -0
  130. package/lib/module/hooks/usePropsErrorBoundary.js +2 -0
  131. package/lib/module/hooks/usePropsErrorBoundary.js.map +1 -0
  132. package/lib/module/hooks/useSizeResolver.js +2 -0
  133. package/lib/module/hooks/useSizeResolver.js.map +1 -0
  134. package/lib/module/hooks/useUpdateGestureConfig.js +2 -0
  135. package/lib/module/hooks/useUpdateGestureConfig.js.map +1 -0
  136. package/lib/module/hooks/useVisibleRanges.js +2 -0
  137. package/lib/module/hooks/useVisibleRanges.js.map +1 -0
  138. package/lib/module/index.js +2 -0
  139. package/lib/module/index.js.map +1 -0
  140. package/lib/module/layouts/index.js +2 -0
  141. package/lib/module/layouts/index.js.map +1 -0
  142. package/lib/module/layouts/normal.js +2 -0
  143. package/lib/module/layouts/normal.js.map +1 -0
  144. package/lib/module/layouts/parallax.js +2 -0
  145. package/lib/module/layouts/parallax.js.map +1 -0
  146. package/lib/module/layouts/stack.js +2 -0
  147. package/lib/module/layouts/stack.js.map +1 -0
  148. package/lib/module/store/index.js +2 -0
  149. package/lib/module/store/index.js.map +1 -0
  150. package/lib/module/types.js +2 -0
  151. package/lib/module/types.js.map +1 -0
  152. package/lib/module/utils/compute-gesture-translation.js +2 -0
  153. package/lib/module/utils/compute-gesture-translation.js.map +1 -0
  154. package/lib/module/utils/compute-offset-if-data-changed.js +2 -0
  155. package/lib/module/utils/compute-offset-if-data-changed.js.map +1 -0
  156. package/lib/module/utils/compute-offset-if-size-changed.js +2 -0
  157. package/lib/module/utils/compute-offset-if-size-changed.js.map +1 -0
  158. package/lib/module/utils/compute-offset-if-sizes-changed.js +2 -0
  159. package/lib/module/utils/compute-offset-if-sizes-changed.js.map +1 -0
  160. package/lib/module/utils/computed-with-auto-fill-data.js +2 -0
  161. package/lib/module/utils/computed-with-auto-fill-data.js.map +1 -0
  162. package/lib/module/utils/deal-with-animation.js +2 -0
  163. package/lib/module/utils/deal-with-animation.js.map +1 -0
  164. package/lib/module/utils/handleroffset-direction.js +2 -0
  165. package/lib/module/utils/handleroffset-direction.js.map +1 -0
  166. package/lib/module/utils/log.js +2 -0
  167. package/lib/module/utils/log.js.map +1 -0
  168. package/lib/module/utils/sanitize-animation-style.js +2 -0
  169. package/lib/module/utils/sanitize-animation-style.js.map +1 -0
  170. package/lib/module/utils/size-resolver.js +2 -0
  171. package/lib/module/utils/size-resolver.js.map +1 -0
  172. package/lib/typescript/components/Carousel.d.ts +8 -0
  173. package/lib/typescript/components/Carousel.d.ts.map +1 -0
  174. package/lib/typescript/components/CarouselLayout.d.ts +6 -0
  175. package/lib/typescript/components/CarouselLayout.d.ts.map +1 -0
  176. package/lib/typescript/components/ItemLayout.d.ts +15 -0
  177. package/lib/typescript/components/ItemLayout.d.ts.map +1 -0
  178. package/lib/typescript/components/ItemRenderer.d.ts +24 -0
  179. package/lib/typescript/components/ItemRenderer.d.ts.map +1 -0
  180. package/lib/typescript/components/LazyView.d.ts +8 -0
  181. package/lib/typescript/components/LazyView.d.ts.map +1 -0
  182. package/lib/typescript/components/Pagination/Basic/PaginationItem.d.ts +29 -0
  183. package/lib/typescript/components/Pagination/Basic/PaginationItem.d.ts.map +1 -0
  184. package/lib/typescript/components/Pagination/Basic/index.d.ts +23 -0
  185. package/lib/typescript/components/Pagination/Basic/index.d.ts.map +1 -0
  186. package/lib/typescript/components/Pagination/Custom/PaginationItem.d.ts +35 -0
  187. package/lib/typescript/components/Pagination/Custom/PaginationItem.d.ts.map +1 -0
  188. package/lib/typescript/components/Pagination/Custom/index.d.ts +26 -0
  189. package/lib/typescript/components/Pagination/Custom/index.d.ts.map +1 -0
  190. package/lib/typescript/components/Pagination/index.d.ts +5 -0
  191. package/lib/typescript/components/Pagination/index.d.ts.map +1 -0
  192. package/lib/typescript/components/ScrollViewGesture.d.ts +19 -0
  193. package/lib/typescript/components/ScrollViewGesture.d.ts.map +1 -0
  194. package/lib/typescript/constants/index.d.ts +9 -0
  195. package/lib/typescript/constants/index.d.ts.map +1 -0
  196. package/lib/typescript/hooks/useAutoPlay.d.ts +12 -0
  197. package/lib/typescript/hooks/useAutoPlay.d.ts.map +1 -0
  198. package/lib/typescript/hooks/useCarouselController.d.ts +28 -0
  199. package/lib/typescript/hooks/useCarouselController.d.ts.map +1 -0
  200. package/lib/typescript/hooks/useCheckMounted.d.ts +3 -0
  201. package/lib/typescript/hooks/useCheckMounted.d.ts.map +1 -0
  202. package/lib/typescript/hooks/useCommonVariables.d.ts +13 -0
  203. package/lib/typescript/hooks/useCommonVariables.d.ts.map +1 -0
  204. package/lib/typescript/hooks/useInitProps.d.ts +17 -0
  205. package/lib/typescript/hooks/useInitProps.d.ts.map +1 -0
  206. package/lib/typescript/hooks/useLayoutConfig.d.ts +10 -0
  207. package/lib/typescript/hooks/useLayoutConfig.d.ts.map +1 -0
  208. package/lib/typescript/hooks/useOffsetX.d.ts +24 -0
  209. package/lib/typescript/hooks/useOffsetX.d.ts.map +1 -0
  210. package/lib/typescript/hooks/useOnProgressChange.d.ts +14 -0
  211. package/lib/typescript/hooks/useOnProgressChange.d.ts.map +1 -0
  212. package/lib/typescript/hooks/usePanGestureProxy.d.ts +10 -0
  213. package/lib/typescript/hooks/usePanGestureProxy.d.ts.map +1 -0
  214. package/lib/typescript/hooks/usePropsErrorBoundary.d.ts +5 -0
  215. package/lib/typescript/hooks/usePropsErrorBoundary.d.ts.map +1 -0
  216. package/lib/typescript/hooks/useSizeResolver.d.ts +27 -0
  217. package/lib/typescript/hooks/useSizeResolver.d.ts.map +1 -0
  218. package/lib/typescript/hooks/useUpdateGestureConfig.d.ts +6 -0
  219. package/lib/typescript/hooks/useUpdateGestureConfig.d.ts.map +1 -0
  220. package/lib/typescript/hooks/useVisibleRanges.d.ts +23 -0
  221. package/lib/typescript/hooks/useVisibleRanges.d.ts.map +1 -0
  222. package/lib/typescript/index.d.ts +7 -0
  223. package/lib/typescript/index.d.ts.map +1 -0
  224. package/lib/typescript/layouts/index.d.ts +11 -0
  225. package/lib/typescript/layouts/index.d.ts.map +1 -0
  226. package/lib/typescript/layouts/normal.d.ts +16 -0
  227. package/lib/typescript/layouts/normal.d.ts.map +1 -0
  228. package/lib/typescript/layouts/parallax.d.ts +50 -0
  229. package/lib/typescript/layouts/parallax.d.ts.map +1 -0
  230. package/lib/typescript/layouts/stack.d.ts +38 -0
  231. package/lib/typescript/layouts/stack.d.ts.map +1 -0
  232. package/lib/typescript/store/index.d.ts +38 -0
  233. package/lib/typescript/store/index.d.ts.map +1 -0
  234. package/lib/typescript/types.d.ts +326 -0
  235. package/lib/typescript/types.d.ts.map +1 -0
  236. package/lib/typescript/utils/compute-gesture-translation.d.ts +9 -0
  237. package/lib/typescript/utils/compute-gesture-translation.d.ts.map +1 -0
  238. package/lib/typescript/utils/compute-offset-if-data-changed.d.ts +9 -0
  239. package/lib/typescript/utils/compute-offset-if-data-changed.d.ts.map +1 -0
  240. package/lib/typescript/utils/compute-offset-if-size-changed.d.ts +6 -0
  241. package/lib/typescript/utils/compute-offset-if-size-changed.d.ts.map +1 -0
  242. package/lib/typescript/utils/compute-offset-if-sizes-changed.d.ts +14 -0
  243. package/lib/typescript/utils/compute-offset-if-sizes-changed.d.ts.map +1 -0
  244. package/lib/typescript/utils/computed-with-auto-fill-data.d.ts +23 -0
  245. package/lib/typescript/utils/computed-with-auto-fill-data.d.ts.map +1 -0
  246. package/lib/typescript/utils/deal-with-animation.d.ts +3 -0
  247. package/lib/typescript/utils/deal-with-animation.d.ts.map +1 -0
  248. package/lib/typescript/utils/handleroffset-direction.d.ts +4 -0
  249. package/lib/typescript/utils/handleroffset-direction.d.ts.map +1 -0
  250. package/lib/typescript/utils/log.d.ts +7 -0
  251. package/lib/typescript/utils/log.d.ts.map +1 -0
  252. package/lib/typescript/utils/sanitize-animation-style.d.ts +3 -0
  253. package/lib/typescript/utils/sanitize-animation-style.d.ts.map +1 -0
  254. package/lib/typescript/utils/size-resolver.d.ts +87 -0
  255. package/lib/typescript/utils/size-resolver.d.ts.map +1 -0
  256. package/package.json +151 -0
  257. package/src/components/Carousel.test.tsx +1153 -0
  258. package/src/components/Carousel.tsx +35 -0
  259. package/src/components/CarouselLayout.tsx +231 -0
  260. package/src/components/ItemLayout.tsx +217 -0
  261. package/src/components/ItemRenderer.tsx +114 -0
  262. package/src/components/LazyView.test.tsx +61 -0
  263. package/src/components/LazyView.tsx +14 -0
  264. package/src/components/Pagination/Basic/PaginationItem.tsx +149 -0
  265. package/src/components/Pagination/Basic/index.tsx +98 -0
  266. package/src/components/Pagination/Custom/PaginationItem.tsx +166 -0
  267. package/src/components/Pagination/Custom/index.tsx +111 -0
  268. package/src/components/Pagination/Pagination.test.tsx +178 -0
  269. package/src/components/Pagination/index.tsx +7 -0
  270. package/src/components/ScrollViewGesture.tsx +577 -0
  271. package/src/components/rnr-demo.test.tsx +53 -0
  272. package/src/constants/index.ts +11 -0
  273. package/src/hooks/useAutoPlay.test.ts +194 -0
  274. package/src/hooks/useAutoPlay.ts +58 -0
  275. package/src/hooks/useCarouselController.test.tsx +1158 -0
  276. package/src/hooks/useCarouselController.tsx +525 -0
  277. package/src/hooks/useCheckMounted.test.ts +47 -0
  278. package/src/hooks/useCheckMounted.ts +14 -0
  279. package/src/hooks/useCommonVariables.test.tsx +384 -0
  280. package/src/hooks/useCommonVariables.ts +202 -0
  281. package/src/hooks/useInitProps.test.tsx +134 -0
  282. package/src/hooks/useInitProps.ts +111 -0
  283. package/src/hooks/useLayoutConfig.test.tsx +247 -0
  284. package/src/hooks/useLayoutConfig.ts +30 -0
  285. package/src/hooks/useOffsetX.test.ts +110 -0
  286. package/src/hooks/useOffsetX.ts +109 -0
  287. package/src/hooks/useOnProgressChange.test.tsx +207 -0
  288. package/src/hooks/useOnProgressChange.ts +105 -0
  289. package/src/hooks/usePanGestureProxy.test.tsx +368 -0
  290. package/src/hooks/usePanGestureProxy.ts +144 -0
  291. package/src/hooks/usePropsErrorBoundary.ts +138 -0
  292. package/src/hooks/useSizeResolver.test.tsx +112 -0
  293. package/src/hooks/useSizeResolver.ts +106 -0
  294. package/src/hooks/useUpdateGestureConfig.test.ts +89 -0
  295. package/src/hooks/useUpdateGestureConfig.ts +14 -0
  296. package/src/hooks/useVisibleRanges.test.tsx +366 -0
  297. package/src/hooks/useVisibleRanges.tsx +123 -0
  298. package/src/index.tsx +13 -0
  299. package/src/layouts/index.tsx +12 -0
  300. package/src/layouts/normal.ts +32 -0
  301. package/src/layouts/parallax.test.ts +239 -0
  302. package/src/layouts/parallax.ts +83 -0
  303. package/src/layouts/stack.test.ts +252 -0
  304. package/src/layouts/stack.ts +306 -0
  305. package/src/store/index.test.tsx +314 -0
  306. package/src/store/index.tsx +66 -0
  307. package/src/types.ts +348 -0
  308. package/src/utils/compute-gesture-translation.test.ts +70 -0
  309. package/src/utils/compute-gesture-translation.ts +29 -0
  310. package/src/utils/compute-offset-if-data-changed.test.ts +133 -0
  311. package/src/utils/compute-offset-if-data-changed.ts +44 -0
  312. package/src/utils/compute-offset-if-size-changed.test.ts +78 -0
  313. package/src/utils/compute-offset-if-size-changed.ts +14 -0
  314. package/src/utils/compute-offset-if-sizes-changed.test.ts +74 -0
  315. package/src/utils/compute-offset-if-sizes-changed.ts +44 -0
  316. package/src/utils/computed-with-auto-fill-data.test.ts +298 -0
  317. package/src/utils/computed-with-auto-fill-data.ts +92 -0
  318. package/src/utils/deal-with-animation.test.ts +181 -0
  319. package/src/utils/deal-with-animation.ts +17 -0
  320. package/src/utils/handleroffset-direction.test.ts +124 -0
  321. package/src/utils/handleroffset-direction.ts +18 -0
  322. package/src/utils/index.test.ts +90 -0
  323. package/src/utils/log.test.ts +134 -0
  324. package/src/utils/log.ts +12 -0
  325. package/src/utils/sanitize-animation-style.test.ts +40 -0
  326. package/src/utils/sanitize-animation-style.ts +20 -0
  327. package/src/utils/size-resolver.test.ts +193 -0
  328. package/src/utils/size-resolver.ts +216 -0
@@ -0,0 +1,577 @@
1
+ import type { PropsWithChildren } from "react";
2
+ import React, { useCallback } from "react";
3
+ import type { LayoutChangeEvent, StyleProp, ViewStyle } from "react-native";
4
+ import type {
5
+ GestureStateChangeEvent,
6
+ PanGestureHandlerEventPayload,
7
+ } from "react-native-gesture-handler";
8
+ import { GestureDetector } from "react-native-gesture-handler";
9
+ import Animated, {
10
+ cancelAnimation,
11
+ measure,
12
+ useAnimatedReaction,
13
+ useAnimatedRef,
14
+ useDerivedValue,
15
+ useSharedValue,
16
+ withDecay,
17
+ } from "react-native-reanimated";
18
+ import type { SharedValue } from "react-native-reanimated";
19
+ import { scheduleOnRN } from "react-native-worklets";
20
+
21
+ import { Easing } from "../constants";
22
+ import { usePanGestureProxy } from "../hooks/usePanGestureProxy";
23
+ import { useGlobalState } from "../store";
24
+ import type { WithTimingAnimation } from "../types";
25
+ import { computeGestureTranslation } from "../utils/compute-gesture-translation";
26
+ import { dealWithAnimation } from "../utils/deal-with-animation";
27
+
28
+ interface Props {
29
+ size: number;
30
+ infinite?: boolean;
31
+ testID?: string;
32
+ style?: StyleProp<ViewStyle>;
33
+ translation: SharedValue<number>;
34
+ onLayout?: (e: LayoutChangeEvent) => void;
35
+ onScrollStart?: () => void;
36
+ onScrollEnd?: () => void;
37
+ onTouchBegin?: () => void;
38
+ onTouchEnd?: () => void;
39
+ }
40
+
41
+ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
42
+ const {
43
+ props: {
44
+ onConfigurePanGesture,
45
+ vertical,
46
+ pagingEnabled,
47
+ snapEnabled,
48
+ loop,
49
+ scrollAnimationDuration,
50
+ withAnimation,
51
+ enabled,
52
+ dataLength,
53
+ overscrollEnabled,
54
+ maxScrollDistancePerSwipe,
55
+ minScrollDistancePerSwipe,
56
+ fixedDirection,
57
+ variableSize,
58
+ },
59
+ common: { size, resolvedSize, sizePhase, sizeExplicit },
60
+ sizeResolver,
61
+ layout: { updateContainerSize },
62
+ } = useGlobalState();
63
+
64
+ const {
65
+ translation,
66
+ testID,
67
+ style = {},
68
+ onScrollStart,
69
+ onScrollEnd,
70
+ onTouchBegin,
71
+ onTouchEnd,
72
+ } = props;
73
+
74
+ const maxPage = dataLength;
75
+ const isHorizontal = useDerivedValue(() => !vertical, [vertical]);
76
+ const max = useSharedValue(0);
77
+ const panOffset = useSharedValue<number | undefined>(undefined); // set to undefined when not actively in a pan gesture
78
+ const touching = useSharedValue(false);
79
+ const validStart = useSharedValue(false);
80
+ const scrollEndTranslation = useSharedValue(0);
81
+ const scrollEndVelocity = useSharedValue(0);
82
+ const containerRef = useAnimatedRef<Animated.View>();
83
+ const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number";
84
+ const minScrollDistancePerSwipeIsSet = typeof minScrollDistancePerSwipe === "number";
85
+ const sizeReady = useDerivedValue(() => {
86
+ const currentSize = resolvedSize.value ?? 0;
87
+ return sizePhase.value === "ready" && currentSize > 0;
88
+ }, [resolvedSize, sizePhase]);
89
+
90
+ // Get the limit of the scroll.
91
+ const getLimit = React.useCallback(() => {
92
+ "worklet";
93
+
94
+ const totalSpan = variableSize ? sizeResolver.total.value : dataLength * size;
95
+ if (totalSpan <= 0) return 0;
96
+
97
+ if (!loop && !overscrollEnabled) {
98
+ const measurement = measure(containerRef);
99
+ const containerWidth = (vertical ? measurement?.height : measurement?.width) || 0;
100
+
101
+ // If the total span is less than the container's width, then there is no
102
+ // need to scroll.
103
+ if (totalSpan < containerWidth) return 0;
104
+
105
+ // Disable the "overscroll" effect
106
+ return totalSpan - containerWidth;
107
+ }
108
+
109
+ return totalSpan;
110
+ }, [loop, size, dataLength, overscrollEnabled, variableSize, sizeResolver, vertical]);
111
+
112
+ const withSpring = React.useCallback(
113
+ (toValue: number, onFinished?: () => void) => {
114
+ "worklet";
115
+ const defaultWithAnimation: WithTimingAnimation = {
116
+ type: "timing",
117
+ config: {
118
+ duration: scrollAnimationDuration + 100,
119
+ easing: Easing.easeOutQuart,
120
+ },
121
+ };
122
+
123
+ return dealWithAnimation(withAnimation ?? defaultWithAnimation)(
124
+ toValue,
125
+ (isFinished: boolean) => {
126
+ "worklet";
127
+ if (isFinished) onFinished && scheduleOnRN(onFinished);
128
+ }
129
+ );
130
+ },
131
+ [scrollAnimationDuration, withAnimation]
132
+ );
133
+
134
+ const endWithSpring = React.useCallback(
135
+ (
136
+ scrollEndTranslationValue: number,
137
+ scrollEndVelocityValue: number,
138
+ onFinished?: () => void
139
+ ) => {
140
+ "worklet";
141
+ // Use resolvedSize.value (SharedValue) instead of size (React state)
142
+ // to avoid race condition where sizeReady is true but size is still 0
143
+ const currentSize = resolvedSize.value ?? 0;
144
+ const totalSpan = variableSize ? sizeResolver.total.value : 0;
145
+ if (variableSize ? totalSpan <= 0 : currentSize <= 0) {
146
+ return;
147
+ }
148
+ const origin = translation.value;
149
+ const velocity = scrollEndVelocityValue;
150
+ // Default to scroll in the direction of the slide (with deceleration)
151
+ let finalTranslation: number = withDecay({ velocity, deceleration: 0.999 });
152
+
153
+ // Worklet-safe helpers that translate between scroll offsets and item
154
+ // indices. In uniform mode they reduce to the original `offset / size`
155
+ // math; in variable mode they consult the prefix-sum table.
156
+ const indexForOffset = (offset: number): number => {
157
+ if (variableSize) return sizeResolver.indexAt(offset);
158
+ return Math.round(offset / currentSize);
159
+ };
160
+ const offsetForIndex = (index: number): number => {
161
+ if (variableSize) return sizeResolver.offsetAt(index);
162
+ return index * currentSize;
163
+ };
164
+
165
+ // If the distance of the swipe exceeds the max scroll distance, keep the view at the current position
166
+ if (
167
+ maxScrollDistancePerSwipeIsSet &&
168
+ Math.abs(scrollEndTranslationValue) > maxScrollDistancePerSwipe
169
+ ) {
170
+ finalTranslation = origin;
171
+ } else {
172
+ // The final pan position plus a small velocity contribution determines
173
+ // which item we'd naturally land on. `nextPage` is that item's index.
174
+ const nextPage = indexForOffset(-(origin + velocity * 2));
175
+
176
+ if (pagingEnabled) {
177
+ // we'll never go further than a single page away from the current page when paging
178
+ // is enabled.
179
+
180
+ // distance with direction
181
+ const offset = -(scrollEndTranslationValue >= 0 ? 1 : -1); // 1 or -1
182
+ const page = variableSize
183
+ ? sizeResolver.indexAt(-origin)
184
+ : (offset < 0 ? Math.ceil : Math.floor)(-origin / currentSize);
185
+
186
+ const velocityDirection = -Math.sign(velocity);
187
+ if (page === nextPage || velocityDirection !== offset) {
188
+ // not going anywhere! Velocity was insufficient to overcome the distance to get to a
189
+ // further page. Let's reset gently to the current page.
190
+ finalTranslation = withSpring(
191
+ withProcessTranslation(-offsetForIndex(page)),
192
+ onFinished
193
+ );
194
+ } else if (loop) {
195
+ const finalPage = page + offset;
196
+ finalTranslation = withSpring(
197
+ withProcessTranslation(-offsetForIndex(finalPage)),
198
+ onFinished
199
+ );
200
+ } else {
201
+ const finalPage = Math.min(maxPage - 1, Math.max(0, page + offset));
202
+ finalTranslation = withSpring(
203
+ withProcessTranslation(-offsetForIndex(finalPage)),
204
+ onFinished
205
+ );
206
+ }
207
+ }
208
+
209
+ if (!pagingEnabled && snapEnabled) {
210
+ // scroll to the nearest item
211
+ finalTranslation = withSpring(
212
+ withProcessTranslation(-offsetForIndex(nextPage)),
213
+ onFinished
214
+ );
215
+ }
216
+ }
217
+
218
+ translation.value = finalTranslation;
219
+
220
+ function withProcessTranslation(translation: number) {
221
+ if (!loop && !overscrollEnabled) {
222
+ const limit = getLimit();
223
+ return Math.min(0, Math.max(-limit, translation));
224
+ }
225
+
226
+ return translation;
227
+ }
228
+ },
229
+ [
230
+ withSpring,
231
+ resolvedSize,
232
+ maxPage,
233
+ loop,
234
+ snapEnabled,
235
+ translation,
236
+ pagingEnabled,
237
+ maxScrollDistancePerSwipe,
238
+ maxScrollDistancePerSwipeIsSet,
239
+ variableSize,
240
+ sizeResolver,
241
+ overscrollEnabled,
242
+ getLimit,
243
+ ]
244
+ );
245
+
246
+ const onFinish = React.useCallback(
247
+ (isFinished: boolean) => {
248
+ "worklet";
249
+ if (isFinished) {
250
+ touching.value = false;
251
+ onScrollEnd && scheduleOnRN(onScrollEnd);
252
+ }
253
+ },
254
+ [onScrollEnd, touching]
255
+ );
256
+
257
+ const activeDecay = React.useCallback(() => {
258
+ "worklet";
259
+ touching.value = true;
260
+ translation.value = withDecay({ velocity: scrollEndVelocity.value }, (isFinished) =>
261
+ onFinish(isFinished as boolean)
262
+ );
263
+ }, [onFinish, scrollEndVelocity, touching, translation]);
264
+
265
+ const resetBoundary = React.useCallback(() => {
266
+ "worklet";
267
+ // Use resolvedSize.value (SharedValue) instead of size (React state)
268
+ // to avoid race condition where sizeReady is true but size is still 0
269
+ const currentSize = resolvedSize.value ?? 0;
270
+ const totalSpan = variableSize ? sizeResolver.total.value : 0;
271
+ if (variableSize ? totalSpan <= 0 : currentSize <= 0) return;
272
+ if (touching.value) return;
273
+
274
+ // Maximum negative translation: the start offset of the last item
275
+ // (so the last item sits at the left edge of the viewport).
276
+ const lastItemStart = variableSize
277
+ ? sizeResolver.offsetAt(maxPage - 1)
278
+ : (maxPage - 1) * currentSize;
279
+
280
+ if (translation.value > 0) {
281
+ if (scrollEndTranslation.value < 0) {
282
+ activeDecay();
283
+ return;
284
+ }
285
+ if (!loop) {
286
+ translation.value = withSpring(0);
287
+ return;
288
+ }
289
+ }
290
+
291
+ if (translation.value < -lastItemStart) {
292
+ if (scrollEndTranslation.value > 0) {
293
+ activeDecay();
294
+ return;
295
+ }
296
+ if (!loop) translation.value = withSpring(-lastItemStart);
297
+ }
298
+ }, [
299
+ touching,
300
+ translation,
301
+ maxPage,
302
+ resolvedSize,
303
+ scrollEndTranslation,
304
+ loop,
305
+ activeDecay,
306
+ withSpring,
307
+ variableSize,
308
+ sizeResolver,
309
+ ]);
310
+
311
+ useAnimatedReaction(
312
+ () => translation.value,
313
+ () => {
314
+ if (!pagingEnabled) resetBoundary();
315
+ },
316
+ [pagingEnabled, resetBoundary]
317
+ );
318
+
319
+ function withProcessTranslation(translation: number) {
320
+ "worklet";
321
+
322
+ if (!loop && !overscrollEnabled) {
323
+ const limit = getLimit();
324
+ return Math.min(0, Math.max(-limit, translation));
325
+ }
326
+
327
+ return translation;
328
+ }
329
+
330
+ const onGestureStart = useCallback(
331
+ (_: PanGestureHandlerEventPayload) => {
332
+ "worklet";
333
+ // Use resolvedSize.value (SharedValue) instead of size (React state)
334
+ // to avoid race condition where sizeReady is true but size is still 0
335
+ const currentSize = resolvedSize.value ?? 0;
336
+ const totalSpan = variableSize ? sizeResolver.total.value : 0;
337
+ const ready = variableSize ? totalSpan > 0 : sizeReady.value && currentSize > 0;
338
+ if (!ready) {
339
+ return;
340
+ }
341
+ touching.value = true;
342
+ validStart.value = true;
343
+ onScrollStart && scheduleOnRN(onScrollStart);
344
+
345
+ max.value = variableSize ? sizeResolver.offsetAt(maxPage - 1) : (maxPage - 1) * currentSize;
346
+ if (!loop && !overscrollEnabled) max.value = getLimit();
347
+
348
+ panOffset.value = translation.value;
349
+ },
350
+ [
351
+ max,
352
+ maxPage,
353
+ loop,
354
+ touching,
355
+ panOffset,
356
+ validStart,
357
+ translation,
358
+ overscrollEnabled,
359
+ getLimit,
360
+ onScrollStart,
361
+ sizeReady,
362
+ resolvedSize,
363
+ variableSize,
364
+ sizeResolver,
365
+ ]
366
+ );
367
+
368
+ const onGestureUpdate = useCallback(
369
+ (e: PanGestureHandlerEventPayload) => {
370
+ "worklet";
371
+ const currentSize = resolvedSize.value ?? 0;
372
+ const totalSpan = variableSize ? sizeResolver.total.value : 0;
373
+ const ready = variableSize ? totalSpan > 0 : sizeReady.value && currentSize > 0;
374
+ if (!ready) {
375
+ return;
376
+ }
377
+ if (panOffset.value === undefined) {
378
+ // This may happen if `onGestureStart` is called as a part of the
379
+ // JS thread (instead of the UI thread / worklet). If so, when
380
+ // `onGestureStart` sets panOffset.value, the set will be asynchronous,
381
+ // and so it may not actually occur before `onGestureUpdate` is called.
382
+ //
383
+ // Keeping this value as `undefined` when it is not active protects us
384
+ // from the situation where we may use the previous value for panOffset
385
+ // instead; this would cause a visual flicker in the carousel.
386
+
387
+ // console.warn("onGestureUpdate: panOffset is undefined");
388
+ return;
389
+ }
390
+
391
+ if (validStart.value) {
392
+ validStart.value = false;
393
+ cancelAnimation(translation);
394
+ }
395
+ touching.value = true;
396
+ const { translationX, translationY } = e;
397
+
398
+ let panTranslation = isHorizontal.value ? translationX : translationY;
399
+
400
+ if (fixedDirection === "negative") panTranslation = -Math.abs(panTranslation);
401
+ else if (fixedDirection === "positive") panTranslation = +Math.abs(panTranslation);
402
+
403
+ translation.value = computeGestureTranslation({
404
+ loop,
405
+ overscrollEnabled: overscrollEnabled ?? true,
406
+ currentTranslation: translation.value,
407
+ panOffset: panOffset.value,
408
+ panTranslation,
409
+ max: max.value,
410
+ });
411
+ },
412
+ [
413
+ isHorizontal,
414
+ max,
415
+ panOffset,
416
+ loop,
417
+ overscrollEnabled,
418
+ fixedDirection,
419
+ translation,
420
+ validStart,
421
+ touching,
422
+ sizeReady,
423
+ resolvedSize,
424
+ variableSize,
425
+ sizeResolver,
426
+ ]
427
+ );
428
+
429
+ const onGestureEnd = useCallback(
430
+ (e: GestureStateChangeEvent<PanGestureHandlerEventPayload>, _success: boolean) => {
431
+ "worklet";
432
+ // Use resolvedSize.value (SharedValue) instead of size (React state)
433
+ // to avoid race condition where sizeReady is true but size is still 0
434
+ const currentSize = resolvedSize.value ?? 0;
435
+ const totalSpan = variableSize ? sizeResolver.total.value : 0;
436
+ const ready = variableSize ? totalSpan > 0 : sizeReady.value && currentSize > 0;
437
+ if (!ready) {
438
+ panOffset.value = undefined;
439
+ return;
440
+ }
441
+
442
+ if (panOffset.value === undefined) {
443
+ // console.warn("onGestureEnd: panOffset is undefined");
444
+ return;
445
+ }
446
+
447
+ const { velocityX, velocityY, translationX, translationY } = e;
448
+ const scrollEndVelocityValue = isHorizontal.value ? velocityX : velocityY;
449
+ scrollEndVelocity.value = scrollEndVelocityValue; // may update async: see https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue#remarks
450
+
451
+ let panTranslation = isHorizontal.value ? translationX : translationY;
452
+
453
+ if (fixedDirection === "negative") panTranslation = -Math.abs(panTranslation);
454
+ else if (fixedDirection === "positive") panTranslation = +Math.abs(panTranslation);
455
+
456
+ scrollEndTranslation.value = panTranslation; // may update async: see https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue#remarks
457
+
458
+ const totalTranslation = scrollEndVelocityValue + panTranslation;
459
+
460
+ // Snap to the item-aligned target nearest a particular pixel offset.
461
+ // In uniform mode this is `round(target / size) * size`; in variable
462
+ // mode it's `offsetAt(indexAt(target))` (already item-aligned).
463
+ const snapToItemAlignedOffset = (rawTarget: number): number => {
464
+ if (variableSize) {
465
+ const idx = sizeResolver.indexAt(-rawTarget);
466
+ return -sizeResolver.offsetAt(idx);
467
+ }
468
+ return Math.round(rawTarget / currentSize) * currentSize;
469
+ };
470
+
471
+ /**
472
+ * If the maximum scroll distance is set and the translation `exceeds the maximum scroll distance`,
473
+ * the carousel will keep the view at the current position.
474
+ */
475
+ if (
476
+ maxScrollDistancePerSwipeIsSet &&
477
+ Math.abs(totalTranslation) > maxScrollDistancePerSwipe
478
+ ) {
479
+ const target = panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation);
480
+ translation.value = withSpring(
481
+ withProcessTranslation(snapToItemAlignedOffset(target)),
482
+ onScrollEnd
483
+ );
484
+ } else if (
485
+ /**
486
+ * If the minimum scroll distance is set and the translation `didn't exceeds the minimum scroll distance`,
487
+ * the carousel will keep the view at the current position.
488
+ */
489
+ minScrollDistancePerSwipeIsSet &&
490
+ Math.abs(totalTranslation) < minScrollDistancePerSwipe
491
+ ) {
492
+ const target = panOffset.value + minScrollDistancePerSwipe * Math.sign(totalTranslation);
493
+ translation.value = withSpring(
494
+ withProcessTranslation(snapToItemAlignedOffset(target)),
495
+ onScrollEnd
496
+ );
497
+ } else {
498
+ endWithSpring(panTranslation, scrollEndVelocityValue, onScrollEnd);
499
+ }
500
+
501
+ if (!loop) touching.value = false;
502
+
503
+ panOffset.value = undefined;
504
+ },
505
+ [
506
+ loop,
507
+ touching,
508
+ panOffset,
509
+ translation,
510
+ isHorizontal,
511
+ scrollEndVelocity,
512
+ scrollEndTranslation,
513
+ fixedDirection,
514
+ maxScrollDistancePerSwipeIsSet,
515
+ maxScrollDistancePerSwipe,
516
+ minScrollDistancePerSwipeIsSet,
517
+ minScrollDistancePerSwipe,
518
+ endWithSpring,
519
+ withSpring,
520
+ onScrollEnd,
521
+ sizeReady,
522
+ resolvedSize,
523
+ variableSize,
524
+ sizeResolver,
525
+ ]
526
+ );
527
+
528
+ const gesture = usePanGestureProxy({
529
+ onConfigurePanGesture,
530
+ onGestureStart,
531
+ onGestureUpdate,
532
+ onGestureEnd,
533
+ options: { enabled },
534
+ });
535
+
536
+ const onLayout = React.useCallback(
537
+ (e: LayoutChangeEvent) => {
538
+ "worklet";
539
+
540
+ const measuredWidth = e.nativeEvent.layout.width;
541
+ const measuredHeight = e.nativeEvent.layout.height;
542
+ const measuredSize = Math.round((vertical ? measuredHeight : measuredWidth) || 0);
543
+
544
+ if (!sizeExplicit && measuredSize > 0) {
545
+ const current = resolvedSize.value ?? 0;
546
+ if (Math.abs(current - measuredSize) > 0) {
547
+ sizePhase.value = current > 0 ? "updating" : sizePhase.value;
548
+ resolvedSize.value = measuredSize;
549
+ sizePhase.value = "ready";
550
+ }
551
+ }
552
+
553
+ updateContainerSize({
554
+ width: measuredWidth,
555
+ height: measuredHeight,
556
+ });
557
+ },
558
+ [updateContainerSize, resolvedSize, sizePhase, vertical, sizeExplicit]
559
+ );
560
+
561
+ return (
562
+ <GestureDetector gesture={gesture}>
563
+ <Animated.View
564
+ ref={containerRef}
565
+ testID={testID}
566
+ style={style}
567
+ onTouchStart={onTouchBegin}
568
+ onTouchEnd={onTouchEnd}
569
+ onLayout={onLayout}
570
+ >
571
+ {props.children}
572
+ </Animated.View>
573
+ </GestureDetector>
574
+ );
575
+ };
576
+
577
+ export const ScrollViewGesture = IScrollViewGesture;
@@ -0,0 +1,53 @@
1
+ import { render } from "@testing-library/react-native";
2
+ import type { FC } from "react";
3
+ import React from "react";
4
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
5
+
6
+ describe("Carousel Animations", () => {
7
+ beforeEach(() => {
8
+ jest.useFakeTimers();
9
+ });
10
+
11
+ afterEach(() => {
12
+ jest.runOnlyPendingTimers();
13
+ jest.useRealTimers();
14
+ });
15
+
16
+ it("animates opacity with timing", () => {
17
+ const TestComponent: FC<{ value: number }> = ({ value }) => {
18
+ const sharedValue = useSharedValue(value);
19
+
20
+ // Using withTiming to wrap the value change, so it can be tested
21
+ const animatedStyle = useAnimatedStyle(() => ({
22
+ opacity: withTiming(sharedValue.value, { duration: 500 }),
23
+ }));
24
+
25
+ // When the prop changes, update the shared value
26
+ React.useEffect(() => {
27
+ sharedValue.value = value;
28
+ }, [value, sharedValue]);
29
+
30
+ return <Animated.View testID="animated-view" style={animatedStyle} />;
31
+ };
32
+
33
+ const initialValue = 0;
34
+ const updatedValue = 1;
35
+
36
+ const { getByTestId, rerender } = render(<TestComponent value={initialValue} />);
37
+ const view = getByTestId("animated-view");
38
+
39
+ // Verify the initial state
40
+ expect(view).toHaveAnimatedStyle({ opacity: initialValue });
41
+
42
+ // Trigger the value change
43
+ rerender(<TestComponent value={updatedValue} />);
44
+
45
+ // Advance the animation to the middle point
46
+ jest.advanceTimersByTime(250);
47
+ expect(view).toHaveAnimatedStyle({ opacity: 0.5 }); // Animation middle value
48
+
49
+ // Advance the animation to the completion
50
+ jest.advanceTimersByTime(250);
51
+ expect(view).toHaveAnimatedStyle({ opacity: updatedValue }); // Animation completion
52
+ });
53
+ });
@@ -0,0 +1,11 @@
1
+ import type { EasingFunction } from "react-native-reanimated";
2
+ import { Easing as _Easing } from "react-native-reanimated";
3
+
4
+ export enum DATA_LENGTH {
5
+ SINGLE_ITEM = 1,
6
+ DOUBLE_ITEM = 2,
7
+ }
8
+
9
+ export const Easing = {
10
+ easeOutQuart: _Easing.bezier(0.25, 1, 0.5, 1) as unknown as EasingFunction,
11
+ };