react-native-refresh-loadmore-recyclerlistview
Version:
The listview that you need and deserve. It was built for performance, uses cell recycling to achieve smooth scrolling.
82 lines (72 loc) • 3.46 kB
text/typescript
import { Animated, Easing, View, Platform } from "react-native";
import { BaseItemAnimator } from "../../../../core/ItemAnimator";
interface UnmountAwareView extends View {
_isUnmountedForRecyclerListView?: boolean;
_lastAnimVal?: Animated.ValueXY | null;
}
const IS_WEB = Platform.OS === "web";
/**
* Default implementation of RLV layout animations for react native. These ones are purely JS driven. Also, check out DefaultNativeItemAnimator
* for an implementation on top of LayoutAnimation. We didn't use it by default due the fact that LayoutAnimation is quite
* unstable on Android and to avoid unnecessary interference with developer flow. It would be very easy to do so manually if
* you need to. Check DefaultNativeItemAnimator for inspiration. LayoutAnimation definitely gives better performance but is
* hardly customizable.
*/
export class DefaultJSItemAnimator implements BaseItemAnimator {
public shouldAnimateOnce: boolean = true;
private _hasAnimatedOnce: boolean = false;
private _isTimerOn: boolean = false;
public animateWillMount(atX: number, atY: number, itemIndex: number): void {
//no need
}
public animateDidMount(atX: number, atY: number, itemRef: object, itemIndex: number): void {
//no need
}
public animateWillUpdate(fromX: number, fromY: number, toX: number, toY: number, itemRef: object, itemIndex: number): void {
this._hasAnimatedOnce = true;
}
public animateShift(fromX: number, fromY: number, toX: number, toY: number, itemRef: object, itemIndex: number): boolean {
if (fromX !== toX || fromY !== toY) {
if (!this.shouldAnimateOnce || this.shouldAnimateOnce && !this._hasAnimatedOnce) {
const viewRef = itemRef as UnmountAwareView,
animXY = new Animated.ValueXY({ x: fromX, y: fromY });
animXY.addListener((value) => {
if (viewRef._isUnmountedForRecyclerListView) {
animXY.stopAnimation();
return;
}
viewRef.setNativeProps(this._getNativePropObject(value.x, value.y));
});
if (viewRef._lastAnimVal) {
viewRef._lastAnimVal.stopAnimation();
}
viewRef._lastAnimVal = animXY;
Animated.timing(animXY, {
toValue: { x: toX, y: toY },
duration: 200,
easing: Easing.out(Easing.ease),
useNativeDriver: BaseItemAnimator.USE_NATIVE_DRIVER,
}).start(() => {
viewRef._lastAnimVal = null;
this._hasAnimatedOnce = true;
});
return true;
}
} else if (!this._isTimerOn) {
this._isTimerOn = true;
if (!this._hasAnimatedOnce) {
setTimeout(() => {
this._hasAnimatedOnce = true;
}, 1000);
}
}
return false;
}
public animateWillUnmount(atX: number, atY: number, itemRef: object, itemIndex: number): void {
(itemRef as UnmountAwareView)._isUnmountedForRecyclerListView = true;
}
private _getNativePropObject(x: number, y: number): object {
const point = { left: x, top: y };
return !IS_WEB ? point : { style: point };
}
}