-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
352 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# framer-motion example | ||
|
||
## How to use | ||
|
||
### Using `create-next-app` | ||
|
||
Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example: | ||
|
||
```bash | ||
npx create-next-app --example with-framer-motion with-framer-motion | ||
# or | ||
yarn create next-app --example with-framer-motion with-framer-motion | ||
``` | ||
|
||
### Download manually | ||
|
||
Download the example: | ||
|
||
```bash | ||
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-framer-motion | ||
cd with-framer-motion | ||
``` | ||
|
||
Install it and run: | ||
|
||
```bash | ||
npm install | ||
npm run build | ||
npm start | ||
``` | ||
|
||
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) | ||
|
||
```bash | ||
now | ||
``` | ||
|
||
## The idea behind the example | ||
|
||
Framer [`Motion`](https://github.com/framer/motion) is a production-ready animation library. By using a custom [`<App>`](https://nextjs.org/docs#custom-app) along with Motion's [`AnimatePresence`](https://www.framer.com/api/motion/animate-presence/) component, transitions between Next pages becomes simple and declarative. | ||
|
||
When using Next's `<Link>` component, you will likely want to [disable the default scroll behavior](https://nextjs.org/docs#disabling-the-scroll-changes-to-top-on-page) for a more seamless navigation experience. Scrolling to the top of a page can be re-enabled by adding a `onExitComplete` callback on the `AnimatePresence` component. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import * as React from 'react' | ||
import Link from 'next/link' | ||
|
||
import { motion } from 'framer-motion' | ||
import { images } from '../constants' | ||
|
||
const transition = { duration: 0.5, ease: [0.43, 0.13, 0.23, 0.96] } | ||
|
||
const thumbnailVariants = { | ||
initial: { scale: 0.9, opacity: 0 }, | ||
enter: { scale: 1, opacity: 1, transition }, | ||
exit: { | ||
scale: 0.5, | ||
opacity: 0, | ||
transition: { duration: 1.5, ...transition } | ||
} | ||
} | ||
|
||
const frameVariants = { | ||
hover: { scale: 0.95 } | ||
} | ||
|
||
const imageVariants = { | ||
hover: { scale: 1.1 } | ||
} | ||
|
||
const Thumbnail = ({ id, i }) => ( | ||
<> | ||
<motion.div className='thumbnail' variants={thumbnailVariants}> | ||
<motion.div | ||
className='frame' | ||
whileHover='hover' | ||
variants={frameVariants} | ||
transition={transition} | ||
> | ||
<Link href='/image/[id]' as={`/image/${i}`} scroll={false}> | ||
<motion.img | ||
src={`https://static1.squarespace.com/static/5b475b2c50a54f54f9b4e1dc/t/${id}.jpg?format=1500w`} | ||
alt='The Barbican' | ||
variants={imageVariants} | ||
transition={transition} | ||
/> | ||
</Link> | ||
</motion.div> | ||
</motion.div> | ||
<style> | ||
{` | ||
.thumbnail { | ||
flex: 1 0 33%; | ||
margin: 10px; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
cursor: pointer; | ||
} | ||
.frame { | ||
overflow: hidden; | ||
} | ||
.thumbnail img { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
`} | ||
</style> | ||
</> | ||
) | ||
|
||
const Gallery = () => ( | ||
<> | ||
<h1>Barbican</h1> | ||
<div className='gallery'> | ||
<motion.div | ||
className='thumbnails' | ||
initial='initial' | ||
animate='enter' | ||
exit='exit' | ||
variants={{ exit: { transition: { staggerChildren: 0.1 } } }} | ||
> | ||
{images.map((id, i) => ( | ||
<Thumbnail key={id} id={id} i={i} /> | ||
))} | ||
</motion.div> | ||
</div> | ||
<style> | ||
{` | ||
h1 { | ||
font-size: 100px; | ||
text-align: center; | ||
position: fixed; | ||
bottom: -100px; | ||
z-index: 1; | ||
color: #f9fbf8; | ||
left: 50%; | ||
transform: translateX(-50%); | ||
pointer-events: none; | ||
} | ||
.gallery { | ||
padding: 40px; | ||
margin: 0 auto; | ||
width: 100%; | ||
max-width: 1200px; | ||
position: relative; | ||
} | ||
.thumbnails { | ||
display: flex; | ||
flex-wrap: wrap; | ||
flex-direction: row; | ||
justify-content: space-between; | ||
} | ||
@media screen and (min-width: 600px) { | ||
h1 { | ||
font-size: 140px; | ||
bottom: -130px; | ||
} | ||
} | ||
@media screen and (min-width: 800px) { | ||
h1 { | ||
font-size: 180px; | ||
bottom: -170px; | ||
} | ||
} | ||
@media screen and (min-width: 1000px) { | ||
h1 { | ||
font-size: 220px; | ||
bottom: -200px; | ||
} | ||
} | ||
@media screen and (min-width: 1200px) { | ||
h1 { | ||
font-size: 280px; | ||
bottom: -260px; | ||
} | ||
} | ||
`} | ||
</style> | ||
</> | ||
) | ||
|
||
export default Gallery |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import * as React from 'react' | ||
import Link from 'next/link' | ||
|
||
import { motion } from 'framer-motion' | ||
import { images } from '../constants' | ||
|
||
const transition = { | ||
duration: 1, | ||
ease: [0.43, 0.13, 0.23, 0.96] | ||
} | ||
|
||
const imageVariants = { | ||
exit: { y: '50%', opacity: 0, transition }, | ||
enter: { | ||
y: '0%', | ||
opacity: 1, | ||
transition | ||
} | ||
} | ||
|
||
const backVariants = { | ||
exit: { x: 100, opacity: 0, transition }, | ||
enter: { x: 0, opacity: 1, transition: { delay: 1, ...transition } } | ||
} | ||
|
||
const SingleImage = ({ id }) => ( | ||
<> | ||
<motion.div className='single' initial='exit' animate='enter' exit='exit'> | ||
<motion.img | ||
variants={imageVariants} | ||
src={`https://static1.squarespace.com/static/5b475b2c50a54f54f9b4e1dc/t/${ | ||
images[id] | ||
}.jpg?format=1500w`} | ||
alt='The Barbican' | ||
/> | ||
<motion.div className='back' variants={backVariants}> | ||
<Link href='/'> | ||
<a>← Back</a> | ||
</Link> | ||
</motion.div> | ||
</motion.div> | ||
<style> | ||
{` | ||
.single { | ||
overflow: hidden; | ||
height: 100vh; | ||
} | ||
.single img { | ||
max-width: 100%; | ||
max-height: 100vh; | ||
} | ||
.back { | ||
position: fixed; | ||
top: 50px; | ||
right: 50px; | ||
font-size: 54px; | ||
z-index: 1; | ||
} | ||
.back a { | ||
text-decoration: none; | ||
} | ||
`} | ||
</style> | ||
</> | ||
) | ||
|
||
export default SingleImage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const images = [ | ||
'5b5a3938562fa764113169a6/1532639559620/DSCF3338', | ||
'5b5a3628f950b7390fbfc5f8/1532639027872/DSCF3246', | ||
'5b5a3741575d1fccb5ac6b3f/1532639066455/DSCF3268', | ||
'5b5a376b0e2e728eeeaca8e4/1532683586969/DSCF3274', | ||
'5b5c228403ce64f3c80d4d8e/1532764845121/DSCF3348', | ||
'5b5a3b800e2e728eeead9575/1532640158813/DSCF3375' | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "with-framer-motion", | ||
"version": "1.0.0", | ||
"scripts": { | ||
"dev": "next", | ||
"build": "next build", | ||
"start": "next start" | ||
}, | ||
"dependencies": { | ||
"framer-motion": "latest", | ||
"next": "latest", | ||
"react": "16.11.0", | ||
"react-dom": "16.11.0" | ||
}, | ||
"license": "ISC" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from 'react' | ||
import App, { Container } from 'next/app' | ||
import { AnimatePresence } from 'framer-motion' | ||
|
||
export default class MyApp extends App { | ||
/** | ||
* Handle scrolling gracefully, since next/router scrolls to top | ||
* before exit animation is complete. | ||
* | ||
* Note that next/link components should also be using `scroll={false}` | ||
**/ | ||
handleExitComplete () { | ||
if (typeof window !== 'undefined') { | ||
window.scrollTo({ top: 0 }) | ||
} | ||
} | ||
|
||
render () { | ||
const { Component, pageProps, router } = this.props | ||
return ( | ||
<> | ||
<Container> | ||
<AnimatePresence | ||
exitBeforeEnter | ||
onExitComplete={this.handleExitComplete} | ||
> | ||
<Component {...pageProps} key={router.route} /> | ||
</AnimatePresence> | ||
</Container> | ||
<style> | ||
{` | ||
body { | ||
padding: 0; | ||
margin: 0; | ||
background: #f9fbf8; | ||
} | ||
* { | ||
box-sizing: border-box; | ||
font-family: Helvetica, sans-serif; | ||
font-weight: 900; | ||
color: #222; | ||
} | ||
`} | ||
</style> | ||
</> | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import * as React from 'react' | ||
import SingleImage from '../../components/SingleImage' | ||
|
||
const Page = ({ id }) => { | ||
return <SingleImage id={id} /> | ||
} | ||
|
||
Page.getInitialProps = ({ query }) => { | ||
const id = Number.parseInt(query.id, 10) | ||
return { | ||
id | ||
} | ||
} | ||
|
||
export default Page |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import React from 'react' | ||
import Gallery from '../components/Gallery' | ||
|
||
const Index = () => <Gallery /> | ||
|
||
export default Index |