I am trying to create a carousel in a next.js app, using framer motion and tailwindcss.
import { motion } from "framer-motion";
import { useRef, useEffect, useState, useCallback, useLayoutEffect } from "react";
function Carousel({ images }) {
const [width, setWidth] = useState(0);
const carousel = useRef();
useLayoutEffect(() => {
setWidth(carousel.current.scrollWidth - carousel.current.offsetWidth);
}, []);
return (
<motion.div
ref={carousel}
className="min-w-full h-96 overflow-hidden cursor-grab bg-primary my-5"
whileTap={{ cursor: "grabbing" }}
>
<motion.div
drag="x"
dragConstraints={{ right: 0, left: -width }}
className="flex items-center justify-center min-w-max h-full"
>
{images.map((image) => {
return (
<motion.div key={image.src} className="h-5/6 mx-8 relative">
<img
src={image.src}
alt=""
className="h-full rounded-lg pointer-events-none"
/>
</motion.div>
);
})}
</motion.div>
</motion.div>
);
}
export default Carousel;
The thing is that when I navigate to the page with the carousel from within the app (using next's <Link>), the scrollWidth is messed up - evaluating a much smaller number, thus my right dragConstraints is incorrect and I can't drag much more than what is currently visible on the screen. Only after I refresh the page scrollWidth is correct and the dragging constraint and all is properly working.
Still learning frontend, so I would assume I am missing something obvious, but what I have tried is:
useEffect and useLayoutEffect
useRef and callback ref
passing carousel, carousel.current to the dependency array
not use the next <Link>, but that is just like manually refreshing
the page (not cool)
replacing tailwind with plain CSS
I would appreciate any hint/advice ✌️ since my web searches didn't help me solve this and only brought me more confusion regarding the origin of the issue.
Related
I have a div that wont animate in no matter what i do...this div on only renders if a condition is met
i have tried changing the transtion type to see if it would animate but it just pops in
`
mport React from 'react'
import { useState, useEffect } from 'react'
import sanityClient from '../Client';
import {Link} from 'react-router-dom'
import { motion } from 'framer-motion';
import { useContext } from 'react';
import {variantcontext} from '../variantcontext';
const Models = () => {
const variants = useContext(variantcontext);
console.log(variants)
const [CarModels, SetModels] = useState(null);
useEffect(()=>{
sanityClient.fetch(
`*[_type =="car"]{
_id,
name,
model,
description,
image{
asset->{
_id,
url
}
},
}
`
)
.then((data)=>{
SetModels(data);
console.log(data);
})
.catch(console.error);
},[]);
return (
<motion.div variants={variants} initial='intial' animate='visible' exit='exit' transition={{type:'spring', delay:3}} className='cards flex gap-10'>
{CarModels ? CarModels.map((car, index)=>(
<Link to={'/'+ car.model}><div className='flex flex-col text-center shadow-lg h-52 m-3 bg-slate-100 p-5' key={index}>
<img className='carimages w-56 ' src={car.image.asset.url} alt='M4 Image'/>
<div className='info mt-auto'>
<h1>{car.name}</h1>
<h2>{car.description}</h2>
</div>
</div>
</Link>
)): <p className='m-auto mt-5'>Loading... </p>}
</motion.div>
)
}
export default Models
`above is the code for the entire component...i also tried re-installing framer-motion...but still no luck...i then tried using thesame variants in another component and it worked...i used console.log to make sure that the variants were being passed correctly to the component.
I'm using next.js and I was hoping to see my gallery collection like this on click of one of my images which is not happening. Actually, it's like I have just used a normal component, because literally nothing is happening when I click one of my images. Please help.
// this is my app component
import SimpleReactLightbox from 'simple-react-lightbox'
const MyApp = ()=>{
return(
<SimpleReactLightbox>
<Component {...pageProps} />
</SimpleReactLightbox>
)
}
// this is my collection
import { CollectionStyledTypography } from './styles/collectionStyledComponents'
import { SRLWrapper } from 'simple-react-lightbox'
import Image from 'next/image'
const Collection = ({ imagesList = [] }) => {
return (
<SRLWrapper>
<div style={{ margin: '50px' }}>
{imagesList.map((image, index) => (
<CollectionStyledTypography component="div" key={index}>
<Image src={image.src} alt={image.alt} layout="fill" />
</CollectionStyledTypography>
))}
</div>
</SRLWrapper>
)
}
export default Collection
SRL is no longer being developed, and it won't work in React 18. You will have to downgrade to 17 to make it work. I am facing the same problem, nothing happens when i click on an image.
for some reason my NextJS page transition with framer-motion doesn't seem to work. I followed many tutorials and did a lot of research and it still doesn't work. Here's my _app.tsx code.
import '../styles/globals.css'
import '../styles/ui.css'
import '../fonts/define.css'
import { AppProps } from 'next/app'
import Layout from '../layouts/dashboard'
import Sidebar from '../components/Sidebar'
import { AnimatePresence, motion } from 'framer-motion'
const App = ({ Component, pageProps }: AppProps) => {
return <main>
<Sidebar/>
<AnimatePresence>
<motion.div initial={{opacity: 0}} animate={{opacity: 1}} exit={{opacity: 0}} className="content">
<Component {...pageProps} />
</motion.div>
</AnimatePresence>
</main>
}
export default App
When I switch between routes the transition just doesn't fire. It only fades in on initial load, then opacity: 1 style is applied to .content div and that's it.
Thank you for your help!
I figured it out. I needed to add key attribute to the <motion.div> div. Read more here https://www.framer.com/docs/animate-presence/#unmount-animations
I am using React cards to show dynamic cards. I wanted to show 4 cards for desktop view at one row and 1 card for the mobile view but it is always coming vertically no cards are shown horizontally
The Container Component Of The card
import React from 'react'
import SongCard from '../SongCard'
import {
CardDeck
} from 'reactstrap';
function Popular({ popular }) {
return (
<div>
{popular.map((post) =>
<div key={post.etag}>
{
<CardDeck style={{display: 'flex', flexDirection: 'row',justifyContent: 'right'}}>
<SongCard
Title={post.snippet.title}
VideoId={post.id.videoId}
Image={post.snippet.thumbnails.high.url}
ChannelTitle={post.snippet.channelTitle} />
</CardDeck>
}
</div>)
}
</div>
)
}
export default Popular
And the card component is
import React from 'react'
import {
Card, CardImg, CardText, CardBody,
CardTitle, CardSubtitle
} from 'reactstrap';
function SongCard({ Title, VideoId, Image, ChannelTitle }) {
return (
<div>
<Card style={{maxWidth:'30em',flex: '1'}}>
<CardImg top width="100%" src={Image} alt="image" />
<CardBody>
<CardTitle>{Title}</CardTitle>
<CardSubtitle>{ChannelTitle}</CardSubtitle>
<CardText></CardText>
</CardBody>
</Card>
</div>
)
}
export default SongCard
First, in SongCard you might not need to encapsulate your card component in a div, it make your style for Card kind of unavailable because the div is by default full Width.
Secondly, CardDeck should be outside of the map loop cause you create a new CardDeck each post and it might not be what you want. to put you "key={post.etag}" directly in SongCard instead.
I also don't recommend to add custom style in style in CardDeck because you will break the default layout for all devices.
import React from 'react'
import SongCard from '../SongCard'
import {
CardDeck
} from 'reactstrap';
function Popular({ popular }) {
return (
<CardDeck>
{popular.map((post) =>
<SongCard
key={post.etag}
Title={post.snippet.title}
VideoId={post.id.videoId}
Image={post.snippet.thumbnails.high.url}
ChannelTitle={post.snippet.channelTitle} />
</div>)
}
</CardDeck>
)
}
export default Popular
And
import React from 'react'
import {
Card, CardImg, CardText, CardBody,
CardTitle, CardSubtitle
} from 'reactstrap';
function SongCard({ Title, VideoId, Image, ChannelTitle }) {
return (
<Card>
<CardImg top src={Image} alt="image" />
<CardBody>
<CardTitle>{Title}</CardTitle>
<CardSubtitle>{ChannelTitle}</CardSubtitle>
<CardText></CardText>
</CardBody>
</Card>
)
}
export default SongCard
I'm currently implementing a Modal component within my app, using Portals. What I'm trying to achieve is that when the Modal component is rendered, it should fade in and when it's no longer rendered it should fade out.
Looking at the react-transition-group docs it appears that props have to be used in order to achieve this, however I'd like a solution without any form of state or props. Consider:
<App>
<Modal />
</App>
App should load as normal, however Modal should fade in. When Modal is no longer rendered based on some condition way above these components, it should fade out.
Here's the actual code for
import Fade from "../Fade";
import { TransitionGroup } from "react-transition-group";
const Modal = ({ children, ...other }) => {
return (
<Portal>
<TransitionGroup>
<Fade>
{children}
</Fade>
</TransitionGroup>
</Portal>
);
};
And for the Fade component:
import { CSSTransition } from 'react-transition-group';
const Fade = ({ children, ...other }) => (
<CSSTransition
{...other}
timeout={500}
classNames={{
//my classes here
}}
>
{children}
</CSSTransition>
);
Any suggestion? I'm sure this used to work, before React 16 and react-css-transitions changed to react-transition-group but I'm having a very hard time figuring this out.
To be clear, I can transition using state/props and have a working example of this but that is not what I'm trying to achieve. I'd like to fade in when it is rendered, and fade out when it is un-rendered...
Thanks!
appear={true}
Seems to fix this for now, although there's no way to animate the reverse (component being un-mounted).