官方文档
https://reactcommunity.org/react-transition-group/
安装
cnpm i --save-dev react-transition-group
"react-transition-group": "^4.4.2",
插件中的组件
CSSTransition css过渡组件
分为入场动画和过渡动画
-
*-enter-active表示想要动画到那些样式
-
*-exit-active 表示离开之后的动画
属性
classNames属性是在组件进入、退出时应用于组件的动画类名称。每个阶段都会加上后缀,
例如
.fade-enter 入场动画第一帧 .fade-enter-active 入场动画第二帧 .fade-enter-done 入场动画执行完成 .fade-exit 出场动画第一帧 .fade-exit-active 出场动画第二帧 .fade-exit-done 出场动画结束 .fade-appear .fade-appear-active 功能和入场动画一致 需要添加属性appear={true} 首次渲染时执行动画
import './App.css'
import { useState } from 'react'
//引入过渡动画
import { CSSTransition } from 'react-transition-group'
console.log(CSSTransition);
function App() {
let [show, setShow] = useState(true);
let handle = function () {
setShow(!show);
}
return (
<div className="App">
<CSSTransition
in={show} //显示隐藏
timeout={1000} //动画时间
classNames={'fade'}
unmountOnExit={true} //隐藏移除dom
appear={true}
>
<span>App组件</span>
</CSSTransition>
<button onClick={handle}>显示隐藏</button>
</div>
)
}
export default App
.fade-enter,.fade-appear{
opacity: 0;
}
.fade-enter-active,.fade-appear-active{
opacity: 1;
transition: opacity .5s;
}
.fade-exit{
opacity: 1;
}
.fade-exit-active{
opacity: 0;
transition: opacity .5s;
}
.fade-exit-done{
opacity: 0;
}
如果实现多个标签动画使用动画组
TransitionGroup组件
路由组件实现动画
<TransitionGroup className='box-animate'>
<CSSTransition classNames={"fade"} timeout={1000} key={location.pathname} >
<Switch location={location}>
<Route path="/about" >
<About />
</Route>
<Route path="/users">
<User />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</CSSTransition>
</TransitionGroup>
.fade-enter {
opacity: 0;
transform: translateX(100%);
}
.fade-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 500ms;
}
.fade-exit {
opacity: 1;
transform: translateX(0);
}
.fade-exit-active {
opacity: 0;
transform: translateX(-100%);
transition: all 500ms;
}
如何实现路由组件根据路由的进入和离开 控制动画
childFactory 您可能需要在孩子退出时对其应用响应式更新。这通常是通过使用来完成的,cloneElement但是在现有子元素的情况下,该元素已被删除并且消费者无法访问。 如果您确实需要在孩子离开时对其进行更新,您可以提供一个childFactory 来包装每个孩子,即使是那些正在离开的孩子。 类型:Function(child: ReactElement) -> ReactElement 默认:child => child
多个动画切换
// src/App6/RouteConfig.js
export const RouterConfig = [
{
path: '/',
component: HomePage
},
{
path: '/about',
component: AboutPage,
sceneConfig: {
enter: 'from-bottom',
exit: 'to-bottom'
}
},
{
path: '/list',
component: ListPage,
sceneConfig: {
enter: 'from-right',
exit: 'to-right'
}
},
{
path: '/detail',
component: DetailPage,
sceneConfig: {
enter: 'from-right',
exit: 'to-right'
}
}
];
index.js
// src/App6/index.js
const DEFAULT_SCENE_CONFIG = {
enter: 'from-right',
exit: 'to-exit'
};
const getSceneConfig = location => {
const matchedRoute = RouterConfig.find(config => new RegExp(`^${config.path}$`).test(location.pathname));
return (matchedRoute && matchedRoute.sceneConfig) || DEFAULT_SCENE_CONFIG;
};
let oldLocation = null;
const Routes = withRouter(({location, history}) => {
// 转场动画应该都是采用当前页面的sceneConfig,所以:
// push操作时,用新location匹配的路由sceneConfig
// pop操作时,用旧location匹配的路由sceneConfig
let classNames = '';
if(history.action === 'PUSH') {
classNames = 'forward-' + getSceneConfig(location).enter;
} else if(history.action === 'POP' && oldLocation) {
classNames = 'back-' + getSceneConfig(oldLocation).exit;
}
// 更新旧location
oldLocation = location;
return (
<TransitionGroup
className={'router-wrapper'}
childFactory={child => React.cloneElement(child, {classNames})}
>
<CSSTransition timeout={500} key={location.pathname}>
<Switch location={location}>
{RouterConfig.map((config, index) => (
<Route exact key={index} {...config}/>
))}
</Switch>
</CSSTransition>
</TransitionGroup>
);
});
vite工具配置@
export default defineConfig({
plugins: [react()],
resolve:{
alias:{
"@":resolve('src')
}
}
})
react原生项目中配置@
安装@craco/craco
在src同级目录创建文件
// src/App6/index.js
const DEFAULT_SCENE_CONFIG = {
enter: 'from-right',
exit: 'to-exit'
};
const getSceneConfig = location => {
const matchedRoute = RouterConfig.find(config => new RegExp(`^${config.path}$`).test(location.pathname));
return (matchedRoute && matchedRoute.sceneConfig) || DEFAULT_SCENE_CONFIG;
};
let oldLocation = null;
const Routes = withRouter(({location, history}) => {
// 转场动画应该都是采用当前页面的sceneConfig,所以:
// push操作时,用新location匹配的路由sceneConfig
// pop操作时,用旧location匹配的路由sceneConfig
let classNames = '';
if(history.action === 'PUSH') {
classNames = 'forward-' + getSceneConfig(location).enter;
} else if(history.action === 'POP' && oldLocation) {
classNames = 'back-' + getSceneConfig(oldLocation).exit;
}
// 更新旧location
oldLocation = location;
return (
<TransitionGroup
className={'router-wrapper'}
childFactory={child => React.cloneElement(child, {classNames})}
>
<CSSTransition timeout={500} key={location.pathname}>
<Switch location={location}>
{RouterConfig.map((config, index) => (
<Route exact key={index} {...config}/>
))}
</Switch>
</CSSTransition>
</TransitionGroup>
);
});
vite工具配置@
export default defineConfig({
plugins: [react()],
resolve:{
alias:{
"@":resolve('src')
}
}
})