cap*_*uke 1 typescript reactjs next.js framer-motion
我不知道如何<AnimateSharedLayout />通过成帧器运动为组件的路由设置动画。基本上,在下面的代码中,我想显示图像列表,当单击它们时,我想导航到/images/[image_id]并显示它。我本地没有图像,所以我必须获取它们。这里存在一些问题:
这是代码_app.tsx
import '../styles/globals.css';
import 'bulma/css/bulma.css';
import { AppProps } from "next/app";
import { AnimateSharedLayout, AnimatePresence } from 'framer-motion';
const MyApp: React.FC<AppProps> = ({ Component, pageProps, router }) => {
return (
<AnimateSharedLayout>
<AnimatePresence>
<Component {...pageProps} key={router.route} />
</AnimatePresence>
</AnimateSharedLayout>
);
}
export default MyApp;
Run Code Online (Sandbox Code Playgroud)
这是pages/index.tsx
import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import Picture, { PictureProps } from "./components/Picture";
export const base = 'https://jsonplaceholder.typicode.com';
const Home: React.FC = () => {
const [pics, setPics] = useState<PictureProps[]>([]);
useEffect(() => {
fetch(`${base}/photos?_start=0&_limit=15`)
.then(res => res.json())
.then(res => setPics(res as PictureProps[]))
.catch(err => console.log("fetching error", err));
}, []);
return (
<div className="container pt-4">
<div className="columns is-multiline">
{
pics.map(pic => (
<div key={pic.id} className="column is-one-third-desktop is-half-tablet is-full-mobile">
<Picture {...pic} />
</div>
))
}
</div>
</div>
);
}
export default Home;
Run Code Online (Sandbox Code Playgroud)
这是pages/images/[image].tsx
import { motion } from "framer-motion";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { PictureProps } from "../components/Picture";
import { base } from '../index';
const Image: React.FC = () => {
const router = useRouter();
const { image } = router.query;
const [pic, setPic] = useState<PictureProps>({} as PictureProps);
useEffect(() => {
image && fetch(`${base}/photos/${image}`)
.then(res => res.json())
.then(res => setPic(res))
.catch(err => console.log(err));
}, [image]);
return (
<motion.div>
{
image &&
<motion.figure layoutId={`img-${image}`} className='image'>
<img src={pic.url} alt={pic.title} />
</motion.figure>
}
</motion.div>
);
}
export default Image;
Run Code Online (Sandbox Code Playgroud)
这是components/Picture.tsx
import { motion, Variants } from "framer-motion";
import Link from "next/link";
export type PictureProps = {
albumId: number,
id: number,
title: string,
url: string,
thumbnailUrl: string,
};
const cardVariants: Variants = {
unloaded: {
y: -100,
opacity: 0,
transition: {
when: "afterChildren",
},
},
loaded: {
y: 0,
opacity: 1,
transition: {
when: "beforeChildren",
},
}
};
const textVariants: Variants = {
unloaded: {
scale: .7,
opacity: 0,
},
loaded: {
scale: 1,
opacity: 1,
},
};
const Picture: React.FC<PictureProps> = ({
id, title, url,
albumId, thumbnailUrl
}) => {
console.log(`img-${id}`);
return (
<motion.div className='card' variants={cardVariants} initial="unloaded" animate="loaded" exit="exit">
<div className="card-image">
<motion.figure
className="image"
layoutId={`img-${id}`}
>
<img src={url} alt={title} />
</motion.figure>
</div>
<motion.div className="card-content">
<div className="media">
<div className="media-left">
<Link href={`images/${id}`}>
<a>
<figure className="image is-50by50">
<img src={thumbnailUrl} alt="thumbnail" />
</figure>
</a>
</Link>
</div>
<motion.div variants={textVariants} className="media-content">
<p className="title is-6">{title}</p>
</motion.div>
</div>
<motion.div variants={textVariants} className='content'>
Some random text Some random text
Some random text Some random text
<br />
<strong>Album id</strong>: <em>{albumId}</em>
</motion.div>
</motion.div>
</motion.div>
);
}
export default Picture;
Run Code Online (Sandbox Code Playgroud)
弄清楚了,这是新代码
pages/_app.tsx
import '../styles/globals.css';
import 'bulma/css/bulma.css';
import { AppProps } from "next/app";
import { AnimateSharedLayout, AnimatePresence } from 'framer-motion';
const MyApp: React.FC<AppProps> = ({ Component, pageProps, router }) => {
return (
<AnimateSharedLayout type='crossfade'>
<Component {...pageProps} />
</AnimateSharedLayout>
);
}
export default MyApp;
Run Code Online (Sandbox Code Playgroud)
pages/index.tsx
import Picture, { PictureProps } from "../components/Picture";
import { NextPage, GetStaticProps } from "next"
export const base = 'https://jsonplaceholder.typicode.com';
type IndexProps = {
pictures: PictureProps[],
}
const Home: NextPage<IndexProps> = ({ pictures }) => {
return (
<div className="container pt-4">
<div className="columns is-multiline">
{
pictures.map(pic => (
<div key={pic.id} className="column is-one-third-desktop is-half-tablet is-full-mobile">
<Picture {...pic} />
</div>
))
}
</div>
</div>
);
}
export const getStaticProps: GetStaticProps = async () => {
const pics = await fetch(`${base}/photos?_start=0&_limit=15`)
.then(res => res.json())
.then(res => res)
.catch(err => console.log("fetching error", err));
return {
props: {
pictures: pics,
},
};
}
export default Home;
Run Code Online (Sandbox Code Playgroud)
pages/images/[image].tsx
import { motion } from "framer-motion";
import { PictureProps } from "../../components/Picture";
import { base } from '../index';
import { NextPage, GetStaticProps, GetStaticPaths } from "next";
import { useRouter } from 'next/router';
type ImagePageProps = {
image: PictureProps,
}
const Image: NextPage<ImagePageProps> = ({ image }) => {
const { isFallback } = useRouter();
return isFallback ? <div>loading...</div>
: (
<div>
{
image &&
<motion.div layoutId={`img-${image.id}`} style={{
backgroundImage: `url('${image.url}')`, backgroundPosition: 'center',
backgroundRepeat: 'no-repeat', backgroundSize: 'cover',
height: '70vh', position: 'relative', top: 0, left: 0, width: '100%',
}}>
</motion.div>
}
</div>
);
}
export const getStaticProps: GetStaticProps = async ctx => {
const { image } = ctx.params;
const pic = await fetch(`${base}/photos/${image}`)
.then(res => res.json())
.then(res => res)
.catch(err => console.log(err));
return {
props: {
image: pic,
},
};
}
export const getStaticPaths: GetStaticPaths = async () => {
const ids = await fetch(`${base}/photos?_start=0&_limit=15`)
.then(res => res.json())
.then(res => res.map(el => el.id.toString()))
.catch(err => console.log(err));
return {
paths: ids.map((id: string) => ({
params: {
image: id,
},
})),
fallback: true,
};
};
export default Image;
Run Code Online (Sandbox Code Playgroud)
components/Picture.tsx
import { motion, Variants } from "framer-motion";
import Link from "next/link";
export type PictureProps = {
albumId: number,
id: number,
title: string,
url: string,
thumbnailUrl: string,
};
const cardVariants: Variants = {
initial: {
scale: 0.9,
transition: {
when: 'afterChildren',
},
},
enter: {
scale: 1,
transition: {
when: 'beforeChildren',
duration: .3,
},
},
};
const textVariants: Variants = {
initial: {
opacity: 0,
},
enter: {
opacity: 1,
},
};
const Picture: React.FC<PictureProps> = ({
id, title, url,
albumId, thumbnailUrl
}) => {
return (
<motion.div className='card' variants={cardVariants} initial="initial" animate="enter">
<div className="card-image">
<motion.figure
className="image"
layoutId={`img-${id}`}
>
<img src={url} alt={title} />
</motion.figure>
</div>
<motion.div variants={textVariants} className="card-content">
<div className="media">
<div className="media-left">
<Link href={`/images/${id}`}>
<a>
<figure className="image is-50by50">
<img src={thumbnailUrl} alt="thumbnail" />
</figure>
</a>
</Link>
</div>
<motion.div variants={textVariants} className="media-content">
<p className="title is-6">{title}</p>
</motion.div>
</div>
<motion.div variants={textVariants} className='content'>
Some random text Some random text
Some random text Some random text
<br />
<strong>Album id</strong>: <em>{albumId}</em>
</motion.div>
</motion.div>
</motion.div>
);
}
export default Picture;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2400 次 |
| 最近记录: |