-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: dont scroll if content fits in width #4
Comments
A little AutoMarquee Helper: import { memo, useEffect, useState } from 'react'
import { StyleSheet, Text, TextStyle, type ViewStyle } from 'react-native'
import { View } from 'react-native'
import Animated, {
type SharedValue,
runOnJS,
useAnimatedReaction,
useAnimatedStyle,
useFrameCallback,
useSharedValue,
} from 'react-native-reanimated'
const AnimatedChild = ({
index,
children,
anim,
textWidth,
spacing,
}: React.PropsWithChildren<{
index: number
anim: SharedValue<number>
textWidth: SharedValue<number>
spacing: number
}>) => {
const stylez = useAnimatedStyle(() => {
return {
position: 'absolute',
width: textWidth.value + spacing,
alignSelf: 'flex-start',
left: index * (textWidth.value + spacing),
transform: [
{
translateX: -(anim.value % (textWidth.value + spacing)),
},
],
}
}, [index, spacing, textWidth])
return <Animated.View style={stylez}>{children}</Animated.View>
}
export type MarqueeProps = React.PropsWithChildren<{
speed?: number
spacing?: number
style?: ViewStyle
}>
/**
* Used to animate the given children in a horizontal manner.
*/
export const Marquee = memo(({ speed = 1, children, spacing = 0, style }: MarqueeProps) => {
const parentWidth = useSharedValue(0)
const textWidth = useSharedValue(0)
const [cloneTimes, setCloneTimes] = useState(0)
const anim = useSharedValue(0)
useFrameCallback(() => {
anim.value += speed
}, true)
useAnimatedReaction(
() => {
if (textWidth.value === 0 || parentWidth.value === 0) {
return 0
}
return Math.round(parentWidth.value / textWidth.value) + 1
},
(v) => {
if (v === 0) {
return
}
// This is going to cover the case when the text/element size
// is greater than the actual parent size
// Double this to cover the entire screen twice, in this way we can
// reset the position of the first element when its going to move out
// of the screen without any noticible glitch
runOnJS(setCloneTimes)(v * 2)
},
[]
)
return (
<Animated.View
style={style}
onLayout={(ev) => {
parentWidth.value = ev.nativeEvent.layout.width
}}
pointerEvents="box-none"
>
<Animated.View style={styles.row} pointerEvents="box-none">
{
// We are adding the text inside a ScrollView because in this way we
// ensure that its not going to "wrap".
}
<Animated.ScrollView horizontal style={styles.hidden} pointerEvents="box-none">
<View
onLayout={(ev) => {
textWidth.value = ev.nativeEvent.layout.width
}}
>
{children}
</View>
</Animated.ScrollView>
{cloneTimes > 0 &&
[...Array(cloneTimes).keys()].map((index) => {
return (
<AnimatedChild
key={`clone-${index}`}
index={index}
anim={anim}
textWidth={textWidth}
spacing={spacing}
>
{children}
</AnimatedChild>
)
})}
</Animated.View>
</Animated.View>
)
})
const styles = StyleSheet.create({
hidden: { opacity: 0, zIndex: -9999 },
row: { flexDirection: 'row', overflow: 'hidden' },
})
export const AutoMarquee = memo(
(
props: Omit<MarqueeProps, 'children' | 'style'> & {
children: string
className?: string
style?: TextStyle
}
) => {
const [textWidth, setTextWidth] = useState(0)
const [viewWidth, setViewWidth] = useState(0)
useEffect(() => {
console.log('textWidth', textWidth)
console.log('viewWidth', viewWidth)
}, [textWidth, viewWidth])
return (
<View className="w-full" onLayout={(e) => setViewWidth(e.nativeEvent.layout.width)}>
<View
style={{ height: 0, alignSelf: 'flex-start' }}
onLayout={(e) => {
setTextWidth(e.nativeEvent.layout.width)
}}
>
<Text style={{ width: '100%' }} className={props.className} children={props.children} />
</View>
{textWidth < viewWidth ? (
<Text style={props.style} className={props.className} children={props.children} />
) : (
<Marquee {...{ ...props, style: undefined, children: undefined, textWidth }}>
<Text style={props.style} className={props.className} children={props.children} />
</Marquee>
)}
</View>
)
}
) |
@deadlinecode Thank you, the AutoMarquee works perfectly in my case. And it is smart. I just wonder is this a common way to measure layout in prior in RN? |
I mostly do it Like that by using onLayout |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
only scroll if scrolling is neccessary
The text was updated successfully, but these errors were encountered: