mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Add example with next-page-transitions (#4404)
This commit is contained in:
parent
ee1b6d93ce
commit
f5402476cf
45
examples/with-next-page-transitions/README.md
Normal file
45
examples/with-next-page-transitions/README.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-next-page-transitions)
|
||||||
|
|
||||||
|
# next-page-transitions 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-next-page-transitions with-next-page-transitions
|
||||||
|
# or
|
||||||
|
yarn create next-app --example with-next-page-transitions with-next-page-transitions
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download manually
|
||||||
|
|
||||||
|
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-page-transitions
|
||||||
|
cd with-next-page-transitions
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The [`next-page-transitions`](https://github.com/illinois/next-page-transitions) library is a component that sits at the app level and allows you to animate page changes. It works especially nicely with apps with a shared layout element, like a navbar. This component will ensure that only one page is ever mounted at a time, and manages the timing of animations for you. This component works similarly to [`react-transition-group`](https://github.com/reactjs/react-transition-group) in that it applies classes to a container around your page; it's up to you to write the CSS transitions or animations to make things pretty!
|
||||||
|
|
||||||
|
This example includes two pages with links between them. The "About" page demonstrates how `next-page-transitions` makes it easy to add a loading state when navigating to a page: it will wait for the page to "load" its content (in this examples, that's simulated with a timeout) and then hide the loading indicator and animate in the page when it's done.
|
||||||
|
|
30
examples/with-next-page-transitions/components/Loader.js
Normal file
30
examples/with-next-page-transitions/components/Loader.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Loader = () => (
|
||||||
|
<div className='loader'>
|
||||||
|
<style jsx>{`
|
||||||
|
.loader {
|
||||||
|
border: 8px solid #f3f3f3; /* Light grey */
|
||||||
|
border-top: 8px solid #3498db; /* Blue */
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
animation: spin 2s linear infinite;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Loader
|
16
examples/with-next-page-transitions/package.json
Normal file
16
examples/with-next-page-transitions/package.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "with-next-page-transitions",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "latest",
|
||||||
|
"react": "^16.0.0",
|
||||||
|
"react-dom": "^16.0.0",
|
||||||
|
"next-page-transitions": "1.0.0-alpha.2"
|
||||||
|
},
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
67
examples/with-next-page-transitions/pages/_app.js
Normal file
67
examples/with-next-page-transitions/pages/_app.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import React from 'react'
|
||||||
|
import App, { Container } from 'next/app'
|
||||||
|
import { PageTransition } from 'next-page-transitions'
|
||||||
|
|
||||||
|
import Loader from '../components/Loader'
|
||||||
|
|
||||||
|
const TIMEOUT = 400
|
||||||
|
|
||||||
|
export default class MyApp extends App {
|
||||||
|
static async getInitialProps ({ Component, ctx }) {
|
||||||
|
let pageProps = {}
|
||||||
|
|
||||||
|
if (Component.getInitialProps) {
|
||||||
|
pageProps = await Component.getInitialProps(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { pageProps }
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { Component, pageProps } = this.props
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<PageTransition
|
||||||
|
timeout={TIMEOUT}
|
||||||
|
classNames='page-transition'
|
||||||
|
loadingComponent={<Loader />}
|
||||||
|
loadingDelay={500}
|
||||||
|
loadingTimeout={{
|
||||||
|
enter: TIMEOUT,
|
||||||
|
exit: 0
|
||||||
|
}}
|
||||||
|
loadingClassNames='loading-indicator'
|
||||||
|
>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</PageTransition>
|
||||||
|
<style jsx global>{`
|
||||||
|
.page-transition-enter {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(0, 20px, 0);
|
||||||
|
}
|
||||||
|
.page-transition-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
transition: opacity ${TIMEOUT}ms, transform ${TIMEOUT}ms;
|
||||||
|
}
|
||||||
|
.page-transition-exit {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.page-transition-exit-active {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity ${TIMEOUT}ms;
|
||||||
|
}
|
||||||
|
.loading-indicator-appear,
|
||||||
|
.loading-indicator-enter {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.loading-indicator-appear-active,
|
||||||
|
.loading-indicator-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity ${TIMEOUT}ms;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
37
examples/with-next-page-transitions/pages/_document.js
Normal file
37
examples/with-next-page-transitions/pages/_document.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Document, { Head, Main, NextScript } from 'next/document'
|
||||||
|
|
||||||
|
export default class MyDocument extends Document {
|
||||||
|
static async getInitialProps (ctx) {
|
||||||
|
const initialProps = await Document.getInitialProps(ctx)
|
||||||
|
return { ...initialProps }
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<html lang='en'>
|
||||||
|
<Head>
|
||||||
|
<meta
|
||||||
|
name='viewport'
|
||||||
|
content='initial-scale=1.0, width=device-width'
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel='stylesheet'
|
||||||
|
href='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css'
|
||||||
|
integrity='sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy'
|
||||||
|
crossOrigin='anonymous'
|
||||||
|
/>
|
||||||
|
<style>{`
|
||||||
|
.page {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</Head>
|
||||||
|
<body>
|
||||||
|
<Main />
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
51
examples/with-next-page-transitions/pages/about.js
Normal file
51
examples/with-next-page-transitions/pages/about.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
class About extends React.Component {
|
||||||
|
static pageTransitionDelayEnter = true
|
||||||
|
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
loaded: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.timeoutId = setTimeout(() => {
|
||||||
|
this.props.pageTransitionReadyToEnter()
|
||||||
|
this.setState({ loaded: true })
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
if (this.timeoutId) clearTimeout(this.timeoutId)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (!this.state.loaded) return null
|
||||||
|
return (
|
||||||
|
<div className='container bg-success page'>
|
||||||
|
<h1>About us</h1>
|
||||||
|
<p>
|
||||||
|
Notice how a loading spinner showed up while my content was "loading"?
|
||||||
|
Pretty neat, huh?
|
||||||
|
</p>
|
||||||
|
<Link href='/'>
|
||||||
|
<a className='btn btn-light'>Go back home</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
About.propTypes = {
|
||||||
|
pageTransitionReadyToEnter: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
About.defaultProps = {
|
||||||
|
pageTransitionReadyToEnter: () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default About
|
13
examples/with-next-page-transitions/pages/index.js
Normal file
13
examples/with-next-page-transitions/pages/index.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
const Index = () => (
|
||||||
|
<div className='container bg-primary page'>
|
||||||
|
<h1>Hello, world!</h1>
|
||||||
|
<Link href='/about'>
|
||||||
|
<a className='btn btn-light'>About us</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Index
|
Loading…
Reference in a new issue