import Animated from "react-native-reanimated"; import { State as GestureState } from "react-native-gesture-handler"; let { or, set, cond, add, sub, block, eq, neq, and, divide, greaterThan, greaterOrEq, not, proc, Value, spring, lessThan, lessOrEq, multiply } = Animated; if (!proc) { console.warn("Use reanimated > 1.3 for optimal perf"); const procStub = (cb: any) => cb; proc = procStub; } export const getIsAfterActive = proc( (currentIndex: Animated.Node, activeIndex: Animated.Node) => greaterThan(currentIndex, activeIndex) ); export const getCellStart = proc( ( isAfterActive: Animated.Node, offset: Animated.Node, activeCellSize: Animated.Node, scrollOffset: Animated.Node ) => sub(cond(isAfterActive, sub(offset, activeCellSize), offset), scrollOffset) ); export const getOnChangeTranslate = proc( ( translate: Animated.Node, isAfterActive: Animated.Node, initialized: Animated.Value, toValue: Animated.Value, isPressedIn: Animated.Node ) => block([ cond(or(not(isAfterActive), initialized), [], set(initialized, 1)), cond(isPressedIn, set(toValue, translate)) ]) ); export const hardReset = proc( ( position: Animated.Value, finished: Animated.Value, time: Animated.Value, toValue: Animated.Value ) => block([set(position, 0), set(finished, 0), set(time, 0), set(toValue, 0)]) ); export const setupCell = proc( ( currentIndex: Animated.Node, initialized: Animated.Value, size: Animated.Node, offset: Animated.Node, isAfterActive: Animated.Value, translate: Animated.Value, prevTrans: Animated.Value, prevSpacerIndex: Animated.Value, activeIndex: Animated.Node, activeCellSize: Animated.Node, hoverOffset: Animated.Node, scrollOffset: Animated.Node, isHovering: Animated.Node, hoverTo: Animated.Value, hasMoved: Animated.Value, spacerIndex: Animated.Value, toValue: Animated.Value, position: Animated.Value, time: Animated.Value, finished: Animated.Value, runSpring: Animated.Node, onHasMoved: Animated.Node, onChangeSpacerIndex: Animated.Node, onFinished: Animated.Node, isPressedIn: Animated.Node ) => block([ set(isAfterActive, getIsAfterActive(currentIndex, activeIndex)), // Determining spacer index is hard to visualize. // see diagram here: https://i.imgur.com/jRPf5t3.jpg cond( isPressedIn, cond( isAfterActive, [ cond( and( greaterOrEq(add(hoverOffset, activeCellSize), offset), lessThan( add(hoverOffset, activeCellSize), add(offset, divide(size, 2)) ) ), set(spacerIndex, sub(currentIndex, 1)) ), cond( and( greaterOrEq( add(hoverOffset, activeCellSize), add(offset, divide(size, 2)) ), lessThan(add(hoverOffset, activeCellSize), add(offset, size)) ), set(spacerIndex, currentIndex) ) ], cond(lessThan(currentIndex, activeIndex), [ cond( and( lessThan(hoverOffset, add(offset, size)), greaterOrEq(hoverOffset, add(offset, divide(size, 2))) ), set(spacerIndex, add(currentIndex, 1)) ), cond( and( greaterOrEq(hoverOffset, offset), lessThan(hoverOffset, add(offset, divide(size, 2))) ), set(spacerIndex, currentIndex) ) ]) ) ), // Translate cell down if it is before active index and active cell has passed it. // Translate cell up if it is after the active index and active cell has passed it. cond( neq(currentIndex, activeIndex), set( translate, cond( cond( isAfterActive, lessOrEq(currentIndex, spacerIndex), greaterOrEq(currentIndex, spacerIndex) ), cond( isHovering, cond(isAfterActive, multiply(activeCellSize, -1), activeCellSize), 0 ), 0 ) ) ), // Set value hovering element will snap to once released cond( and(isHovering, eq(spacerIndex, currentIndex)), set( hoverTo, sub( offset, scrollOffset, cond(isAfterActive, sub(activeCellSize, size)) // Account for cells of differing size ) ) ), set(toValue, translate), cond(and(isPressedIn, neq(translate, prevTrans)), [ set(prevTrans, translate), getOnChangeTranslate( translate, isAfterActive, initialized, toValue, isPressedIn ), cond(hasMoved, onHasMoved, set(position, translate)) ]), cond(neq(prevSpacerIndex, spacerIndex), [ set(prevSpacerIndex, spacerIndex), cond(eq(spacerIndex, -1), [ // Hard reset to prevent stale state bugs onChangeSpacerIndex, hardReset(position, finished, time, toValue) ]) ]), runSpring, cond(finished, [onFinished, set(time, 0), set(finished, 0)]), position ]) ); const betterSpring = proc( ( finished: Animated.Value, velocity: Animated.Value, position: Animated.Value, time: Animated.Value, prevPosition: Animated.Value, toValue: Animated.Value, damping: Animated.Value, mass: Animated.Value, stiffness: Animated.Value, overshootClamping: Animated.SpringConfig["overshootClamping"], restSpeedThreshold: Animated.Value, restDisplacementThreshold: Animated.Value, clock: Animated.Clock ) => spring( clock, { finished, velocity, position, time, // @ts-ignore -- https://github.com/software-mansion/react-native-reanimated/blob/master/src/animations/spring.js#L177 prevPosition }, { toValue, damping, mass, stiffness, overshootClamping, restDisplacementThreshold, restSpeedThreshold } ) ); export function springFill( clock: Animated.Clock, state: Animated.SpringState, config: Animated.SpringConfig ) { return betterSpring( state.finished, state.velocity, state.position, state.time, new Value(0), config.toValue, config.damping, config.mass, config.stiffness, //@ts-ignore config.overshootClamping, config.restSpeedThreshold, config.restDisplacementThreshold, clock ); }