我正在使用React Router v4和react-transition-group v2来测试页面滑动动画。
const RouterMap = () => (
<Router>
<Route render={({ location }) =>
<TransitionGroup>
<CSSTransition key={location.pathname.split('/')[1]} timeout={500} classNames="pageSlider" mountOnEnter={true} unmountOnExit={true}>
<Switch location={location}>
<Route path="/" exact component={ Index } />
<Route path="/comments" component={ Comments } />
<Route path="/opinions" component={ Opinions } />
<Route path="/games" component={ Games } />
</Switch>
</CSSTransition>
</TransitionGroup>
} />
</Router>
)
CSS:
.pageSlider-enter {
transform: translate3d(100%, 0, 0);
}
.pageSlider-enter.pageSlider-enter-active {
transform: translate3d(0, 0, 0);
transition: all 600ms;
}
.pageSlider-exit {
transform: translate3d(0, 0, 0);
}
.pageSlider-exit.pageSlider-exit-active {
transform: translate3d(-100%, 0, 0);
transition: all 600ms;
}
动画如下:
如您所见,index page
滑动到detail page
的动画是完全正确的(从右到左)。但是当我点击Back
图标时,我希望index page
从左到右出现。
我知道如果我将CSS改为如下,页面将从左到右出现:
.pageSlider-enter {
transform: translate3d(-100%, 0, 0);
}
.pageSlider-exit.pageSlider-exit-active {
transform: translate3d(100%, 0, 0);
transition: all 600ms;
}
但是如何将两个动画结合在一起?一般来说,每当用户点击后退图标时,动画应为from left to right
。
更新:2017.08.31
感谢@MatijaG,使用路径深度真的是一个很棒的主意。我跟着它并遇到了一个新问题。
function getPathDepth(location) {
let pathArr = (location || {}).pathname.split('/');
pathArr = pathArr.filter(n => n !== '');
return pathArr.length;
}
<Route render={({ location }) =>
<TransitionGroup>
<CSSTransition
key={location.pathname.split('/')[1]}
timeout={500}
classNames={ getPathDepth(location) - this.state.prevDepth > 0 ? 'pageSliderLeft' : 'pageSliderRight' }
mountOnEnter={true}
unmountOnExit={true}
>
<Switch location={location}>
<Route path="/" exact component={ Index } />
<Route path="/comments" component={ Comments } />
<Route path="/opinions" component={ Opinions } />
<Route path="/games/lol" component={ LOL } /> // add a new route
<Route path="/games" component={ Games } />
</Switch>
</CSSTransition>
</TransitionGroup>
} />
更新了CSS:
.pageSliderLeft-enter {
transform: translate3d(100%, 0, 0);
}
.pageSliderLeft-enter.pageSliderLeft-enter-active {
transform: translate3d(0, 0, 0);
transition: all 600ms;
}
.pageSliderLeft-exit {
transform: translate3d(0, 0, 0);
}
.pageSliderLeft-exit.pageSliderLeft-exit-active {
transform: translate3d(100%, 0, 0);
transition: all 600ms;
}
.pageSliderRight-enter {
transform: translate3d(-100%, 0, 0);
}
.pageSliderRight-enter.pageSliderRight-enter-active {
transform: translate3d(0, 0, 0);
transition: all 600ms;
}
.pageSliderRight-exit {
transform: translate3d(0, 0, 0);
}
.pageSliderRight-exit.pageSliderRight-exit-active {
transform: translate3d(-100%, 0, 0);
transition: all 600ms;
}
动画:
从'/'到'/ games'是好的,从'/ games'回到'/'仍然可以(类型1:路线A - &gt;路线B ,只有2路由)。但是如果首先从'/'到'/ games',然后从'/ games'到'/ games / lol',第二阶段会失去动画(类型2:路线A - >路线B - &gt; ;路线C ,3条或更多路线)。我们还看到从'/ games / lol'回到'/ games'然后回到'/',幻灯片动画与类型1不同。
有人对这个问题有任何想法吗?
答案 0 :(得分:7)
主要的问题是,即使在您的问题中,您也没有指定应用程序应该如何知道使用哪个方向。
你能做到的一种方法是使用路径深度/长度:如果你导航到的路径比当前路径“更深”,则从右向左过渡,但如果从左到右过渡?
另一个问题是路由器只为您提供了您要去的位置,因此您应该保存以前的位置(或深度),以便将其与之比较。
之后只是在css类名之间切换, 这方面的事情:
import React from 'common/react'
import { withRouter, Route, Switch } from 'react-router'
function getPathDepth (location) {
return (location || {} ).pathname.split('/').length
}
class RouterMap extends React.PureComponent {
constructor (props, context) {
super(props, context)
this.state = {
prevDepth: getPathDepth(props.location),
}
}
componentWillReceiveProps () {
this.setState({ prevDepth: getPathDepth(this.props.location) })
}
render () {
return (
<Router>
<Route render={ ({ location }) =>
(<TransitionGroup>
<CSSTransition
key={ location.pathname.split('/')[1] }
timeout={ 500 }
classNames={ getPathDepth(location) - this.state.prevDepth ? 'pageSliderLeft' : 'pageSliderRight' } mountOnEnter={ true } unmountOnExit={ true }
>
<Switch location={ location }>
<Route path='/' exact component={ Index } />
<Route path='/comments' component={ Comments } />
<Route path='/opinions' component={ Opinions } />
<Route path='/games' component={ Games } />
</Switch>
</CSSTransition>
</TransitionGroup>)
}
/>
</Router>
)
}
}
export default withRouter(RouterMap)
Codepen示例:https://codepen.io/matijag/pen/ayXpGr?editors=0110
答案 1 :(得分:1)
扩展答案以容纳更多页面,并保持左右转换的顺序。
import React from 'react';
import './assets/scss/styles.scss';
import { Route, Switch, withRouter } from "react-router-dom";
import {TransitionGroup, CSSTransition } from 'react-transition-group';
import Header from './components/Header';
import Home from './pages/Home';
import Yellow from './pages/Yellow';
import Red from './pages/Red';
import Green from './pages/Green';
import Blue from './pages/Blue';
import Purple from './pages/Purple';
const pages = [
{ path: '/', name: 'home', order: 1 },
{ path: '/yellow', name: 'yellow', order: 2 },
{ path: '/red', name: 'red', order: 3 },
{ path: '/green', name: 'green', order: 4 },
{ path: '/blue', name: 'blue', order: 5 },
{ path: '/purple', name: 'purple', order: 6 }
]
class App extends React.Component {
constructor(props){
super(props)
this.state = {
currentPage: this.setPage(this.props.location.pathname),
curPageOrder: this.setCurrentOrder(this.props.location.pathname),
newPageOrder: null
}
}
componentDidUpdate(prevProps, prevState){
console.log('Component did update');
let newPage = this.setPage(this.props.location.pathname);
let newPageOrder = pages.filter(function (page) {
return page.name === newPage;
});
let curPage = this.state.currentPage;
let curPageOrder = pages.filter(function (page) {
return page.name === curPage;
});
if( newPage !== curPage){
console.log('new page');
let direction = curPageOrder[0].order < newPageOrder[0].order ? 'left' : 'right';
// Set State
this.setState({
currentPage: newPage,
pageDirection: direction,
curPageOrder: curPageOrder[0].order,
newPageOrder: newPageOrder[0].order,
})
}
}
setCurrentOrder = (path) => {
let curPageOrder = pages.filter(function (page) {
return page.path === path;
});
return curPageOrder[0].order;
}
setPage = (pathname) => {
// SET PAGE FOR CSS CLASSES
let page = null;
switch (pathname){
case('/'):
page = 'home';
break;
case('/yellow'):
page = 'yellow';
break;
case('/red'):
page = 'red';
break;
case('/green'):
page = 'green';
break;
case('/blue'):
page = 'blue'
break;
case('/purple'):
page = 'purple';
break;
default:
page = 'home';
}
return page;
}
render() {
const { location } = this.props;
const currentKey = location.pathname.split("/")[1] || "/";
return (
<div className={`wrapper ${this.setPage(this.props.location.pathname)}`}>
<Header />
<div className={`wrap ${currentKey} `}>
<TransitionGroup className={`transition-group ${this.state.pageDirection}`}>
<CSSTransition
key={currentKey}
timeout={{ enter: 800, exit: 400 }}
classNames={'transition-wrap'}
>
<section className={`route-section fade`}>
<Switch location={location}>
<Route exact path="/" component={() => <Home />} />
<Route path="/yellow" component={() => <Yellow /> } />
<Route path="/red" component={() => <Red /> } />
<Route path="/green" component={() => <Green /> } />
<Route path="/blue" component={() => <Blue /> } />
<Route path="/purple" component={() => <Purple /> } />
</Switch>
</section>
</CSSTransition>
</TransitionGroup>
</div>
</div>
)
}
}
export default withRouter(App);
https://codesandbox.io/s/github/darbley/react-page-slide-transitions
答案 2 :(得分:1)
我在这里带来了@Darbley答案的简化版本,它很可靠
App.js
import React, { useEffect, useState } from 'react'
import styled from 'styled-components/macro'
import { hot } from 'react-hot-loader/root'
import { setConfig } from 'react-hot-loader'
import { Switch, Route } from 'react-router-dom'
import { TransitionGroup, CSSTransition } from 'react-transition-group'
import { compose } from 'redux'
import { withRouter } from '@hoc'
import Home from '@components/Home/Home'
import About from './About/About'
import Contact from './Contact/Contact'
import Navbar from './Navbar/Navbar'
setConfig({
reloadHooks: false
})
const AppContainer = styled.main``
const pages = [
{ path: '/', order: 1 },
{ path: '/about', order: 2 },
{ path: '/contact', order: 3 }
]
const App = ({ location }) => {
const [pageDirection, setPageDirection] = useState()
const [currentPath, setCurrentPath] = useState(location.pathname)
const [currentPathOrder, setCurrentPathOrder] = useState(
pages.filter(({ path }) => path === location.pathname)[0].order
)
const currentKey = location.pathname.split('/')[1] || '/'
useEffect(() => {
const newPath = location.pathname
const newPathOrder = pages.filter(({ path }) => path === newPath)[0].order
if (newPath !== currentPath) {
const direction = currentPathOrder < newPathOrder ? 'left' : 'right'
setCurrentPath(newPath)
setCurrentPathOrder(newPathOrder)
setPageDirection(direction)
}
})
return (
<AppContainer>
<Navbar />
<TransitionGroup className={`${pageDirection}`}>
<CSSTransition key={currentKey} timeout={1000} classNames={'route'}>
<div className="route__container">
<Switch location={location}>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</div>
</CSSTransition>
</TransitionGroup>
</AppContainer>
)
}
export default process.env.NODE_ENV === 'development'
? compose(withRouter)(hot(App))
: compose(withRouter)(App)
index.scss
.route__container {
width: 100%;
height: 100vh;
position: absolute;
top: 0px;
left: 0px;
transition: 1s;
}
.left .route-enter {
transform: translateX(100%);
}
.left .route-enter-active {
transform: translateX(0%);
}
.left .route-exit {
transform: translateX(-100%);
}
.left .route-exit-active {
transform: translateX(-100%);
}
.right .route-enter {
transform: translateX(-100%);
}
.right .route-enter-active {
transform: translateX(0%);
}
.right .route-exit {
transform: translateX(100%);
}
.right .route-exit-active {
transform: translateX(100%);
}
答案 3 :(得分:0)
我为React Router v4本机写了一个AnimatedSwitch。
https://javascriptrambling.blogspot.com/2020/07/react-router-native-animatedswitch.html
我使用history.action来确定我是向前还是向后(即滑入或滑出)。然后只需确保我的转场使用了适当的操作即可匹配我想要的动画。使用history.goBack或history.replace将其滑出。正常的历史记录。按下或将其滑入。
if (history.action === 'POP' || history.action === 'REPLACE') {
setNewAnimationStyle(animationType.backNew(anim));
setPrevAnimationStyle(animationType.backPrevious(anim));
} else {
setNewAnimationStyle(animationType.new(anim));
setPrevAnimationStyle(animationType.previous(anim));
}