Different page transition in framer motion react - javascript

I want to set up different transition pages to the left and down and right sides of my Main component that have all the components like [this website][1]
when you click Blog its gonna go right and when click work it'll be going left, I fully complete the animation code but I don't know how can I set it.
down :
initial={{opacity: 0 }}
animate={{opacity: 1, y: 0}}
exit={{opacity: 0, y: '-100vh'}}
transition={{ease:"circOut",type:"tween",duration: 0.5}}
right :
initial={{opacity: 0}}
animate={{opacity: 1, x: 0}}
exit={{opacity: 1, x: '100vw'}}
transition={{ease:"circOut",type:"tween",duration: 1}}
my Main component:
const Main = (props) => {
const [click, setClick] = useState(false)
const handleClick = () => setClick(!click)
return (
<MainContainer
initial={{opacity: 1 } : {opacity: 0}}
animate={{opacity: 1, x: 0}}
exit={{opacity: 1, x: '100vw'}}
transition={{ease:"circOut",type:"tween",duration: 1}}
>
<DarkDiv click={click} />
<Container>
<PowerButton />
<LogoComponent theme={click ? 'dark' : 'light' } />
<SocialIcons theme={click ? 'dark' : 'light'} />
<Center click={click}>
<YinYang onClick={() => handleClick()} width={click ? 120 : 200}
height={click ? 120 : 200} fill='currentColor' />
<span>Click Here</span>
</Center>
<Contact href='mailto:9kbabouziane#gmail.com' rel="noreferrer"
target='_blank'>
<motion.h2>
Say hi..
</motion.h2>
</Contact>
<Blog to =''>
<motion.h2
onClick={() => toast("Coming Soon.." , {
duration: 3000,
position: 'top-center',
style: {background : "#FCF6F4",
color: '#000'},
})}
>
Blog
</motion.h2>
</Blog>
<Toaster/>
<Work to='/work' click={click}>
<motion.h2>
Work
</motion.h2>
</Work>
<BottomBar>
<About to='/about' click={click} >
<motion.h2 >
About.
</motion.h2>
</About>
<Skills to='/skills' click={click}>
<motion.h2>
My Skills.
</motion.h2>
</Skills>
</BottomBar>
</Container>
{click ? <Intro click={click} /> : null}
</MainContainer>
)
}
Now when I click all have the same animation, I want to make a condition when I click blog it'll go right and when I click work it'll go left like that website .. how can I make this, please?
[1]: https://react-portfolio-sigma.vercel.app/

Related

React Navigation | How do I change the buttons on a Tab Navigator from a child screen nested in a Stack Navigator?

I have a tab bar that looks like this:
The two side buttons are stack navigators (Learn and Journal) and the middle button needs to navigate the Journal Stack, and depending on what screen in the Journal Stack the user is on, it needs to say and do different things.
const Tab = createBottomTabNavigator();
const TabBarIcon = ({ icon, title, focused }) => {
return (
<View style={styles.iconContainer}>
<FontAwesomeIcon
icon={icon}
color={focused ? Colors.neutral[4] : Colors.neutral[6]}
size={24}
style={styles.icon}
/>
<Text style={[styles.iconText, focused && styles.iconTextFocused]}>
{title}
</Text>
</View>
);
};
const NullScreen = () => null;
const TabNavigator = () => {
return (
<Tab.Navigator
initialRouteName="Journal"
screenOptions={({ route }) => ({
...defaultNavOptions,
headerShown: false,
tabBarStyle: { backgroundColor: Colors.neutral[3] },
tabBarShowLabel: false,
})}
>
<Tab.Screen
name="Learn"
component={LearnStackNavigator}
options={{
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
title={'Learn'}
icon={faUserGraduate}
/>
),
}}
/>
<Tab.Screen
name="Null Screen"
component={NullScreen}
options={{
tabBarButton: ({ focused }) => (
<View
style={{
position: 'relative',
bottom: 25,
width: 80,
height: 80,
borderRadius: '50%',
backgroundColor: 'grey',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
shadowColor: 'black',
shadowOpacity: 0.3,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 3,
}}
>
<TouchableOpacity onPress={() => Alert.alert('hello world')}> // This is the button that I want use for useful things
<View style={[styles.iconContainer, styles.paddingBottom10]}>
<FontAwesomeIcon
icon={faPlus}
color={focused ? Colors.neutral[4] : Colors.neutral[6]}
size={32}
/>
<Text style={styles.iconText}>{'Add Sport'}</Text>
</View>
</TouchableOpacity>
</View>
),
}}
/>
<Tab.Screen
name="Journal"
component={LogbookStackNavigator}
options={{
tabBarIcon: ({ focused }) => (
<TabBarIcon focused={focused} title={'Journal'} icon={faPenAlt} />
),
}}
/>
</Tab.Navigator>
);
};
And here is what the LogbookStackNavigator looks like:
const LogbookStack = createStackNavigator();
const LogbookStackNavigator = () => {
return (
<LogbookStack.Navigator
screenOptions={{
...defaultNavOptions,
headerBackTitleVisible: false,
}}
>
<LogbookStack.Screen
name="Screen1"
component={screen1Component}
options={defaultNavOptions}
/>
<LogbookStack.Screen
name="Screen2"
component={screen2Component}
options={defaultNavOptions}
/>
<LogbookStack.Screen
name="Screen3"
component={screen3Component}
options={entryScreenOptions}
/>
<LogbookStack.Screen
name="Screen4"
component={screen4Component}
options={SaveLogbookScreenOptions}
/>
<LogbookStack.Screen
name="Screen5"
component={screen1Component5}
options={defaultNavOptions}
/>
</LogbookStack.Navigator>
);
};
I know how to use navigation.setOptions, but it only affects the immediate parent navigator, not the grandparent navigator.
Another thing I tried was to make the big circle button on the page itself, but it always rendered underneath the Tab Navigator. If there was a way to make it render above, I think I could just use that. I tried 'position: 'absolute', etc and it always rendered underneath the tab navigator. As it is, I had to basically make a dummy screen in the tab navigator to give me the button on top.
What I need to be able to do, is use big circle button on the Tab Navigator, to navigate to different screens in the LogbookStackNavigator. How do I do that?
Also, I need the title to change from "Add Sport" to "Add " depending on what screen the LogbookStackNavigator is on. How do I do that?
Thanks for your help
Finally figured this out. You have to use react-native-portalize. Just wrap the elements you want to be rendered on top in a
<Portal></Portal>. This will place it above a Bottom Tab navigator.
import { Portal } from 'react-native-portalize';
const FooterButton = () => {
return(
<Portal>
<View>
<Text>I appear above the Tab Navigator!</Text>
</View>
</Portal>
);
export default FooterButton;
Don't forget to wrap the whole app in the the Host:
//In app.js
import { Host } from 'react-native-portalize';
const App = () => {
return (
<Host>
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
</Host>
)
}
export default App;
NOTE: The elements inside the Portal, do not clear when the navigator navigates to another screen. So to get around this, you have to only display the Portal, when the screen is active. Thankfully React Navigation 5+ provides a useIsFocused hook that accomplishes this perfectly.
import { Portal } from 'react-native-portalize';
import { useIsFocused } from '#react-navigation/native';
const FooterButton = () => {
const isFocused = useIsFocused();
// Only display the button when screen is focused. Otherwise, it doesn't go away when you switch screens
return isFocused ? (
<Portal>
<View style={styles.buttonContainer}>
<View style={styles.footer}>{props.children}</View>
</View>
</Portal>
) : null;
};
export default FooterButton;
If you want a modal-style popup, you can wrap react-native-modalize and wrap it with react-native-modalize
Thanks to livin52 on Reddit for the solution

Is this a good way to implement button highlight in a <Pressable /> component in React-Native?

I am implementing a custom button that uses a <Pressable/> as it's base component.
//...
<Pressable
style={({ pressed }) => [
styles.button,
style,
// if button is disabled it gets grayed out
// if it's pressed, and user did not specify underlay color
// it uses default overlay color
// if it's not pressed, use either user-specified
// background color, or default
{
backgroundColor: isDisabled
? "#808080"
: pressed && underlayColor !== false
? underlayColor || styles.button.underlayColor
: style?.backgroundColor || styles.button.backgroundColor,
},
]}
//...
I feel like this is unreadable.
Furthermore, I have another custom button component that uses this component (custom-custom component, so to speak). If I pass an array as a style to it, the overlay feature won't work since it is expecting an object (style?.backgroundColor), not an array.
Here is the source code for both components:
Custom buttom:
const Button = ({
title,
containerStyle,
style,
onPress,
isDisabled,
textStyle,
icon,
iconContainerStyle,
underlayColor,
android_ripple,
}) => {
return (
<View style={[styles.container, containerStyle]}>
<Pressable
style={({ pressed }) => [
styles.button,
style,
{
backgroundColor: isDisabled
? "#808080"
: pressed && underlayColor !== false
? underlayColor || styles.button.underlayColor
: style?.backgroundColor || styles.button.backgroundColor,
},
]}
android_ripple={
android_ripple === true && { color: "rgba(0, 0, 0, 0.2)" }
}
disabled={isDisabled}
onPress={onPress}
>
<>
{/* show icon if specified */}
{icon ? (
<View style={[styles.icon, iconContainerStyle]}>{icon}</View>
) : null}
{/* show title if specified */}
{title && (
<Text
style={{
...styles.text,
...textStyle,
}}
>
{title}
</Text>
)}
</>
</Pressable>
</View>
);
};
export default Button;
Icon Button (custom-custom button):
export default function IconButton({
containerStyle,
style,
onPress,
isDisabled,
iconName,
iconType,
iconColor,
iconSize,
}) {
return (
<Button
isDisabled={isDisabled}
containerStyle={{ ...styles.container, ...containerStyle }}
/*[styles.button, style] will break the overlay color feature.. since in the custom <Button /> component, it is expecting an object, not an array*/
style={{ ...styles.button, ...style }}
onPress={onPress}
icon={
<Icon
name={iconName}
type={iconType}
color={iconColor}
size={iconSize || 35}
/>
}
underlayColor={false}
android_ripple={true}
/>
);
}
I want to be able to pass arrays and still be able to access the styles.backgroundColor. What would be the best of way of doing this?
Thanks in advance!

Framer motion - react last child of component doesn't animate when I added ternary operator

When I delete products from my cart, the last product <CartComponent /> which I want to delete doesn't animate. I use framer-motion to this project and next.js/chakra-ui/redux.
I followed the docs on framer motion with animate-presence:
Multiple children AnimatePresence works the same way with multiple
children. Just ensure that each has a unique key and components will
animate in and out as they're added or removed from the tree.
I recorded a video on youtube to show the problem more clearly:
youtube video showing the problem
Here's cart page below:
// some imports up here :D /\
const MotionBox = motion<BoxProps>(Box);
const Cart = () => {
const selectProducts = useSelector(selectAllDataFromStore);
const dispatch = useDispatch();
return (
<>
<PageLayout title='Koszyk' description='Fake Sugar - koszyk'>
<VStack>
{selectProducts.length > 0 ? (
<Box>
<AnimatePresence>
{selectProducts.map((item) => {
return (
<MotionBox
animate={{ opacity: 1 }}
exit={{ opacity: 0, x: -200 }}
key={item.slug}
>
<CartComponent item={item} />
</MotionBox>
);
})}
</AnimatePresence>
<Box pt={4}>
<Center>
<Divider orientation='horizontal' />
</Center>
<Button mt={2} onClick={() => dispatch(clearCart())}>
Wyczyść koszyk
</Button>
</Box>
</Box>
) : (
<MotionBox
initial={{ opacity: 0, y: -400 }}
animate={{ opacity: 1, y: 0, transition: { duration: 2 } }}
zIndex='tooltip'
>
<Flex direction='column' align='center' justify='center'>
<Heading as='h3' fontSize='3xl'>
Twój koszyk jest pusty.
</Heading>
<NextLink href='/' passHref>
<Button colorScheme='gray'>Przeglądaj produkty</Button>
</NextLink>
</Flex>
</MotionBox>
)}
</VStack>
</PageLayout>
</>
);
};
export default Cart;
edit 1: I didnt mention that before I wrap it with ternary operator, it was animating the last component when I delete it. So everything was good!
so it was like
youtube video: before it worked it was like
const Cart = () => {
const selectProducts = useSelector(selectAllDataFromStore);
const dispatch = useDispatch();
return (
<>
<PageLayout title='Koszyk' description='Fake Sugar - koszyk'>
<VStack>
<Box>
<AnimatePresence>
{selectProducts.map((item) => {
return (
<MotionBox
animate={{ opacity: 1 }}
exit={{ opacity: 0, x: -200 }}
key={item.slug}
>
<CartComponent item={item} />
</MotionBox>
);
})}
</AnimatePresence>
</Box>
</VStack>
</PageLayout>
</>
);
};
export default Cart;
hmm sussy!

How Do I Stop Swiper.JS Thumbs From Automatically Transitioning?

I have a swiper.js slider made in react. If I have 8 images in the swiper and then I navigate to the 8th thumbnail and click on the 7th thumb it will slider the thumbnail part up. Is there a way to prevent this behavior from happening?
What I want to do is for it to only auto slide if there are more slides up or down available. For example, if I have 5 thumbnails and slide [2, 3, 4, 5, 6] are visible, if I select slide 6 it would move over and show slide 7 available and if I select slide 2 it would slide over and show slide 1. That is the only movement I want it to do. Is that feature available in the docs?
<div className="mainSliderWrap">
<Swiper
pagination
onSlideChange={swiper => {
setImageSelectedIndex(swiper.activeIndex + 1)
return true
}}
thumbs={{ swiper: thumbsSwiper }}>
{products.images.map((product, index) => (
<SwiperSlide key={product.originalSrc}>
<img
className={classes.mainSliderImg}
ref={el => {
imagesRef.current[index] = el as HTMLImageElement
}}
src={product.originalSrc}
data-zoom={product.originalSrc}
alt={product.title}
height={360}
width={360}
/>
</SwiperSlide>
))}
</Swiper>
</div>
<div
className={[
"productSlider",
ProductSliderOrientation(width) === "horizontal" &&
"horizontalProductSlider"
].join(" ")}>
<div className="swiper-button-next mainProdNext" />
<div className="swiper-button-prev mainProdPrev" />
<Swiper
direction={ProductSliderOrientation(width)}
touchRatio={1}
threshold={10}
slidesPerView={slidesPerView}
spaceBetween={15}
navigation={{
nextEl: ".mainProdNext",
prevEl: ".mainProdPrev"
}}
onSwiper={setThumbsSwiper}
breakpoints={{
0: {
spaceBetween: 10,
slidesOffsetBefore: 20,
slidesOffsetAfter: 20
},
576: {
spaceBetween: 10,
slidesPerView: 5
},
768: {
touchRatio: 0,
slidesPerView: 5
},
992: {
touchRatio: 1,
slidesPerView: 5
},
1200: {
touchRatio: 0,
slidesPerView: 5
}
}}>
{products &&
products.images.map(product => (
<SwiperSlide key={product.originalSrc}>
<ProductImage
image={product}
alt={products.title}
moduleClass={classes.productImagePick}
gatsbyImageClass={classes.productImagePickGatsby}
/>
</SwiperSlide>
))}
</Swiper>
</div>
</div>
</div>
It looks like there is no way to implement thumbnails with this behavior without manipulating data. So idea is when you change a thumbnail you need to reorder data in order to show next/previous thumb.
export default function App() {
const [thumbsSwiper, setThumbsSwiper] = useState(null);
const [data, setData] = useState([
"https://swiperjs.com/demos/images/nature-1.jpg",
"https://swiperjs.com/demos/images/nature-2.jpg",
"https://swiperjs.com/demos/images/nature-3.jpg",
"https://swiperjs.com/demos/images/nature-4.jpg",
"https://swiperjs.com/demos/images/nature-5.jpg",
"https://swiperjs.com/demos/images/nature-6.jpg",
"https://swiperjs.com/demos/images/nature-7.jpg",
"https://swiperjs.com/demos/images/nature-8.jpg",
"https://swiperjs.com/demos/images/nature-9.jpg",
"https://swiperjs.com/demos/images/nature-10.jpg"
]);
return (
<>
<Swiper
style={{
"--swiper-navigation-color": "#fff",
"--swiper-pagination-color": "#fff"
}}
loop={false}
spaceBetween={10}
navigation={true}
thumbs={{ swiper: thumbsSwiper }}
className="mySwiper2"
onSlideChange={(e) => {
if (e.realIndex % 3 === 0) {
// move first element to the end
const newData = [...data];
newData.push(newData.shift());
setData(newData);
e.slideTo(e.realIndex - 1);
}
if (e.realIndex === 0) {
// move last element to the beginning
const newData = [...data];
newData.unshift(newData.pop());
setData(newData);
e.slideTo(e.realIndex + 1);
}
}}
>
{data.map((img) => (
<SwiperSlide key={img}>
<img src={img} />
</SwiperSlide>
))}
</Swiper>
<Swiper
onSwiper={setThumbsSwiper}
loop={false}
loopedSlides={1}
spaceBetween={10}
slidesPerView={4}
freeMode={false}
watchSlidesVisibility={true}
watchSlidesProgress={true}
className="mySwiper"
>
{data.map((img) => (
<SwiperSlide key={img}>
<img src={img} />
</SwiperSlide>
))}
</Swiper>
</>
);
}
Here is a codesandbox
This is a quick example only. I hope it will help you.

How to play a specific video on a React native ScrollView carousel?

I have a scrollview that has as a child an array of video components. It displays on the UI a carousel of videos. I would like the user to be able to click a play button that is above the video and play that video, but the way my logic is set, all the videos play together because the condition to play the video lives in a react useState.
see the code:
const VideosView = (props) => {
const videoClips = props.route.params.data;
const [state, setState] = React.useState(0);
const [play, setPlay] = React.useState(false);
return (
<>
<SafeAreaView pointerEvents='box-none' style={styles.root}>
<Header
goBack={() => props.navigation.goBack()}
title={state === 0 ? 'RYGGRöRLIGHET' : 'STYRKA & BALANS'}
/>
<View style={styles.paragraphsContainer}>
<Text style={styles.title}>
{state === 0 ? 'Ryggrörlighet' : 'Styrka & Balans'}
</Text>
<Text style={styles.paragraph}>
'Utför övningen i ca 5 min för bästa resultat.'
</Text>
<View style={styles.circlesContainer}>
{renderImages.map((_, index) => {
return (
<TouchableOpacity key={index} onPress={() => setState(0)}>
<View
style={[
styles.circles,
{ opacity: index === state ? 1 : 0.5 }
]}
/>
</TouchableOpacity>
);
})}
</View>
</View>
</SafeAreaView>
<View style={styles.scrollViewContainer}>
<ScrollView
bounces={false}
showsHorizontalScrollIndicator={false}
scrollEventThrottle={30}
onScroll={({ nativeEvent }) => {
const slide = Math.ceil(
nativeEvent.contentOffset.x / nativeEvent.layoutMeasurement.width
);
if (slide !== state) {
setState(slide);
}
}}
horizontal>
{videoClips.map((video, index) => (
<View style={{ position: 'relative' }}>
{!play && (
<TouchableOpacity
style={{
position: 'absolute',
backgroundColor: 'rgba(255,255,255,0.5)',
alignSelf: 'center',
top: 130,
width: 160,
height: 160,
borderRadius: 500,
zIndex: 4000
}}
onPress={() => setPlay(true)}></TouchableOpacity>
)}
<Video
shouldPlay={play}
key={index}
source={{
uri: video
}}
resizeMode='cover'
style={{ width: wp('100%'), height: hp('100%') }}
/>
</View>
))}
</ScrollView>
</View>
</>
);
};
I would like the user to click a button and play one video, use the carousel to go to next video, click play and play the next video.
Please, help.
If you change the play state from Boolean to Number you will probably solve your problem. Here is how:
const [videoBeingDisplayedIndex, setVideoBeingDisplayedIndex] = React.useState(5);
const [play, setPlay] = React.useState(5); // If it is equals to the video id the video should play, else it should not
...
<Button onClick={() => setPlay(videoBeingDisplayedIndex)}>
Play Video
</Button>
<VideoCarousel>
{
myVideoList.map((video, index) => {
return (
<Video
content={video}
index={index}
play={play === index} // Check if the current play state is equals to the video unique id
/>
);
})
}
</VideoCarousel>
...
PS.: please, have in mind that this snippet is abstracting the react-native elements and focusing on the problem.

Categories