@fountain-ui/lab 2.0.0-beta.17 → 2.0.0-beta.18

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 (37) hide show
  1. package/build/commonjs/Carousel/Carousel.js +6 -13
  2. package/build/commonjs/Carousel/Carousel.js.map +1 -1
  3. package/build/commonjs/Carousel/components/ScrollViewGesture.js +4 -3
  4. package/build/commonjs/Carousel/components/ScrollViewGesture.js.map +1 -1
  5. package/build/commonjs/Carousel/hooks/index.js +3 -3
  6. package/build/commonjs/Carousel/hooks/index.js.map +1 -1
  7. package/build/commonjs/Carousel/hooks/useIndexController.js +14 -13
  8. package/build/commonjs/Carousel/hooks/useIndexController.js.map +1 -1
  9. package/build/commonjs/Carousel/hooks/{usePagingAnimation.js → usePagingAnimator.js} +44 -22
  10. package/build/commonjs/Carousel/hooks/usePagingAnimator.js.map +1 -0
  11. package/build/commonjs/Carousel/types.js +1 -1
  12. package/build/commonjs/Carousel/types.js.map +1 -1
  13. package/build/module/Carousel/Carousel.js +8 -12
  14. package/build/module/Carousel/Carousel.js.map +1 -1
  15. package/build/module/Carousel/components/ScrollViewGesture.js +4 -3
  16. package/build/module/Carousel/components/ScrollViewGesture.js.map +1 -1
  17. package/build/module/Carousel/hooks/index.js +1 -1
  18. package/build/module/Carousel/hooks/index.js.map +1 -1
  19. package/build/module/Carousel/hooks/useIndexController.js +14 -13
  20. package/build/module/Carousel/hooks/useIndexController.js.map +1 -1
  21. package/build/module/Carousel/hooks/{usePagingAnimation.js → usePagingAnimator.js} +43 -22
  22. package/build/module/Carousel/hooks/usePagingAnimator.js.map +1 -0
  23. package/build/module/Carousel/types.js +1 -1
  24. package/build/module/Carousel/types.js.map +1 -1
  25. package/build/typescript/Carousel/components/ScrollViewGesture.d.ts +1 -1
  26. package/build/typescript/Carousel/hooks/index.d.ts +1 -1
  27. package/build/typescript/Carousel/hooks/{usePagingAnimation.d.ts → usePagingAnimator.d.ts} +6 -5
  28. package/build/typescript/Carousel/types.d.ts +8 -4
  29. package/package.json +2 -2
  30. package/src/Carousel/Carousel.tsx +7 -12
  31. package/src/Carousel/components/ScrollViewGesture.tsx +8 -4
  32. package/src/Carousel/hooks/index.ts +1 -1
  33. package/src/Carousel/hooks/useIndexController.tsx +12 -20
  34. package/src/Carousel/hooks/{usePagingAnimation.ts → usePagingAnimator.ts} +48 -29
  35. package/src/Carousel/types.ts +9 -4
  36. package/build/commonjs/Carousel/hooks/usePagingAnimation.js.map +0 -1
  37. package/build/module/Carousel/hooks/usePagingAnimation.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  export { default as useAutoplayController } from './useAutoplayController';
2
2
  export { default as useIndexController } from './useIndexController';
3
3
  export { default as useLoopedData } from './useLoopedData';
4
- export { default as usePagingAnimation } from './usePagingAnimation';
4
+ export { default as usePagingAnimator } from './usePagingAnimator';
5
5
  export { default as useItemVisibilityStore } from './useItemVisibilityStore';
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["default","useAutoplayController","useIndexController","useLoopedData","usePagingAnimation","useItemVisibilityStore"],"sources":["index.ts"],"sourcesContent":["export { default as useAutoplayController } from './useAutoplayController';\nexport { default as useIndexController } from './useIndexController';\nexport { default as useLoopedData } from './useLoopedData';\nexport { default as usePagingAnimation } from './usePagingAnimation';\nexport { default as useItemVisibilityStore } from './useItemVisibilityStore';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,qBAApB,QAAiD,yBAAjD;AACA,SAASD,OAAO,IAAIE,kBAApB,QAA8C,sBAA9C;AACA,SAASF,OAAO,IAAIG,aAApB,QAAyC,iBAAzC;AACA,SAASH,OAAO,IAAII,kBAApB,QAA8C,sBAA9C;AACA,SAASJ,OAAO,IAAIK,sBAApB,QAAkD,0BAAlD"}
1
+ {"version":3,"names":["default","useAutoplayController","useIndexController","useLoopedData","usePagingAnimator","useItemVisibilityStore"],"sources":["index.ts"],"sourcesContent":["export { default as useAutoplayController } from './useAutoplayController';\nexport { default as useIndexController } from './useIndexController';\nexport { default as useLoopedData } from './useLoopedData';\nexport { default as usePagingAnimator } from './usePagingAnimator';\nexport { default as useItemVisibilityStore } from './useItemVisibilityStore';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,qBAApB,QAAiD,yBAAjD;AACA,SAASD,OAAO,IAAIE,kBAApB,QAA8C,sBAA9C;AACA,SAASF,OAAO,IAAIG,aAApB,QAAyC,iBAAzC;AACA,SAASH,OAAO,IAAII,iBAApB,QAA6C,qBAA7C;AACA,SAASJ,OAAO,IAAIK,sBAApB,QAAkD,0BAAlD"}
@@ -12,8 +12,16 @@ export default function useIndexController(params) {
12
12
  } = params;
13
13
  const currentIndex = useImperativeState(initialIndex);
14
14
  const currentPosition = useImperativeState(initialIndex);
15
- const notifyAnimationState = useCallback(state => {
16
- if (state === 'finished' || state === 'interrupted') {
15
+ const notifyScrollStateHasChanged = useCallback(_ref => {
16
+ let {
17
+ offset,
18
+ state
19
+ } = _ref;
20
+ const normalized = -Math.round(offset / itemWidth);
21
+ const index = Math.floor(mod(normalized, numberOfOriginalData));
22
+ const position = Math.floor(mod(normalized, numberOfData));
23
+
24
+ if (state === 'idle' || state === 'interrupted') {
17
25
  if (currentIndex.hasChanged()) {
18
26
  onIndexChange === null || onIndexChange === void 0 ? void 0 : onIndexChange(currentIndex.get());
19
27
  }
@@ -22,21 +30,14 @@ export default function useIndexController(params) {
22
30
  onPositionChange === null || onPositionChange === void 0 ? void 0 : onPositionChange(currentPosition.get());
23
31
  }
24
32
  }
25
- }, [onIndexChange, onPositionChange]);
26
- const notifyOffsetHasChanged = useCallback(offset => {
27
- const roundedOffset = Math.round(offset / itemWidth) * itemWidth; // To prevent floating point problem, make sure index is integer type.
28
-
29
- const nextIndex = Math.floor(mod(-roundedOffset / itemWidth, numberOfOriginalData));
30
- currentIndex.set(nextIndex); // To prevent floating point problem, make sure index is integer type.
31
33
 
32
- const nextPosition = Math.floor(mod(-roundedOffset / itemWidth, numberOfData));
33
- currentPosition.set(nextPosition);
34
- }, [itemWidth, numberOfData, numberOfOriginalData]);
34
+ currentIndex.set(index);
35
+ currentPosition.set(position);
36
+ }, [itemWidth, numberOfData, numberOfOriginalData, onIndexChange, onPositionChange]);
35
37
  return {
36
38
  getCurrentIndex: currentIndex.get,
37
39
  lastIndex: numberOfOriginalData - 1,
38
- notifyAnimationState,
39
- notifyOffsetHasChanged
40
+ notifyScrollStateHasChanged
40
41
  };
41
42
  }
42
43
  ;
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useImperativeState","mod","useIndexController","params","initialIndex","itemWidth","numberOfData","numberOfOriginalData","onIndexChange","onPositionChange","currentIndex","currentPosition","notifyAnimationState","state","hasChanged","get","notifyOffsetHasChanged","offset","roundedOffset","Math","round","nextIndex","floor","set","nextPosition","getCurrentIndex","lastIndex"],"sources":["useIndexController.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { useImperativeState } from '@fountain-ui/core';\nimport { mod } from '@fountain-ui/utils';\nimport type { AnimationState, IndexController } from '../types';\n\nexport interface UseIndexControllerParameters {\n initialIndex: number;\n itemWidth: number;\n numberOfData: number;\n numberOfOriginalData: number;\n onIndexChange?: (itemIndex: number) => void;\n onPositionChange?: (position: number) => void;\n}\n\nexport default function useIndexController(params: UseIndexControllerParameters): IndexController {\n const {\n initialIndex,\n itemWidth,\n numberOfData,\n numberOfOriginalData,\n onIndexChange,\n onPositionChange,\n } = params;\n\n const currentIndex = useImperativeState(initialIndex);\n const currentPosition = useImperativeState(initialIndex);\n\n const notifyAnimationState = useCallback((state: AnimationState) => {\n if (state === 'finished' || state === 'interrupted') {\n if (currentIndex.hasChanged()) {\n onIndexChange?.(currentIndex.get());\n }\n\n if (currentPosition.hasChanged()) {\n onPositionChange?.(currentPosition.get());\n }\n }\n }, [\n onIndexChange,\n onPositionChange,\n ]);\n\n const notifyOffsetHasChanged = useCallback((offset: number) => {\n const roundedOffset = Math.round(offset / itemWidth) * itemWidth;\n\n // To prevent floating point problem, make sure index is integer type.\n const nextIndex = Math.floor(mod((-roundedOffset / itemWidth), numberOfOriginalData));\n currentIndex.set(nextIndex);\n\n // To prevent floating point problem, make sure index is integer type.\n const nextPosition = Math.floor(mod((-roundedOffset / itemWidth), numberOfData));\n currentPosition.set(nextPosition);\n }, [\n itemWidth,\n numberOfData,\n numberOfOriginalData,\n ]);\n\n return {\n getCurrentIndex: currentIndex.get,\n lastIndex: numberOfOriginalData - 1,\n notifyAnimationState,\n notifyOffsetHasChanged,\n };\n};\n"],"mappings":"AAAA,SAASA,WAAT,QAA4B,OAA5B;AACA,SAASC,kBAAT,QAAmC,mBAAnC;AACA,SAASC,GAAT,QAAoB,oBAApB;AAYA,eAAe,SAASC,kBAAT,CAA4BC,MAA5B,EAAmF;EAC9F,MAAM;IACFC,YADE;IAEFC,SAFE;IAGFC,YAHE;IAIFC,oBAJE;IAKFC,aALE;IAMFC;EANE,IAOFN,MAPJ;EASA,MAAMO,YAAY,GAAGV,kBAAkB,CAACI,YAAD,CAAvC;EACA,MAAMO,eAAe,GAAGX,kBAAkB,CAACI,YAAD,CAA1C;EAEA,MAAMQ,oBAAoB,GAAGb,WAAW,CAAEc,KAAD,IAA2B;IAChE,IAAIA,KAAK,KAAK,UAAV,IAAwBA,KAAK,KAAK,aAAtC,EAAqD;MACjD,IAAIH,YAAY,CAACI,UAAb,EAAJ,EAA+B;QAC3BN,aAAa,SAAb,IAAAA,aAAa,WAAb,YAAAA,aAAa,CAAGE,YAAY,CAACK,GAAb,EAAH,CAAb;MACH;;MAED,IAAIJ,eAAe,CAACG,UAAhB,EAAJ,EAAkC;QAC9BL,gBAAgB,SAAhB,IAAAA,gBAAgB,WAAhB,YAAAA,gBAAgB,CAAGE,eAAe,CAACI,GAAhB,EAAH,CAAhB;MACH;IACJ;EACJ,CAVuC,EAUrC,CACCP,aADD,EAECC,gBAFD,CAVqC,CAAxC;EAeA,MAAMO,sBAAsB,GAAGjB,WAAW,CAAEkB,MAAD,IAAoB;IAC3D,MAAMC,aAAa,GAAGC,IAAI,CAACC,KAAL,CAAWH,MAAM,GAAGZ,SAApB,IAAiCA,SAAvD,CAD2D,CAG3D;;IACA,MAAMgB,SAAS,GAAGF,IAAI,CAACG,KAAL,CAAWrB,GAAG,CAAE,CAACiB,aAAD,GAAiBb,SAAnB,EAA+BE,oBAA/B,CAAd,CAAlB;IACAG,YAAY,CAACa,GAAb,CAAiBF,SAAjB,EAL2D,CAO3D;;IACA,MAAMG,YAAY,GAAGL,IAAI,CAACG,KAAL,CAAWrB,GAAG,CAAE,CAACiB,aAAD,GAAiBb,SAAnB,EAA+BC,YAA/B,CAAd,CAArB;IACAK,eAAe,CAACY,GAAhB,CAAoBC,YAApB;EACH,CAVyC,EAUvC,CACCnB,SADD,EAECC,YAFD,EAGCC,oBAHD,CAVuC,CAA1C;EAgBA,OAAO;IACHkB,eAAe,EAAEf,YAAY,CAACK,GAD3B;IAEHW,SAAS,EAAEnB,oBAAoB,GAAG,CAF/B;IAGHK,oBAHG;IAIHI;EAJG,CAAP;AAMH;AAAA"}
1
+ {"version":3,"names":["useCallback","useImperativeState","mod","useIndexController","params","initialIndex","itemWidth","numberOfData","numberOfOriginalData","onIndexChange","onPositionChange","currentIndex","currentPosition","notifyScrollStateHasChanged","offset","state","normalized","Math","round","index","floor","position","hasChanged","get","set","getCurrentIndex","lastIndex"],"sources":["useIndexController.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { useImperativeState } from '@fountain-ui/core';\nimport { mod } from '@fountain-ui/utils';\nimport type { IndexController, ScrollStateChangeEvent } from '../types';\n\nexport interface UseIndexControllerParameters {\n initialIndex: number;\n itemWidth: number;\n numberOfData: number;\n numberOfOriginalData: number;\n onIndexChange?: (itemIndex: number) => void;\n onPositionChange?: (position: number) => void;\n}\n\nexport default function useIndexController(params: UseIndexControllerParameters): IndexController {\n const {\n initialIndex,\n itemWidth,\n numberOfData,\n numberOfOriginalData,\n onIndexChange,\n onPositionChange,\n } = params;\n\n const currentIndex = useImperativeState(initialIndex);\n const currentPosition = useImperativeState(initialIndex);\n\n const notifyScrollStateHasChanged = useCallback(({ offset, state }: ScrollStateChangeEvent) => {\n const normalized = -Math.round(offset / itemWidth);\n const index = Math.floor(mod(normalized, numberOfOriginalData));\n const position = Math.floor(mod(normalized, numberOfData));\n\n if (state === 'idle' || state === 'interrupted') {\n if (currentIndex.hasChanged()) {\n onIndexChange?.(currentIndex.get());\n }\n if (currentPosition.hasChanged()) {\n onPositionChange?.(currentPosition.get());\n }\n }\n\n currentIndex.set(index);\n currentPosition.set(position);\n }, [\n itemWidth,\n numberOfData,\n numberOfOriginalData,\n onIndexChange,\n onPositionChange,\n ]);\n\n return {\n getCurrentIndex: currentIndex.get,\n lastIndex: numberOfOriginalData - 1,\n notifyScrollStateHasChanged,\n };\n};\n"],"mappings":"AAAA,SAASA,WAAT,QAA4B,OAA5B;AACA,SAASC,kBAAT,QAAmC,mBAAnC;AACA,SAASC,GAAT,QAAoB,oBAApB;AAYA,eAAe,SAASC,kBAAT,CAA4BC,MAA5B,EAAmF;EAC9F,MAAM;IACFC,YADE;IAEFC,SAFE;IAGFC,YAHE;IAIFC,oBAJE;IAKFC,aALE;IAMFC;EANE,IAOFN,MAPJ;EASA,MAAMO,YAAY,GAAGV,kBAAkB,CAACI,YAAD,CAAvC;EACA,MAAMO,eAAe,GAAGX,kBAAkB,CAACI,YAAD,CAA1C;EAEA,MAAMQ,2BAA2B,GAAGb,WAAW,CAAC,QAA+C;IAAA,IAA9C;MAAEc,MAAF;MAAUC;IAAV,CAA8C;IAC3F,MAAMC,UAAU,GAAG,CAACC,IAAI,CAACC,KAAL,CAAWJ,MAAM,GAAGR,SAApB,CAApB;IACA,MAAMa,KAAK,GAAGF,IAAI,CAACG,KAAL,CAAWlB,GAAG,CAACc,UAAD,EAAaR,oBAAb,CAAd,CAAd;IACA,MAAMa,QAAQ,GAAGJ,IAAI,CAACG,KAAL,CAAWlB,GAAG,CAACc,UAAD,EAAaT,YAAb,CAAd,CAAjB;;IAEA,IAAIQ,KAAK,KAAK,MAAV,IAAoBA,KAAK,KAAK,aAAlC,EAAiD;MAC7C,IAAIJ,YAAY,CAACW,UAAb,EAAJ,EAA+B;QAC3Bb,aAAa,SAAb,IAAAA,aAAa,WAAb,YAAAA,aAAa,CAAGE,YAAY,CAACY,GAAb,EAAH,CAAb;MACH;;MACD,IAAIX,eAAe,CAACU,UAAhB,EAAJ,EAAkC;QAC9BZ,gBAAgB,SAAhB,IAAAA,gBAAgB,WAAhB,YAAAA,gBAAgB,CAAGE,eAAe,CAACW,GAAhB,EAAH,CAAhB;MACH;IACJ;;IAEDZ,YAAY,CAACa,GAAb,CAAiBL,KAAjB;IACAP,eAAe,CAACY,GAAhB,CAAoBH,QAApB;EACH,CAhB8C,EAgB5C,CACCf,SADD,EAECC,YAFD,EAGCC,oBAHD,EAICC,aAJD,EAKCC,gBALD,CAhB4C,CAA/C;EAwBA,OAAO;IACHe,eAAe,EAAEd,YAAY,CAACY,GAD3B;IAEHG,SAAS,EAAElB,oBAAoB,GAAG,CAF/B;IAGHK;EAHG,CAAP;AAKH;AAAA"}
@@ -1,4 +1,5 @@
1
- import { useCallback, useRef } from 'react';
1
+ import { useCallback, useMemo, useRef } from 'react';
2
+ import { Animated } from 'react-native';
2
3
 
3
4
  function directionToValue(itemWidth) {
4
5
  return function (direction) {
@@ -25,22 +26,27 @@ function toValueCompensator(itemWidth) {
25
26
  };
26
27
  }
27
28
 
28
- export default function usePagingAnimation(params) {
29
+ export default function usePagingAnimator(config) {
29
30
  const {
30
31
  createScrollAnimation,
31
32
  itemWidth,
32
33
  indexController,
34
+ initialIndex,
33
35
  loop,
34
- numberOfData,
35
- offsetX,
36
- translateX
37
- } = params;
36
+ numberOfData
37
+ } = config;
38
38
  const {
39
39
  getCurrentIndex,
40
40
  lastIndex,
41
- notifyAnimationState,
42
- notifyOffsetHasChanged
41
+ notifyScrollStateHasChanged
43
42
  } = indexController;
43
+ const initialOffsetX = itemWidth * initialIndex;
44
+ const accumulativeOffsetX = useRef(new Animated.Value(initialOffsetX)).current;
45
+ const animationOffsetX = useRef(new Animated.Value(0)).current;
46
+ const gestureTranslationX = useRef(new Animated.Value(0)).current;
47
+ const globalInterpolation = useMemo(() => {
48
+ return Animated.add(Animated.add(accumulativeOffsetX, animationOffsetX), gestureTranslationX);
49
+ }, []);
44
50
  const toValueRef = useRef(0);
45
51
  const currentOffsetRef = useRef(0);
46
52
  const isAnimatingRef = useRef(false);
@@ -60,32 +66,37 @@ export default function usePagingAnimation(params) {
60
66
  const requireNewOffset = useCallback(newOffset => {
61
67
  const nextOffset = ensureOffsetBoundary(newOffset);
62
68
  currentOffsetRef.current = nextOffset;
63
- offsetX.setValue(nextOffset);
69
+ accumulativeOffsetX.setValue(nextOffset);
64
70
  toValueRef.current = 0;
65
- translateX.setValue(0);
66
- }, [ensureOffsetBoundary, offsetX, translateX]);
71
+ animationOffsetX.setValue(0);
72
+ }, [ensureOffsetBoundary]);
67
73
  const interruptAnimation = useCallback(() => {
68
74
  if (!isAnimatingRef.current) {
69
75
  return;
70
76
  }
71
77
 
72
- translateX.stopAnimation(lastValue => {
78
+ animationOffsetX.stopAnimation(lastValue => {
73
79
  isAnimatingRef.current = false;
74
80
  const prevOffset = currentOffsetRef.current;
75
81
  const totalOffset = prevOffset + lastValue;
76
- notifyOffsetHasChanged(totalOffset);
77
- notifyAnimationState('interrupted');
78
82
  requireNewOffset(totalOffset);
83
+ notifyScrollStateHasChanged({
84
+ offset: totalOffset,
85
+ state: 'interrupted'
86
+ });
79
87
  });
80
- }, [requireNewOffset, translateX]);
88
+ }, [notifyScrollStateHasChanged, requireNewOffset]);
81
89
  const finalizeAnimation = useCallback(() => {
82
- notifyAnimationState('finished');
83
90
  isAnimatingRef.current = false;
84
91
  const prevOffset = currentOffsetRef.current;
85
92
  const toValue = toValueRef.current;
86
93
  const totalOffset = prevOffset + toValue;
87
94
  requireNewOffset(totalOffset);
88
- }, [notifyAnimationState, requireNewOffset]);
95
+ notifyScrollStateHasChanged({
96
+ offset: totalOffset,
97
+ state: 'idle'
98
+ });
99
+ }, [notifyScrollStateHasChanged, requireNewOffset]);
89
100
  const startPagingAnimation = useCallback((type, config) => {
90
101
  if (isAnimatingRef.current) {
91
102
  return;
@@ -143,10 +154,19 @@ export default function usePagingAnimation(params) {
143
154
  const toValue = compensateToValue(wantedToValue, currentOffsetRef.current);
144
155
  toValueRef.current = toValue;
145
156
  isAnimatingRef.current = true;
146
- notifyOffsetHasChanged(currentOffsetRef.current + toValue);
157
+ notifyScrollStateHasChanged({
158
+ offset: currentOffsetRef.current + toValue,
159
+ state: 'dragging'
160
+ });
161
+ const lastGestureTranslationX = configWithDefaults.lastGestureTranslationX ?? 0;
162
+
163
+ if (Number.isFinite(lastGestureTranslationX)) {
164
+ animationOffsetX.setValue(lastGestureTranslationX);
165
+ gestureTranslationX.setValue(0);
166
+ }
147
167
 
148
168
  if (configWithDefaults.animated) {
149
- const animation = createScrollAnimation(translateX, toValue);
169
+ const animation = createScrollAnimation(animationOffsetX, toValue);
150
170
  animation.start(_ref2 => {
151
171
  let {
152
172
  finished
@@ -156,15 +176,16 @@ export default function usePagingAnimation(params) {
156
176
  finalizeAnimation();
157
177
  }
158
178
  });
159
- notifyAnimationState('started');
160
179
  } else {
161
180
  finalizeAnimation();
162
181
  }
163
- }, [createScrollAnimation, getCurrentIndex, finalizeAnimation, itemWidth, lastIndex, loop, notifyAnimationState, notifyOffsetHasChanged]);
182
+ }, [createScrollAnimation, getCurrentIndex, finalizeAnimation, itemWidth, lastIndex, loop, notifyScrollStateHasChanged]);
164
183
  return {
184
+ gestureTranslationX,
185
+ globalInterpolation,
165
186
  interruptAnimation,
166
187
  startPagingAnimation
167
188
  };
168
189
  }
169
190
  ;
170
- //# sourceMappingURL=usePagingAnimation.js.map
191
+ //# sourceMappingURL=usePagingAnimator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useCallback","useMemo","useRef","Animated","directionToValue","itemWidth","direction","toValueCompensator","toValue","currentOffset","remainder","Math","abs","halfOfItemWidth","compensateVector","usePagingAnimator","config","createScrollAnimation","indexController","initialIndex","loop","numberOfData","getCurrentIndex","lastIndex","notifyScrollStateHasChanged","initialOffsetX","accumulativeOffsetX","Value","current","animationOffsetX","gestureTranslationX","globalInterpolation","add","toValueRef","currentOffsetRef","isAnimatingRef","maxWidth","ensureOffsetBoundary","offset","isCloseToEnd","signOfOffset","requireNewOffset","newOffset","nextOffset","setValue","interruptAnimation","stopAnimation","lastValue","prevOffset","totalOffset","state","finalizeAnimation","startPagingAnimation","type","configWithDefaults","animated","currentIndex","getValueByDirectionOnAllAdjacentItemsVisible","compensateToValue","getValueByDirectionalPagingOnLoopDisabled","_config","isOriginatedFromGesture","getValueByDirectionalPaging","_configWithDefaults","getValueByIndexPaging","index","distance","wantedToValue","lastGestureTranslationX","Number","isFinite","animation","start","finished"],"sources":["usePagingAnimator.ts"],"sourcesContent":["import { useCallback, useMemo, useRef } from 'react';\nimport { Animated } from 'react-native';\nimport type {\n CreateScrollAnimation,\n DirectionalPagingAnimationConfig,\n IndexController,\n IndexPagingAnimationConfig,\n PagingAnimationConfig,\n PagingAnimationType,\n PagingDirection,\n StartPagingAnimation,\n} from '../types';\n\nexport interface PagingAnimatorConfig {\n createScrollAnimation: CreateScrollAnimation;\n itemWidth: number;\n indexController: IndexController;\n initialIndex: number;\n loop: boolean;\n numberOfData: number;\n}\n\nexport interface PagingAnimator {\n gestureTranslationX: Animated.Value;\n globalInterpolation: Animated.AnimatedInterpolation;\n interruptAnimation: () => void;\n startPagingAnimation: StartPagingAnimation;\n}\n\nfunction directionToValue(itemWidth: number) {\n return function (direction: PagingDirection): number {\n switch (direction) {\n case 'next':\n return -itemWidth;\n case 'prev':\n return itemWidth;\n case 'stay':\n return 0;\n }\n };\n}\n\nfunction toValueCompensator(itemWidth: number) {\n return function (toValue: number, currentOffset: number): number {\n const remainder = Math.abs(currentOffset % itemWidth);\n\n const halfOfItemWidth = Math.abs(itemWidth / 2);\n const compensateVector = remainder > halfOfItemWidth\n ? remainder - itemWidth\n : remainder;\n\n const direction = currentOffset > 0 ? -1 : 1;\n\n return toValue + (direction * compensateVector);\n };\n}\n\nexport default function usePagingAnimator(config: PagingAnimatorConfig): PagingAnimator {\n const {\n createScrollAnimation,\n itemWidth,\n indexController,\n initialIndex,\n loop,\n numberOfData,\n } = config;\n\n const {\n getCurrentIndex,\n lastIndex,\n notifyScrollStateHasChanged,\n } = indexController;\n\n const initialOffsetX = itemWidth * initialIndex;\n const accumulativeOffsetX = useRef(new Animated.Value(initialOffsetX)).current;\n const animationOffsetX = useRef(new Animated.Value(0)).current;\n const gestureTranslationX = useRef(new Animated.Value(0)).current;\n\n const globalInterpolation = useMemo(() => {\n return Animated.add(\n Animated.add(accumulativeOffsetX, animationOffsetX),\n gestureTranslationX,\n );\n }, []);\n\n const toValueRef = useRef<number>(0);\n const currentOffsetRef = useRef<number>(0);\n\n const isAnimatingRef = useRef<boolean>(false);\n\n const maxWidth = Math.abs(numberOfData * itemWidth);\n\n const ensureOffsetBoundary: (offset: number) => number = useCallback((offset: number) => {\n if (loop) {\n const isCloseToEnd = Math.abs(offset) >= (maxWidth - itemWidth);\n if (isCloseToEnd) {\n const signOfOffset = offset > 0 ? 1 : -1;\n return offset + (-signOfOffset * maxWidth);\n }\n }\n\n return offset % maxWidth;\n }, [itemWidth, loop, maxWidth]);\n\n const requireNewOffset = useCallback((newOffset: number) => {\n const nextOffset = ensureOffsetBoundary(newOffset);\n\n currentOffsetRef.current = nextOffset;\n accumulativeOffsetX.setValue(nextOffset);\n\n toValueRef.current = 0;\n animationOffsetX.setValue(0);\n }, [\n ensureOffsetBoundary,\n ]);\n\n const interruptAnimation = useCallback(() => {\n if (!isAnimatingRef.current) {\n return;\n }\n\n animationOffsetX.stopAnimation(lastValue => {\n isAnimatingRef.current = false;\n\n const prevOffset = currentOffsetRef.current;\n const totalOffset = prevOffset + lastValue;\n\n requireNewOffset(totalOffset);\n\n notifyScrollStateHasChanged({ offset: totalOffset, state: 'interrupted' });\n });\n }, [\n notifyScrollStateHasChanged,\n requireNewOffset,\n ]);\n\n const finalizeAnimation = useCallback(() => {\n isAnimatingRef.current = false;\n\n const prevOffset = currentOffsetRef.current;\n const toValue = toValueRef.current;\n const totalOffset = prevOffset + toValue;\n\n requireNewOffset(totalOffset);\n\n notifyScrollStateHasChanged({ offset: totalOffset, state: 'idle' });\n }, [\n notifyScrollStateHasChanged,\n requireNewOffset,\n ]);\n\n const startPagingAnimation = useCallback((type: PagingAnimationType, config: PagingAnimationConfig) => {\n if (isAnimatingRef.current) {\n return;\n }\n\n const configWithDefaults: PagingAnimationConfig = {\n animated: true,\n ...config,\n };\n\n const currentIndex = getCurrentIndex();\n\n const getValueByDirectionOnAllAdjacentItemsVisible = directionToValue(itemWidth);\n const compensateToValue = toValueCompensator(itemWidth);\n\n const getValueByDirectionalPagingOnLoopDisabled = (_config: DirectionalPagingAnimationConfig): number => {\n const { direction, isOriginatedFromGesture } = _config;\n\n if (currentIndex === 0 && direction === 'prev') {\n return isOriginatedFromGesture\n ? getValueByDirectionOnAllAdjacentItemsVisible('stay')\n : -lastIndex * itemWidth; // last position\n } else if (currentIndex === lastIndex && direction === 'next') {\n return isOriginatedFromGesture\n ? getValueByDirectionOnAllAdjacentItemsVisible('stay')\n : lastIndex * itemWidth; // first position\n }\n return getValueByDirectionOnAllAdjacentItemsVisible(direction);\n };\n\n const getValueByDirectionalPaging = (_config: DirectionalPagingAnimationConfig): number => {\n const _configWithDefaults: DirectionalPagingAnimationConfig = {\n isOriginatedFromGesture: false,\n ..._config,\n };\n\n return loop\n ? getValueByDirectionOnAllAdjacentItemsVisible(_configWithDefaults.direction)\n : getValueByDirectionalPagingOnLoopDisabled(_configWithDefaults);\n };\n\n const getValueByIndexPaging = ({ index }: IndexPagingAnimationConfig): number => {\n if (index < 0 || index > lastIndex || index === currentIndex) {\n // no animation if index is invalid or equals to current index\n return 0;\n }\n\n const distance = Math.abs(currentIndex - index) * itemWidth;\n const direction = index > currentIndex ? -1 : 1;\n\n return distance * direction;\n };\n\n const wantedToValue = type === 'directional'\n // @ts-ignore\n ? getValueByDirectionalPaging(configWithDefaults)\n // @ts-ignore\n : getValueByIndexPaging(configWithDefaults);\n\n const toValue = compensateToValue(wantedToValue, currentOffsetRef.current);\n\n toValueRef.current = toValue;\n isAnimatingRef.current = true;\n\n notifyScrollStateHasChanged({\n offset: currentOffsetRef.current + toValue,\n state: 'dragging',\n });\n\n const lastGestureTranslationX: number = configWithDefaults.lastGestureTranslationX ?? 0;\n if (Number.isFinite(lastGestureTranslationX)) {\n animationOffsetX.setValue(lastGestureTranslationX);\n gestureTranslationX.setValue(0);\n }\n\n if (configWithDefaults.animated) {\n const animation = createScrollAnimation(animationOffsetX, toValue);\n\n animation.start(({ finished }) => {\n if (finished) {\n finalizeAnimation();\n }\n });\n } else {\n finalizeAnimation();\n }\n }, [\n createScrollAnimation,\n getCurrentIndex,\n finalizeAnimation,\n itemWidth,\n lastIndex,\n loop,\n notifyScrollStateHasChanged,\n ]);\n\n return {\n gestureTranslationX,\n globalInterpolation,\n interruptAnimation,\n startPagingAnimation,\n };\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  const directions = ['next', 'prev', 'stay'];
2
- const animationStates = ['started', 'finished', 'interrupted'];
2
+ const scrollStates = ['idle', 'dragging', 'interrupted'];
3
3
  export {};
4
4
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["directions","animationStates"],"sources":["types.ts"],"sourcesContent":["import type { ReactElement } from 'react';\nimport type { Animated, ViewProps } from 'react-native';\n\nconst directions = ['next', 'prev', 'stay'] as const;\n\nexport type PagingDirection = (typeof directions)[number];\n\nconst animationStates = ['started', 'finished', 'interrupted'] as const;\n\nexport type AnimationState = (typeof animationStates)[number];\n\nexport type ItemHeight = number | 'auto';\n\nexport interface RenderItem<T> {\n (info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;\n}\n\nexport interface CreateScrollAnimation {\n (aValue: Animated.AnimatedValue, toValue: number): Animated.CompositeAnimation;\n}\n\nexport interface CreateItemStyle {\n (itemInterpolation: Animated.AnimatedInterpolation, itemWidth: number): Animated.AnimatedProps<ViewProps['style']>;\n}\n\nexport interface GetCurrentIndex {\n (): number;\n}\n\nexport interface OnIndexChange {\n (itemIndex: number): void;\n}\n\nexport interface OnPositionChange {\n (position: number): void;\n}\n\nexport interface IndexController {\n getCurrentIndex: GetCurrentIndex;\n lastIndex: number;\n notifyAnimationState: (state: AnimationState) => void;\n notifyOffsetHasChanged: (offset: number) => void;\n}\n\nexport type PagingAnimationType = 'directional' | 'index';\n\nexport interface BasePagingAnimationConfig {\n animated?: boolean;\n}\n\nexport interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {\n direction: PagingDirection;\n isOriginatedFromGesture?: boolean;\n}\n\nexport interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {\n index: number;\n}\n\nexport type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;\n\nexport interface StartPagingAnimation {\n (type: PagingAnimationType, config: PagingAnimationConfig): void;\n}\n\nexport type VisibleIndexRanges = Array<[number, number]>;\n\nexport interface StoreSubscription {\n (): void;\n}\n\nexport interface ItemVisibilityStore {\n dispatch: (ranges: VisibleIndexRanges) => void;\n subscribe: (listener: (ranges: VisibleIndexRanges) => void) => StoreSubscription;\n removeAllListeners: () => void;\n}\n\nexport interface AutoplayController {\n pause: () => void;\n resume: () => void;\n}\n\nexport interface ScrollToOption {\n index: number;\n animated?: boolean;\n}\n\nexport interface CarouselInstance {\n /**\n * Get current visible item index.\n */\n getCurrentIndex: GetCurrentIndex;\n\n /**\n * Scroll to next visible item.\n */\n next: () => void;\n\n /**\n * Scroll to previous visible item.\n */\n prev: () => void;\n\n /**\n * Scroll to desired indexed item.\n * Invalid index is ignored.\n */\n scrollTo: (option: ScrollToOption) => void;\n}\n"],"mappings":"AAGA,MAAMA,UAAU,GAAG,CAAC,MAAD,EAAS,MAAT,EAAiB,MAAjB,CAAnB;AAIA,MAAMC,eAAe,GAAG,CAAC,SAAD,EAAY,UAAZ,EAAwB,aAAxB,CAAxB"}
1
+ {"version":3,"names":["directions","scrollStates"],"sources":["types.ts"],"sourcesContent":["import type { ReactElement } from 'react';\nimport type { Animated, ViewProps } from 'react-native';\n\nconst directions = ['next', 'prev', 'stay'] as const;\n\nexport type PagingDirection = (typeof directions)[number];\n\nconst scrollStates = ['idle', 'dragging', 'interrupted'] as const;\n\nexport type ScrollState = (typeof scrollStates)[number];\n\nexport type ItemHeight = number | 'auto';\n\nexport interface RenderItem<T> {\n (info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;\n}\n\nexport interface CreateScrollAnimation {\n (aValue: Animated.AnimatedValue, toValue: number): Animated.CompositeAnimation;\n}\n\nexport interface CreateItemStyle {\n (itemInterpolation: Animated.AnimatedInterpolation, itemWidth: number): Animated.AnimatedProps<ViewProps['style']>;\n}\n\nexport interface GetCurrentIndex {\n (): number;\n}\n\nexport interface OnIndexChange {\n (itemIndex: number): void;\n}\n\nexport interface OnPositionChange {\n (position: number): void;\n}\n\nexport interface ScrollStateChangeEvent {\n offset: number;\n state: ScrollState;\n}\n\nexport interface IndexController {\n getCurrentIndex: GetCurrentIndex;\n lastIndex: number;\n notifyScrollStateHasChanged: (event: ScrollStateChangeEvent) => void;\n}\n\nexport type PagingAnimationType = 'directional' | 'index';\n\nexport interface BasePagingAnimationConfig {\n animated?: boolean;\n lastGestureTranslationX?: number;\n}\n\nexport interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {\n direction: PagingDirection;\n isOriginatedFromGesture?: boolean;\n}\n\nexport interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {\n index: number;\n}\n\nexport type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;\n\nexport interface StartPagingAnimation {\n (type: PagingAnimationType, config: PagingAnimationConfig): void;\n}\n\nexport type VisibleIndexRanges = Array<[number, number]>;\n\nexport interface StoreSubscription {\n (): void;\n}\n\nexport interface ItemVisibilityStore {\n dispatch: (ranges: VisibleIndexRanges) => void;\n subscribe: (listener: (ranges: VisibleIndexRanges) => void) => StoreSubscription;\n removeAllListeners: () => void;\n}\n\nexport interface AutoplayController {\n pause: () => void;\n resume: () => void;\n}\n\nexport interface ScrollToOption {\n index: number;\n animated?: boolean;\n}\n\nexport interface CarouselInstance {\n /**\n * Get current visible item index.\n */\n getCurrentIndex: GetCurrentIndex;\n\n /**\n * Scroll to next visible item.\n */\n next: () => void;\n\n /**\n * Scroll to previous visible item.\n */\n prev: () => void;\n\n /**\n * Scroll to desired indexed item.\n * Invalid index is ignored.\n */\n scrollTo: (option: ScrollToOption) => void;\n}\n"],"mappings":"AAGA,MAAMA,UAAU,GAAG,CAAC,MAAD,EAAS,MAAT,EAAiB,MAAjB,CAAnB;AAIA,MAAMC,YAAY,GAAG,CAAC,MAAD,EAAS,UAAT,EAAqB,aAArB,CAArB"}
@@ -4,8 +4,8 @@ import type { AutoplayController, StartPagingAnimation } from '../types';
4
4
  export interface ScrollViewGestureProps {
5
5
  autoplayController: AutoplayController;
6
6
  children: ReactNode;
7
+ gestureTranslationX: Animated.Value;
7
8
  interruptAnimation: () => void;
8
- translateX: Animated.Value;
9
9
  scrollEnabled: boolean;
10
10
  startPagingAnimation: StartPagingAnimation;
11
11
  }
@@ -1,5 +1,5 @@
1
1
  export { default as useAutoplayController } from './useAutoplayController';
2
2
  export { default as useIndexController } from './useIndexController';
3
3
  export { default as useLoopedData } from './useLoopedData';
4
- export { default as usePagingAnimation } from './usePagingAnimation';
4
+ export { default as usePagingAnimator } from './usePagingAnimator';
5
5
  export { default as useItemVisibilityStore } from './useItemVisibilityStore';
@@ -1,16 +1,17 @@
1
1
  import { Animated } from 'react-native';
2
2
  import type { CreateScrollAnimation, IndexController, StartPagingAnimation } from '../types';
3
- export interface PagingAnimationParameters {
3
+ export interface PagingAnimatorConfig {
4
4
  createScrollAnimation: CreateScrollAnimation;
5
5
  itemWidth: number;
6
6
  indexController: IndexController;
7
+ initialIndex: number;
7
8
  loop: boolean;
8
9
  numberOfData: number;
9
- offsetX: Animated.Value;
10
- translateX: Animated.Value;
11
10
  }
12
- export interface UsePagingAnimation {
11
+ export interface PagingAnimator {
12
+ gestureTranslationX: Animated.Value;
13
+ globalInterpolation: Animated.AnimatedInterpolation;
13
14
  interruptAnimation: () => void;
14
15
  startPagingAnimation: StartPagingAnimation;
15
16
  }
16
- export default function usePagingAnimation(params: PagingAnimationParameters): UsePagingAnimation;
17
+ export default function usePagingAnimator(config: PagingAnimatorConfig): PagingAnimator;
@@ -2,8 +2,8 @@ import type { ReactElement } from 'react';
2
2
  import type { Animated, ViewProps } from 'react-native';
3
3
  declare const directions: readonly ["next", "prev", "stay"];
4
4
  export declare type PagingDirection = (typeof directions)[number];
5
- declare const animationStates: readonly ["started", "finished", "interrupted"];
6
- export declare type AnimationState = (typeof animationStates)[number];
5
+ declare const scrollStates: readonly ["idle", "dragging", "interrupted"];
6
+ export declare type ScrollState = (typeof scrollStates)[number];
7
7
  export declare type ItemHeight = number | 'auto';
8
8
  export interface RenderItem<T> {
9
9
  (info: {
@@ -27,15 +27,19 @@ export interface OnIndexChange {
27
27
  export interface OnPositionChange {
28
28
  (position: number): void;
29
29
  }
30
+ export interface ScrollStateChangeEvent {
31
+ offset: number;
32
+ state: ScrollState;
33
+ }
30
34
  export interface IndexController {
31
35
  getCurrentIndex: GetCurrentIndex;
32
36
  lastIndex: number;
33
- notifyAnimationState: (state: AnimationState) => void;
34
- notifyOffsetHasChanged: (offset: number) => void;
37
+ notifyScrollStateHasChanged: (event: ScrollStateChangeEvent) => void;
35
38
  }
36
39
  export declare type PagingAnimationType = 'directional' | 'index';
37
40
  export interface BasePagingAnimationConfig {
38
41
  animated?: boolean;
42
+ lastGestureTranslationX?: number;
39
43
  }
40
44
  export interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {
41
45
  direction: PagingDirection;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fountain-ui/lab",
3
- "version": "2.0.0-beta.17",
3
+ "version": "2.0.0-beta.18",
4
4
  "private": false,
5
5
  "author": "Fountain-UI Team",
6
6
  "description": "Incubator for Fountain-UI React components.",
@@ -70,5 +70,5 @@
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
- "gitHead": "973591c61fab2561cea9f30d03e802a24817b5f8"
73
+ "gitHead": "8ca13c00c2424b287a26c7cb62619c2c0a9596df"
74
74
  }
@@ -1,5 +1,4 @@
1
- import React, { forwardRef, memo, useImperativeHandle, useMemo, useRef } from 'react';
2
- import { Animated } from 'react-native';
1
+ import React, { forwardRef, memo, useImperativeHandle, useMemo } from 'react';
3
2
  import ViewabilityTrackerView from '../ViewabilityTrackerView';
4
3
  import type CarouselProps from './CarouselProps';
5
4
  import type { CarouselInstance } from './types';
@@ -8,7 +7,7 @@ import {
8
7
  useIndexController,
9
8
  useItemVisibilityStore,
10
9
  useLoopedData,
11
- usePagingAnimation,
10
+ usePagingAnimator,
12
11
  } from './hooks';
13
12
  import { createDefaultItemStyle, createDefaultScrollAnimation } from './animation';
14
13
  import { InternalContext, RootView, ScrollViewGesture } from './components';
@@ -34,11 +33,6 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
34
33
 
35
34
  const data = useLoopedData(originalData, loop);
36
35
 
37
- const initialTx = itemWidth * initialIndex;
38
- const offsetX = useRef(new Animated.Value(initialTx)).current;
39
- const translateX = useRef(new Animated.Value(0)).current;
40
- const globalInterpolation = Animated.add(offsetX, translateX);
41
-
42
36
  const [itemVisibilityStore, onPositionChange] = useItemVisibilityStore({
43
37
  initialIndex,
44
38
  numberOfData: data.length,
@@ -57,16 +51,17 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
57
51
  const { getCurrentIndex } = indexController;
58
52
 
59
53
  const {
54
+ gestureTranslationX,
55
+ globalInterpolation,
60
56
  interruptAnimation,
61
57
  startPagingAnimation,
62
- } = usePagingAnimation({
58
+ } = usePagingAnimator({
63
59
  createScrollAnimation,
64
60
  itemWidth,
65
61
  indexController,
62
+ initialIndex,
66
63
  loop,
67
64
  numberOfData: data.length,
68
- offsetX,
69
- translateX,
70
65
  });
71
66
 
72
67
  const autoplayController = useAutoplayController({
@@ -119,8 +114,8 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
119
114
  >
120
115
  <ScrollViewGesture
121
116
  autoplayController={autoplayController}
117
+ gestureTranslationX={gestureTranslationX}
122
118
  interruptAnimation={interruptAnimation}
123
- translateX={translateX}
124
119
  scrollEnabled={scrollEnabled}
125
120
  startPagingAnimation={startPagingAnimation}
126
121
  >
@@ -8,8 +8,8 @@ import type { AutoplayController, PagingDirection, StartPagingAnimation } from '
8
8
  export interface ScrollViewGestureProps {
9
9
  autoplayController: AutoplayController;
10
10
  children: ReactNode;
11
+ gestureTranslationX: Animated.Value,
11
12
  interruptAnimation: () => void;
12
- translateX: Animated.Value,
13
13
  scrollEnabled: boolean;
14
14
  startPagingAnimation: StartPagingAnimation;
15
15
  }
@@ -40,7 +40,7 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
40
40
  autoplayController,
41
41
  children,
42
42
  interruptAnimation,
43
- translateX,
43
+ gestureTranslationX,
44
44
  scrollEnabled,
45
45
  startPagingAnimation,
46
46
  } = props;
@@ -54,7 +54,7 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
54
54
  }, [interruptAnimation, pauseAutoplay]);
55
55
 
56
56
  const handleGestureEvent = useCallback(Animated.event(
57
- [{ nativeEvent: { translationX: translateX } }],
57
+ [{ nativeEvent: { translationX: gestureTranslationX } }],
58
58
  { useNativeDriver: true },
59
59
  ), []);
60
60
 
@@ -68,7 +68,11 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
68
68
 
69
69
  startPagingAnimation(
70
70
  'directional',
71
- { direction: direction, isOriginatedFromGesture: true },
71
+ {
72
+ direction: direction,
73
+ isOriginatedFromGesture: true,
74
+ lastGestureTranslationX: translationX,
75
+ },
72
76
  );
73
77
 
74
78
  resumeAutoplay();
@@ -1,5 +1,5 @@
1
1
  export { default as useAutoplayController } from './useAutoplayController';
2
2
  export { default as useIndexController } from './useIndexController';
3
3
  export { default as useLoopedData } from './useLoopedData';
4
- export { default as usePagingAnimation } from './usePagingAnimation';
4
+ export { default as usePagingAnimator } from './usePagingAnimator';
5
5
  export { default as useItemVisibilityStore } from './useItemVisibilityStore';
@@ -1,7 +1,7 @@
1
1
  import { useCallback } from 'react';
2
2
  import { useImperativeState } from '@fountain-ui/core';
3
3
  import { mod } from '@fountain-ui/utils';
4
- import type { AnimationState, IndexController } from '../types';
4
+ import type { IndexController, ScrollStateChangeEvent } from '../types';
5
5
 
6
6
  export interface UseIndexControllerParameters {
7
7
  initialIndex: number;
@@ -25,41 +25,33 @@ export default function useIndexController(params: UseIndexControllerParameters)
25
25
  const currentIndex = useImperativeState(initialIndex);
26
26
  const currentPosition = useImperativeState(initialIndex);
27
27
 
28
- const notifyAnimationState = useCallback((state: AnimationState) => {
29
- if (state === 'finished' || state === 'interrupted') {
28
+ const notifyScrollStateHasChanged = useCallback(({ offset, state }: ScrollStateChangeEvent) => {
29
+ const normalized = -Math.round(offset / itemWidth);
30
+ const index = Math.floor(mod(normalized, numberOfOriginalData));
31
+ const position = Math.floor(mod(normalized, numberOfData));
32
+
33
+ if (state === 'idle' || state === 'interrupted') {
30
34
  if (currentIndex.hasChanged()) {
31
35
  onIndexChange?.(currentIndex.get());
32
36
  }
33
-
34
37
  if (currentPosition.hasChanged()) {
35
38
  onPositionChange?.(currentPosition.get());
36
39
  }
37
40
  }
38
- }, [
39
- onIndexChange,
40
- onPositionChange,
41
- ]);
42
-
43
- const notifyOffsetHasChanged = useCallback((offset: number) => {
44
- const roundedOffset = Math.round(offset / itemWidth) * itemWidth;
45
-
46
- // To prevent floating point problem, make sure index is integer type.
47
- const nextIndex = Math.floor(mod((-roundedOffset / itemWidth), numberOfOriginalData));
48
- currentIndex.set(nextIndex);
49
41
 
50
- // To prevent floating point problem, make sure index is integer type.
51
- const nextPosition = Math.floor(mod((-roundedOffset / itemWidth), numberOfData));
52
- currentPosition.set(nextPosition);
42
+ currentIndex.set(index);
43
+ currentPosition.set(position);
53
44
  }, [
54
45
  itemWidth,
55
46
  numberOfData,
56
47
  numberOfOriginalData,
48
+ onIndexChange,
49
+ onPositionChange,
57
50
  ]);
58
51
 
59
52
  return {
60
53
  getCurrentIndex: currentIndex.get,
61
54
  lastIndex: numberOfOriginalData - 1,
62
- notifyAnimationState,
63
- notifyOffsetHasChanged,
55
+ notifyScrollStateHasChanged,
64
56
  };
65
57
  };