blog
blog copied to clipboard
React移动端添加路由转场动画
移动端添加转场动画
移动端底部有菜单强烈建议采用flex竖向布局,不要使用
position: fixed
去定位菜单到底部
原React路由切换效果
实现的效果
实现
安装官方推荐动画库依赖
yarn add react-transition-group
使用
import {CSSTransition, TransitionGroup} from 'react-transition-group';
const ANIMATION_MAP = {
PUSH: 'forward',
POP: 'back',
REPLACE: 'replace'// 底部菜单的页面不需要使用动态路由,底部菜单使用replace替换路径,防止菜单也会跟着转场动画
}
<TransitionGroup
className={'router-wrapper'}
childFactory={child => React.cloneElement(
child,
{classNames: ANIMATION_MAP[history.action]}
)}
>
<CSSTransition
timeout={500}
key={location.pathname}
>
<Suspense fallback={<div>加载中...</div>}>
<Switch>
<Route exact path={'/'} component={HomePage} />
<Route exact path={'/about'} component={AboutPage} />
<Route exact path={'/list'} component={ListPage} />
<Route exact path={'/detail'} component={DetailPage} />
</Switch>
</Suspense>
</CSSTransition>
</TransitionGroup>
懒加载和非懒加载的区别
import { HomePage, AboutPage, ListPage, DetailPage } from '@/components/component/index';
const HomePage = lazy(() => import('@/components/component/HomePage'));
const AboutPage = lazy(() => import('@/components/component/AboutPage'));
const ListPage = lazy(() => import('@/components/component/ListPage'));
const DetailPage = lazy(() => import('@/components/component/DetailPage'));
network 页面会闪
引入样式
:global{
.router-wrapper {
height: 100%;
}
.forward-enter {
opacity: 0;
transform: translateX(100%);
box-shadow: 0 4px 7px rgba(0,0,0,.4);
transition-timing-function:linear;
}
.forward-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 120ms;
transition-timing-function:linear;
}
.forward-exit {
opacity: 1;
transform: translateX(0);
transition-timing-function:linear;
}
.forward-exit-active {
opacity: 0;
transform: translateX(-100%);
transition: all 120ms;
transition-timing-function:linear;
}
.back-enter {
opacity: 0;
transform: translateX(-100%);
box-shadow: 0 4px 7px rgba(0,0,0,.4);
transition-timing-function:linear;
}
.back-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 120ms;
transition-timing-function:linear;
}
.back-exit {
opacity: 1;
transform: translateX(0);
transition-timing-function:linear;
}
.back-exit-active {
opacity: 0;
transform: translate(100%);
transition: all 120ms;
transition-timing-function:linear;
}
.replace-enter >div:first-child{
opacity: 0.01;
}
.replace-enter-active >div:first-child{
opacity: 1;
transition: opacity 250ms ease-in;
}
.replace-exit >div:first-child{
opacity: 1;
}
.replace-exit-active >div:first-child{
opacity: 0.01;
transition: opacity 250ms ease-in;
}
}
改造dva路由
import { Switch, Route, withRouter, HashRouter } from 'react-router-dom';
const Routes = withRouter(({ history, location }) => (
<TransitionGroup
className={'router-wrapper'}
childFactory={child => React.cloneElement(
child,
{ classNames: ANIMATION_MAP[history.action] }
)}
>
<CSSTransition
timeout={500}
key={location.pathname}
>
<Suspense fallback={<div>加载中...</div>}>
<Switch>
<Route exact path={'/'} component={HomePage} />
<Route exact path={'/about'} component={AboutPage} />
<Route exact path={'/list'} component={ListPage} />
<Route exact path={'/detail'} component={DetailPage} />
</Switch>
</Suspense>
</CSSTransition>
</TransitionGroup>
));
export default () => {
return (
<HashRouter>
<Routes />
</HashRouter>
);
};
懒加载没有效果?
试下不使用懒加载,不使用懒加载有效果
改造适合懒加载
<div className="mobile-main">
<Suspense fallback={<Loading />} maxDuration={500}>
<Switch location={location}>
...
</Switch>
</Suspense>
</div>
修改index.scss,添加样式
.mobile-main{
position: relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
}
大功告成
其他效果
项目运用
https://cli.im/ 二维码生成
其他问题
- 页面有滚动条,底部有菜单,样式有bug;
- 菜单点击滚动到顶部:采用flex竖向布局,菜单在底部
完整代码
## router.js
import './index.scss';
import React, { lazy, Suspense } from 'react';
import { Route, Switch, HashRouter, withRouter } from 'react-router-dom';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
const HomePage = lazy(() => import('@/components/component/HomePage')); //路由组件
const AboutPage = lazy(() => import('@/components/component/AboutPage')); //路由组件
const ListPage = lazy(() => import('@/components/component/ListPage')); //路由组件
const DetailPage = lazy(() => import('@/components/component/DetailPage')); //路由组件
// import { HomePage, AboutPage, ListPage, DetailPage } from '@/components/component/index';
const ANIMATION_MAP = {
PUSH: 'forward',
POP: 'back',
REPLACE: 'replace'
};
const Router = withRouter(({ history, location }) => (
<TransitionGroup
className={'router-wrapper'}
fade
childFactory={child => React.cloneElement(
child,
{ classNames: ANIMATION_MAP[history.action] }
)}
>
<CSSTransition
timeout={500}
key={location.pathname}
>
<div className="mobile-main">
<Suspense fallback={<div>加载中...</div>}>
<Switch>
<Route exact path={'/'} component={HomePage} />
<Route exact path={'/about'} component={AboutPage} />
<Route exact path={'/list'} component={ListPage} />
<Route exact path={'/detail'} component={DetailPage} />
</Switch>
</Suspense>
</div>
</CSSTransition>
</TransitionGroup>
));
export default () => (<HashRouter><Router /></HashRouter>);
样式
:global{
.router-wrapper {
height: 100%;
}
.mobile-main{
position: relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
}
.forward-enter {
opacity: 0;
transform: translateX(100%);
}
.forward-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 500ms;
}
.forward-exit {
opacity: 1;
transform: translateX(0);
}
.forward-exit-active {
opacity: 0;
transform: translateX(-100%);
transition: all 500ms;
}
.back-enter {
opacity: 0;
transform: translateX(-100%);
}
.back-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 500ms;
}
.back-exit {
opacity: 1;
transform: translateX(0);
}
.back-exit-active {
opacity: 0;
transform: translate(100%);
transition: all 500ms;
}
.replace-enter >div:first-child{
opacity: 0.01;
}
.replace-enter-active >div:first-child{
opacity: 1;
transition: opacity 250ms ease-in;
}
.replace-exit >div:first-child{
opacity: 1;
}
.replace-exit-active >div:first-child{
opacity: 0.01;
transition: opacity 250ms ease-in;
}
}
入口JS
import 'bootstrap/dist/css/bootstrap.min.css';
import 'font-awesome/css/font-awesome.min.css';// shareui-font版本迁移完成之后删除该依赖
import '@share/shareui-html';
import '@share/shareui-font';
import '@/components/form';
import dva from 'dva';
import router from './router';
// 1. Initialize
const app = dva();
// 2. Plugins
// app.use({});
// 3. Model
// app.model();
// 4. Router
app.router(router);
// 5. Start
app.start('#root');
底部菜单路由,不用懒加载,不然会闪一下,其他非菜单页可以使用