subtle avatar grow animation (#5480)
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {StyleProp, View, ViewStyle} from 'react-native'
|
||||||
|
import Animated, {
|
||||||
|
Extrapolation,
|
||||||
|
interpolate,
|
||||||
|
SharedValue,
|
||||||
|
useAnimatedStyle,
|
||||||
|
} from 'react-native-reanimated'
|
||||||
|
|
||||||
|
import {isIOS} from '#/platform/detection'
|
||||||
|
import {usePagerHeaderContext} from '#/view/com/pager/PagerHeaderContext'
|
||||||
|
|
||||||
|
export function GrowableAvatar({
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
}) {
|
||||||
|
const pagerContext = usePagerHeaderContext()
|
||||||
|
|
||||||
|
// pagerContext should only be present on iOS, but better safe than sorry
|
||||||
|
if (!pagerContext || !isIOS) {
|
||||||
|
return <View style={style}>{children}</View>
|
||||||
|
}
|
||||||
|
|
||||||
|
const {scrollY} = pagerContext
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GrowableAvatarInner scrollY={scrollY} style={style}>
|
||||||
|
{children}
|
||||||
|
</GrowableAvatarInner>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function GrowableAvatarInner({
|
||||||
|
scrollY,
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
scrollY: SharedValue<number>
|
||||||
|
children: React.ReactNode
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
}) {
|
||||||
|
const animatedStyle = useAnimatedStyle(() => ({
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
scale: interpolate(scrollY.value, [-150, 0], [1.2, 1], {
|
||||||
|
extrapolateRight: Extrapolation.CLAMP,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View
|
||||||
|
style={[style, {transformOrigin: 'bottom left'}, animatedStyle]}>
|
||||||
|
{children}
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import {UserBanner} from '#/view/com/util/UserBanner'
|
|||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {LabelsOnMe} from '#/components/moderation/LabelsOnMe'
|
import {LabelsOnMe} from '#/components/moderation/LabelsOnMe'
|
||||||
import {ProfileHeaderAlerts} from '#/components/moderation/ProfileHeaderAlerts'
|
import {ProfileHeaderAlerts} from '#/components/moderation/ProfileHeaderAlerts'
|
||||||
|
import {GrowableAvatar} from './GrowableAvatar'
|
||||||
import {GrowableBanner} from './GrowableBanner'
|
import {GrowableBanner} from './GrowableBanner'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -119,27 +120,29 @@ let ProfileHeaderShell = ({
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TouchableWithoutFeedback
|
<GrowableAvatar style={styles.aviPosition}>
|
||||||
testID="profileHeaderAviButton"
|
<TouchableWithoutFeedback
|
||||||
onPress={onPressAvi}
|
testID="profileHeaderAviButton"
|
||||||
accessibilityRole="image"
|
onPress={onPressAvi}
|
||||||
accessibilityLabel={_(msg`View ${profile.handle}'s avatar`)}
|
accessibilityRole="image"
|
||||||
accessibilityHint="">
|
accessibilityLabel={_(msg`View ${profile.handle}'s avatar`)}
|
||||||
<View
|
accessibilityHint="">
|
||||||
style={[
|
<View
|
||||||
t.atoms.bg,
|
style={[
|
||||||
{borderColor: t.atoms.bg.backgroundColor},
|
t.atoms.bg,
|
||||||
styles.avi,
|
{borderColor: t.atoms.bg.backgroundColor},
|
||||||
profile.associated?.labeler && styles.aviLabeler,
|
styles.avi,
|
||||||
]}>
|
profile.associated?.labeler && styles.aviLabeler,
|
||||||
<UserAvatar
|
]}>
|
||||||
type={profile.associated?.labeler ? 'labeler' : 'user'}
|
<UserAvatar
|
||||||
size={90}
|
type={profile.associated?.labeler ? 'labeler' : 'user'}
|
||||||
avatar={profile.avatar}
|
size={90}
|
||||||
moderation={moderation.ui('avatar')}
|
avatar={profile.avatar}
|
||||||
/>
|
moderation={moderation.ui('avatar')}
|
||||||
</View>
|
/>
|
||||||
</TouchableWithoutFeedback>
|
</View>
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
</GrowableAvatar>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -168,10 +171,12 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
avi: {
|
aviPosition: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 110,
|
top: 110,
|
||||||
left: 10,
|
left: 10,
|
||||||
|
},
|
||||||
|
avi: {
|
||||||
width: 94,
|
width: 94,
|
||||||
height: 94,
|
height: 94,
|
||||||
borderRadius: 47,
|
borderRadius: 47,
|
||||||
|
|||||||
Reference in New Issue
Block a user