Skip to content

Commit

Permalink
feat: add new props to control or get information for carousel animation
Browse files Browse the repository at this point in the history
re #285
  • Loading branch information
dohooo committed Oct 19, 2022
1 parent f222427 commit 2a39346
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 61 deletions.
6 changes: 3 additions & 3 deletions docs/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This is how it works in code.
1. First we need a unit `size` to help us calculate the scroll distance. When horizontal, `size` is equal to the `width` prop setting, when vertical, `size` is equal to the `height` prop setting.

2. Then we need a value `handlerOffsetX`, which is the current scroll distance, and it's a total value, if we scroll two, `handlerOffsetX` is equal to `size` * 2, if we scroll ten, `handlerOffsetX` is equal to `size` * 10.
2. Then we need a value `handlerOffset`, which is the current scroll distance, and it's a total value, if we scroll two, `handlerOffset` is equal to `size` * 2, if we scroll ten, `handlerOffset` is equal to `size` * 10.

3. Followed by dealing with how to get at the end of the element at the right time to move to the front, this part of logic in `./src/hooks/useOffsetX.ts`. First we need to know the current window size (the total number of elements rendered on one side). The window size defaults to half the total number of elements, i.e. full render.
![steps-6](./assets/steps-6.jpg)
Expand Down Expand Up @@ -56,7 +56,7 @@ The above logic is translated into code as follows:
startPos,
];
return interpolate(
handlerOffsetX.value,
handlerOffset.value,
inputRange,
outputRange,
Extrapolate.CLAMP
Expand All @@ -69,7 +69,7 @@ const inputRange = [-1, 0 ,1]
const outputRange = [-size, 0 ,size]
return {
transform: [
{ translateX: interpolate(handlerOffsetX.value, inputRange, outputRange) },
{ translateX: interpolate(handlerOffset.value, inputRange, outputRange) },
],
}
```
Expand Down
6 changes: 3 additions & 3 deletions docs/about.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
1. 首先我们需要一个单位`size`,它用来帮我们计算滚动距离,当水平时`size`等于`width` prop的设置,当垂直时`size`等于`height` prop的设置。

2. 其次我们需要一个值`handlerOffsetX`,用来记录当前的滚动距离,这是一个总值,当我们滚动两张,那`handlerOffsetX`等于size * 2,如果滚动十张那`handlerOffsetX`等于size * 10。
2. 其次我们需要一个值`handlerOffset`,用来记录当前的滚动距离,这是一个总值,当我们滚动两张,那`handlerOffset`等于size * 2,如果滚动十张那`handlerOffset`等于size * 10。

3. 紧接着是处理如何让末尾的元素在合适的时候挪动到最前面,这部分逻辑在`./src/hooks/useOffsetX.ts`中。首先我们需要知道目前的视窗大小(一侧元素渲染的总数量),视窗大小默认为元素总数量的一半,即全量渲染。
![steps-6](./assets/steps-6.jpg)
Expand Down Expand Up @@ -56,7 +56,7 @@
startPos,
];
return interpolate(
handlerOffsetX.value,
handlerOffset.value,
inputRange,
outputRange,
Extrapolate.CLAMP
Expand All @@ -69,7 +69,7 @@ const inputRange = [-1, 0 ,1]
const outputRange = [-size, 0 ,size]
return {
transform: [
{ translateX: interpolate(handlerOffsetX.value, inputRange, outputRange) },
{ translateX: interpolate(handlerOffset.value, inputRange, outputRange) },
],
}
```
Expand Down
3 changes: 2 additions & 1 deletion docs/props.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
| name | required | default | types | description |
| ----------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| data || | T[] | Carousel items data set |
| renderItem || | (info: { data: T, index: number, animationValue: SharedValue\<number> }) => React.ReactElement | Render carousel item |
| renderItem || | (info: { item: T, index: number, animationValue: SharedValue\<number> }) => React.ReactElement | Render carousel item |
| defaultScrollOffsetValue|| useSharedValue<number>(0) | boolean | The default animated value of the carousel. |
| autoFillData || true | boolean | Auto fill data array to allow loop playback when the loop props is true.([1] => [1, 1, 1][1, 2] => [1, 2, 1, 2]) |
| vertical || false | boolean | Layout items vertically instead of horizontally |
| width | vertical ❌ horizontal ✅ | '100%' | number \| undefined | Specified carousel item width |
Expand Down
3 changes: 2 additions & 1 deletion docs/props.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
| name | required | default | types | description |
| ----------------------- | ------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| data || | T[] | 即将渲染的数据集合 |
| renderItem || | (info: { data: T, index: number, animationValue: SharedValue\<number> }) => React.ReactElement | 渲染元素的方法 |
| renderItem || | (info: { item: T, index: number, animationValue: SharedValue\<number> }) => React.ReactElement | 渲染元素的方法 |
| defaultScrollOffsetValue|| useSharedValue<number>(0) | boolean | 轮播图的默认动画值 |
| autoFillData || true | boolean | 将会在`loop`属性设置为 true 时,自动填充 data 元素以满足 loop 循环效果([1] => [1, 1, 1][1, 2] => [1, 2, 1, 2]) |
| vertical || false | boolean | 将元素垂直布局而不是水平 |
| width | 垂直时 ❌ 水平时 ✅ | '100%' | number \| undefined | 指定每一项的宽度 |
Expand Down
14 changes: 7 additions & 7 deletions src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
} = props;

const commonVariables = useCommonVariables(props);
const { size, handlerOffsetX } = commonVariables;
const { size, handlerOffset } = commonVariables;
const dataLength = data.length;

const offsetX = useDerivedValue(() => {
const totalSize = size * dataLength;
const x = handlerOffsetX.value % totalSize;
const x = handlerOffset.value % totalSize;

if (!loop) {
return handlerOffsetX.value;
return handlerOffset.value;
}
return isNaN(x) ? 0 : x;
}, [loop, size, dataLength]);
Expand All @@ -76,7 +76,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
size,
data,
autoFillData,
handlerOffsetX,
handlerOffset,
withAnimation,
defaultIndex,
onScrollEnd: () => runOnJS(_onScrollEnd)(),
Expand Down Expand Up @@ -151,7 +151,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
const visibleRanges = useVisibleRanges({
total: data.length,
viewSize: size,
translation: handlerOffsetX,
translation: handlerOffset,
windowSize,
});

Expand All @@ -170,7 +170,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
<BaseLayout
key={i}
index={i}
handlerOffsetX={offsetX}
handlerOffset={offsetX}
visibleRanges={visibleRanges}
animationStyle={customAnimation || layoutConfig}
>
Expand Down Expand Up @@ -201,7 +201,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
<ScrollViewGesture
key={mode}
size={size}
translation={handlerOffsetX}
translation={handlerOffset}
style={[
styles.container,
{
Expand Down
51 changes: 24 additions & 27 deletions src/hooks/useCarouselController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface IOpts {
size: number;
data: TCarouselProps['data'];
autoFillData: TCarouselProps['autoFillData'];
handlerOffsetX: Animated.SharedValue<number>;
handlerOffset: Animated.SharedValue<number>;
withAnimation?: TCarouselProps['withAnimation'];
duration?: number;
defaultIndex?: number;
Expand All @@ -41,7 +41,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
size,
data,
loop,
handlerOffsetX,
handlerOffset,
withAnimation,
defaultIndex = 0,
duration,
Expand All @@ -64,26 +64,26 @@ export function useCarouselController(options: IOpts): ICarouselController {

const currentFixedPage = React.useCallback(() => {
if (loop) {
return -Math.round(handlerOffsetX.value / size);
return -Math.round(handlerOffset.value / size);
}

const fixed = (handlerOffsetX.value / size) % dataInfo.length;
const fixed = (handlerOffset.value / size) % dataInfo.length;
return Math.round(
handlerOffsetX.value <= 0
handlerOffset.value <= 0
? Math.abs(fixed)
: Math.abs(fixed > 0 ? dataInfo.length - fixed : 0)
);
}, [handlerOffsetX, dataInfo, size, loop]);
}, [handlerOffset, dataInfo, size, loop]);

function setSharedIndex(newSharedIndex: number) {
sharedIndex.current = newSharedIndex;
}

useAnimatedReaction(
() => {
const handlerOffsetXValue = handlerOffsetX.value;
const toInt = round(handlerOffsetXValue / size) % dataInfo.length;
const isPositive = handlerOffsetXValue <= 0;
const handlerOffsetValue = handlerOffset.value;
const toInt = round(handlerOffsetValue / size) % dataInfo.length;
const isPositive = handlerOffsetValue <= 0;
const i = isPositive
? Math.abs(toInt)
: Math.abs(toInt > 0 ? dataInfo.length - toInt : 0);
Expand Down Expand Up @@ -112,7 +112,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
index,
loop,
autoFillData,
handlerOffsetX,
handlerOffset,
]
);

Expand Down Expand Up @@ -169,12 +169,12 @@ export function useCarouselController(options: IOpts): ICarouselController {
index.value = nextPage;

if (animated) {
handlerOffsetX.value = scrollWithTiming(
handlerOffset.value = scrollWithTiming(
-nextPage * size,
onFinished
) as any;
} else {
handlerOffsetX.value = -nextPage * size;
handlerOffset.value = -nextPage * size;
onFinished?.();
}
},
Expand All @@ -184,7 +184,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
index,
dataInfo,
onScrollBegin,
handlerOffsetX,
handlerOffset,
size,
scrollWithTiming,
currentFixedPage,
Expand All @@ -202,12 +202,12 @@ export function useCarouselController(options: IOpts): ICarouselController {
index.value = prevPage;

if (animated) {
handlerOffsetX.value = scrollWithTiming(
handlerOffset.value = scrollWithTiming(
-prevPage * size,
onFinished
);
} else {
handlerOffsetX.value = -prevPage * size;
handlerOffset.value = -prevPage * size;
onFinished?.();
}
},
Expand All @@ -216,7 +216,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
loop,
index,
onScrollBegin,
handlerOffsetX,
handlerOffset,
size,
scrollWithTiming,
currentFixedPage,
Expand All @@ -231,13 +231,13 @@ export function useCarouselController(options: IOpts): ICarouselController {

onScrollBegin?.();
// direction -> 1 | -1
const isPositiveZero = Object.is(handlerOffsetX.value, +0);
const isNegativeZero = Object.is(handlerOffsetX.value, -0);
const isPositiveZero = Object.is(handlerOffset.value, +0);
const isNegativeZero = Object.is(handlerOffset.value, -0);
const direction = isPositiveZero
? 1
: isNegativeZero
? -1
: Math.sign(handlerOffsetX.value);
: Math.sign(handlerOffset.value);

// target offset
const offset = i * size * direction;
Expand All @@ -248,25 +248,22 @@ export function useCarouselController(options: IOpts): ICarouselController {

if (loop) {
isCloseToNextLoop =
Math.abs(handlerOffsetX.value % totalSize) / totalSize >=
Math.abs(handlerOffset.value % totalSize) / totalSize >=
0.5;
}

const finalOffset =
(Math.floor(Math.abs(handlerOffsetX.value / totalSize)) +
(Math.floor(Math.abs(handlerOffset.value / totalSize)) +
(isCloseToNextLoop ? 1 : 0)) *
totalSize *
direction +
offset;

if (animated) {
index.value = i;
handlerOffsetX.value = scrollWithTiming(
finalOffset,
onFinished
);
handlerOffset.value = scrollWithTiming(finalOffset, onFinished);
} else {
handlerOffsetX.value = finalOffset;
handlerOffset.value = finalOffset;
index.value = i;
onFinished?.();
}
Expand All @@ -275,7 +272,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
index,
canSliding,
onScrollBegin,
handlerOffsetX,
handlerOffset,
size,
dataInfo.length,
loop,
Expand Down
22 changes: 15 additions & 7 deletions src/hooks/useCommonVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,33 @@ import type { TInitializeCarouselProps } from './useInitProps';
interface ICommonVariables {
size: number;
validLength: number;
handlerOffsetX: Animated.SharedValue<number>;
handlerOffset: Animated.SharedValue<number>;
}

export function useCommonVariables(
props: TInitializeCarouselProps<any>
): ICommonVariables {
const { vertical, height, width, data, defaultIndex } = props;
const {
vertical,
height,
width,
data,
defaultIndex,
defaultScrollOffsetValue,
} = props;
const size = vertical ? height : width;
const validLength = data.length - 1;
const defaultHandlerOffsetX = -Math.abs(defaultIndex * size);
const handlerOffsetX = useSharedValue<number>(defaultHandlerOffsetX);
const defaultHandlerOffsetValue = -Math.abs(defaultIndex * size);
const _handlerOffset = useSharedValue<number>(defaultHandlerOffsetValue);
const handlerOffset = defaultScrollOffsetValue ?? _handlerOffset;

React.useEffect(() => {
handlerOffsetX.value = defaultHandlerOffsetX;
}, [vertical, handlerOffsetX, defaultHandlerOffsetX]);
handlerOffset.value = defaultHandlerOffsetValue;
}, [vertical, handlerOffset, defaultHandlerOffsetValue]);

return {
size,
validLength,
handlerOffsetX,
handlerOffset,
};
}
Loading

0 comments on commit 2a39346

Please sign in to comment.