diff --git a/examples/with-framer-motion/README.md b/examples/with-framer-motion/README.md new file mode 100644 index 0000000000000..29fd642abd26d --- /dev/null +++ b/examples/with-framer-motion/README.md @@ -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 [``](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 `` 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. diff --git a/examples/with-framer-motion/components/Gallery.js b/examples/with-framer-motion/components/Gallery.js new file mode 100644 index 0000000000000..c5712b59116df --- /dev/null +++ b/examples/with-framer-motion/components/Gallery.js @@ -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 }) => ( + <> + + + + + + + + + +) + +const Gallery = () => ( + <> +

Barbican

+
+ + {images.map((id, i) => ( + + ))} + +
+ + +) + +export default Gallery diff --git a/examples/with-framer-motion/components/SingleImage.js b/examples/with-framer-motion/components/SingleImage.js new file mode 100644 index 0000000000000..e615a60ea9b0c --- /dev/null +++ b/examples/with-framer-motion/components/SingleImage.js @@ -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 }) => ( + <> + + + + + ← Back + + + + + +) + +export default SingleImage diff --git a/examples/with-framer-motion/constants.js b/examples/with-framer-motion/constants.js new file mode 100644 index 0000000000000..3ce3f5fdd5578 --- /dev/null +++ b/examples/with-framer-motion/constants.js @@ -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' +] diff --git a/examples/with-framer-motion/package.json b/examples/with-framer-motion/package.json new file mode 100644 index 0000000000000..fadd2fd5bd35d --- /dev/null +++ b/examples/with-framer-motion/package.json @@ -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" +} diff --git a/examples/with-framer-motion/pages/_app.js b/examples/with-framer-motion/pages/_app.js new file mode 100644 index 0000000000000..16bb9c98ce77e --- /dev/null +++ b/examples/with-framer-motion/pages/_app.js @@ -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 ( + <> + + + + + + + + ) + } +} diff --git a/examples/with-framer-motion/pages/image/[id].js b/examples/with-framer-motion/pages/image/[id].js new file mode 100644 index 0000000000000..4d51a020da93c --- /dev/null +++ b/examples/with-framer-motion/pages/image/[id].js @@ -0,0 +1,15 @@ +import * as React from 'react' +import SingleImage from '../../components/SingleImage' + +const Page = ({ id }) => { + return +} + +Page.getInitialProps = ({ query }) => { + const id = Number.parseInt(query.id, 10) + return { + id + } +} + +export default Page diff --git a/examples/with-framer-motion/pages/index.js b/examples/with-framer-motion/pages/index.js new file mode 100644 index 0000000000000..bbfdd431cb025 --- /dev/null +++ b/examples/with-framer-motion/pages/index.js @@ -0,0 +1,6 @@ +import React from 'react' +import Gallery from '../components/Gallery' + +const Index = () => + +export default Index