Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
() =>
// when the active state changes
Animated.cond(Animated.neq(lastIsActive, tabIsActive), [
Animated.set(lastIsActive, tabIsActive),
Animated.cond(
tabIsActive,
[
// the tab just became active so we might need to adjust the scroll offset to avoid unwanted
// white space before allowing the scroll offset to affect the header position
Animated.cond(
Animated.greaterThan(Animated.multiply(-1, headerOffsetY), scrollOffsetY),
Animated.call([headerOffsetY], ([y]) => {
if (!flatListRef.current) {
throw new Error(
"Please make sure that tab content is wrapped with a StickyTabPageFlatList or a StickyTabPageScrollView"
)
}
flatListRef.current.getNode().scrollToOffset({ offset: -y, animated: false })
lockHeaderPosition.setValue(0)
}),
Animated.set(lockHeaderPosition, 0)
),
],
Animated.set(lockHeaderPosition, 1)
),
]),
[]
export const runSpring = (clock, value, velocity) => {
const state = {
finished: new Animated.Value(0),
position: new Animated.Value(0),
velocity: new Animated.Value(0),
time: new Animated.Value(0),
};
const config = {
// When velocity is positive, zoom to snap point `1`, otheriwse
// target snap point `0`
toValue: Animated.cond(
Animated.greaterThan(velocity, 0),
new Animated.Value(1),
new Animated.Value(0)
),
// Some spring behaviour config:
damping: 7.5,
mass: 1.2,
stiffness: 121.6,
overshootClamping: true,
restSpeedThreshold: 0.001,
restDisplacementThreshold: 0.001,
};
return Animated.block([
// Reset state when we're starting the clock
Animated.cond(
Animated.not(Animated.clockRunning(clock)), [
() => {
// scrollDiff is the amount the header has scrolled since last time this code ran
const scrollDiff = Animated.diff(scrollOffsetY)
const upwardVelocityBreached = Animated.lessOrEq(scrollDiff, -SHOW_HEADER_VELOCITY)
const headerIsNotFullyUp = Animated.neq(headerOffsetY, -headerHeight)
const nearTheTop = Animated.lessOrEq(scrollOffsetY, headerHeight)
// this is the code which actually performs the update to headerOffsetY, according to which direction
// the scrolling is going
const updateHeaderOffset = Animated.cond(
Animated.greaterThan(scrollDiff, 0),
[
// y offset got bigger so scrolling down (content travels up the screen)
// move the header up (hide it) unconditionally
Animated.set(headerOffsetY, Animated.max(-headerHeight, Animated.sub(headerOffsetY, scrollDiff))),
],
[
// y offset got smaller so scrolling up (content travels down the screen)
// if velocity is high enough or we're already moving the header up or we're near the top of the scroll view
// then move the header down (show it)
Animated.cond(Animated.or(upwardVelocityBreached, headerIsNotFullyUp, nearTheTop), [
Animated.set(headerOffsetY, Animated.min(0, Animated.sub(headerOffsetY, scrollDiff))),
]),
]
)
// we don't want to manipulate the header position while bouncing at the top or the bottom of the scroll view
Animated.set(headerOffsetY, Animated.max(negative(headerHeight), Animated.sub(headerOffsetY, scrollDiff))),
],
[
// y offset got smaller so scrolling up (content travels down the screen)
// if velocity is high enough or we're already moving the header up or we're near the top of the scroll view
// then move the header down (show it)
Animated.set(amountScrolledUpward, Animated.add(amountScrolledUpward, Animated.abs(scrollDiff))),
Animated.cond(Animated.or(upwardScrollThresholdBreached, headerIsNotFullyUp, nearTheTop), [
Animated.set(headerOffsetY, Animated.min(0, Animated.sub(headerOffsetY, scrollDiff))),
]),
]
)
// we don't want to manipulate the header position while bouncing at the top or the bottom of the scroll view
// cause it feels weeeird
const notBouncingAtTheTop = Animated.greaterThan(scrollOffsetY, 0)
const notBouncingAtTheBottom = Animated.lessThan(scrollOffsetY, Animated.sub(contentHeight, layoutHeight))
const updateHeaderOffsetWhenNotBouncing = Animated.cond(
Animated.and(notBouncingAtTheTop, notBouncingAtTheBottom),
updateHeaderOffset,
[
Animated.cond(
notBouncingAtTheTop,
[
// bouncing at the bottom,
// normally the header will be fully up at this point but sometimes
// the content is not tall enough to cause that, so we still need to
// update the header position just like above. The only difference is that
// when the bounce snaps back, we don't want to trigger opening the header
// like we do when the user explicitly scrolls back upward.
Animated.cond(
// move the header up (hide it) unconditionally
Animated.set(headerOffsetY, Animated.max(-headerHeight, Animated.sub(headerOffsetY, scrollDiff))),
],
[
// y offset got smaller so scrolling up (content travels down the screen)
// if velocity is high enough or we're already moving the header up or we're near the top of the scroll view
// then move the header down (show it)
Animated.cond(Animated.or(upwardVelocityBreached, headerIsNotFullyUp, nearTheTop), [
Animated.set(headerOffsetY, Animated.min(0, Animated.sub(headerOffsetY, scrollDiff))),
]),
]
)
// we don't want to manipulate the header position while bouncing at the top or the bottom of the scroll view
// cause it feels weeeird
const notBouncingAtTheTop = Animated.greaterThan(scrollOffsetY, 0)
const notBouncingAtTheBottom = Animated.lessThan(scrollOffsetY, Animated.sub(contentHeight, layoutHeight))
const updateHeaderOffsetWhenNotBouncingOrLocked = Animated.cond(
Animated.and(notBouncingAtTheTop, notBouncingAtTheBottom, Animated.not(lockHeaderPosition)),
updateHeaderOffset,
// deref scroll diff to prevent diff buildup when ignoring changes
scrollDiff
)
// on first eval (when the component mounts) the scroll values will be nonsensical so ignore
const firstEval = new Animated.Value(1)
return Animated.cond(
firstEval,
[
Animated.set(firstEval, 0),
// again, deref scrollDiff to prevent buildup
const delta = (a0: Animated.Node, a: Animated.Node) => {
const da = sub(a0, a);
return cond(
greaterThan(abs(da), PI),
cond(greaterThan(a0, 0), sub(2 * PI, da), sub(-2 * PI, da)),
da
);
};
const styles = StyleSheet.create({
);
}
if (mathType === 'reanimated') {
return Animated.lessThan(left, right);
}
return Number((left as number) < (right as number));
}
if (tree.token.value === '>') {
if (Array.isArray(left) || Array.isArray(right)) {
throw new InvalidExpressionError(
`${logPrefix} Cannot use operator ">" on array`
);
}
if (mathType === 'reanimated') {
return Animated.greaterThan(left, right);
}
return Number((left as number) > (right as number));
}
if (tree.token.value === '>=') {
if (Array.isArray(left) || Array.isArray(right)) {
throw new InvalidExpressionError(
`${logPrefix} Cannot use operator ">=" on array`
);
}
if (mathType === 'reanimated') {
return Animated.greaterOrEq(left, right);
}
return Number((left as number) >= (right as number));
}