React过渡动画
react-transition-group介绍
- 在开发中,我们想要给一个组件的显示和消失添加某种过渡动画,提高用户体验→可通过react-transition-group实现。
- React曾为开发者提供过动画插件 react-addons-css-transition-group,后由社区维护,形成了现在的 react-transitiongroup。
- 该组件帮助我们实现组件的入场和离场动画。
- 安装方式:
bash
# npm
npm install react-transition-group --save
# yarn
yarn add react-transition-group
react-transition-group主要组件
该插件主要包含四个组件:
Transition:基础组件,用于管理单个组件的进入和退出动画
CSSTransition:基于 <Transition> 的高级组件,它通过添加 CSS 类名来实现动画效果
SwitchTransition:两个组件显示和隐藏切换时,使用该组件
TransitionGroup:将多个动画组件包裹在其中,一般用于列表中元素的动画;
CSSTransition
- 执行过程中的三个状态:appear、enter、exit
- 三种状态对应的CSS样式:
- 开始状态:xxx-appear、xxx-enter、xxx-exit
- 执行动画:xxx-appear-active、xxx-enter-active、xxx-exit-active;
- 执行结束:xxx-appear-done、xxx-enter-done、xxx-exit-done;
3.in:触发进入或者退出状态
- 当in为true时,触发进入状态,会添加-enter、-enter-acitve的class开始执行动画,当动画执行结束后,会移除两个class, 并且添加-enter-done的class;
- 当in为false时,触发退出状态,,会添加-exit、-exit-active的class开始执行动画,当动画执行结束后,会移除两个class,并且添加-enter-done的class;
App.jsx
javascript
import React, { createRef, PureComponent } from 'react'
import { CSSTransition } from "react-transition-group"
import "./style.css"
export class App extends PureComponent {
constructor(props) {
super(props)
this.state = {
isShow: true
}
this.sectionRef = createRef()
}
render() {
const { isShow } = this.state
return (
<div>
<button onClick={e => this.setState({isShow: !isShow})}>切换</button>
{/* { isShow && <h2>哈哈哈</h2> } */}
<CSSTransition
nodeRef={this.sectionRef}
in={isShow}
unmountOnExit={true}
classNames="why"
timeout={2000}
appear
onEnter={e => console.log("开始进入动画")}
onEntering={e => console.log("执行进入动画")}
onEntered={e => console.log("执行进入结束")}
onExit={e => console.log("开始离开动画")}
onExiting={e => console.log("执行离开动画")}
onExited={e => console.log("执行离开结束")}
>
<div className='section' ref={this.sectionRef}>
<h2>哈哈哈</h2>
<p>我是内容, 哈哈哈</p>
</div>
</CSSTransition>
</div>
)
}
}
export default App
style.css
css
/* 进入动画 */
/* .why-appear {
transform: translateX(-150px);
}
.why-appear-active {
transform: translateX(0);
transition: transform 2s ease;
} */
.why-appear, .why-enter {
opacity: 0;
}
.why-appear-active, .why-enter-active {
opacity: 1;
transition: opacity 2s ease;
}
/* 离开动画 */
.why-exit {
opacity: 1;
}
.why-exit-active {
opacity: 0;
transition: opacity 2s ease;
}
SwitchTransition
1.核心属性:mode
- in-out:表示新组件先进入,旧组件再移除;
- out-in:表示就组件先移除,新组建再进入;
2.子组件必须是 CSSTransition 或 Transition
3.通过 key
属性来区分不同的子组件
App.jsx
javascript
import React, { PureComponent } from 'react'
import { SwitchTransition, CSSTransition } from 'react-transition-group'
import "./style.css"
export class App extends PureComponent {
constructor() {
super()
this.state = {
isLogin: true
}
}
render() {
const { isLogin } = this.state
return (
<div>
<SwitchTransition mode='out-in'>
<CSSTransition
key={isLogin ? "exit": "login"}
classNames="login"
timeout={1000}
>
<button onClick={e => this.setState({ isLogin: !isLogin })}>
{ isLogin ? "退出": "登录" }
</button>
</CSSTransition>
</SwitchTransition>
</div>
)
}
}
export default App
style.css
css
.login-enter {
transform: translateX(100px);
opacity: 0;
}
.login-enter-active {
transform: translateX(0);
opacity: 1;
transition: all 1s ease;
}
.login-exit {
transform: translateX(0);
opacity: 1;
}
.login-exit-active {
transform: translateX(-100px);
opacity: 0;
transition: all 1s ease;
}
TransitionGroup
可同时管理多个 Transition
或 CSSTransition
组件
App.jsx
javascript
import React, { PureComponent } from 'react'
import { TransitionGroup, CSSTransition } from "react-transition-group"
import "./style.css"
export class App extends PureComponent {
constructor() {
super()
this.state = {
books: [
{ id: 111, name: "你不知道JS", price: 99 },
{ id: 222, name: "JS高级程序设计", price: 88 },
{ id: 333, name: "Vuejs高级设计", price: 77 },
]
}
}
addNewBook() {
const books = [...this.state.books]
books.push({ id: new Date().getTime(), name: "React高级程序设计", price: 99 })
this.setState({ books })
}
removeBook(index) {
const books = [...this.state.books]
books.splice(index, 1)
this.setState({ books })
}
render() {
const { books } = this.state
return (
<div>
<h2>书籍列表:</h2>
<TransitionGroup component="ul">
{
books.map((item, index) => {
return (
<CSSTransition key={item.id} classNames="book" timeout={1000}>
<li>
<span>{item.name}-{item.price}</span>
<button onClick={e => this.removeBook(index)}>删除</button>
</li>
</CSSTransition>
)
})
}
</TransitionGroup>
<button onClick={e => this.addNewBook()}>添加新书籍</button>
</div>
)
}
}
export default App
style.css
css
.book-enter {
transform: translateX(150px);
opacity: 0;
}
.book-enter-active {
transform: translateX(0);
opacity: 1;
transition: all 1s ease;
}
.book-exit {
transform: translateX(0);
opacity: 1;
}
.book-exit-active {
transform: translateX(150px);
opacity: 0;
transition: all 1s ease;
}
React编写CSS方式
内联样式style
写在行内,传入一个小驼峰命名的js对象,并且可以设置动态的样式
javascript
import React, { PureComponent } from 'react'
export class App extends PureComponent {
constructor() {
super()
this.state = {
titleSize: 30
}
}
addTitleSize() {
this.setState({ titleSize: this.state.titleSize + 2 })
}
render() {
const { titleSize } = this.state
return (
<div>
<button onClick={e => this.addTitleSize()}>增加titleSize</button>
<h2 style={{color: "red", fontSize: `${titleSize}px`}}>我是标题</h2>
<p style={{color: "blue", fontSize: "20px"}}>我是内容, 哈哈哈</p>
</div>
)
}
}
export default App
CSS Module
将CSS样式封装到组件中,为每个组件生成唯一的类名,确保样式作用域仅限于当前组件,从而避免样式冲突
步骤一:创建一个名为 App.module.css
的文件
css
.title {
font-size: 32px;
color: green;
}
.content {
font-size: 22px;
color: orange;
}
步骤二:通过 import 导入 CSS Module 文件,并使用生成的类名
javascript
import React, { PureComponent } from 'react'
import Home from './home/Home'
import Profile from './profile/Profile'
import appStyle from "./App.module.css"
export class App extends PureComponent {
render() {
return (
<div>
<h2 className={appStyle.title}>我是标题</h2>
<p className={appStyle.content}>我是内容, 哈哈哈哈</p>
<Home/>
<Profile/>
</div>
)
}
}
export default App
工作原理:CSS Modules 在构建过程中会自动为每个类名生成一个唯一的哈希值
Less编写
1. 安装 Less 和相关依赖
npm install less less-loader --save-dev
2.配置 Webpack 支持 Less
对于 Create React App 项目
由于 Create React App 默认隐藏了 Webpack 配置,可以通过 craco
(Create React App Configuration Override)来修改配置:
step1:安装 craco
和 craco-less
:
npm install @craco/craco craco-less --save-dev
step2:创建 craco.config.js 文件,并配置 Less:
javascript
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1e80ff' },
javascriptEnabled: true,
},
},
},
},
],
};
step3:修改 package.json
中的脚本命令,将 react-scripts
替换为 craco
:
javascript
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
}
3.使用 Less 编写样式
step1:定义全局变量
css
@primaryColor: red;
.section {
border: 1px solid @primaryColor;
.title {
font-size: 30px;
color: @primaryColor;
}
.content {
font-size: 20px;
color: @primaryColor;
}
}
step2:在组件中使用 Less
javascript
import React, { PureComponent } from 'react'
import "./App.less"
export class App extends PureComponent {
render() {
return (
<div className='app'>
<div className='section'>
<h2 className='title'>我是标题</h2>
<p className='content'>我是内容, 哈哈哈</p>
</div>
</div>
)
}
}
export default App
CSS in JS
React思想中认为逻辑本身和UI是无法分离的,所有才有了JSX语法;样式也是UI的一部分,CSS-in-JS就是将样式写入到JS或TS中的方式
1.工作原理:
- 将样式定义在 JavaScript 中,并通过动态生成的类名或内联样式直接应用于组件
- 这种方式使得样式的作用域完全限定在组件内部,避免了全局样式冲突
2.基本使用
step1:安装styled-components:使用模板字符串定义样式
npm i styled-components
step2:定义一个style.js文件
javascript
import styled from "styled-components"
export const AppWrapper = styled.div`
border: 1px solid orange;
`
step3:导入组件中
javascript
import React, { PureComponent } from 'react'
import { AppWrapper} from "./style"
export class App extends PureComponent {
render() {
return (
<AppWrapper>
<h2 className='title'>我是标题</h2>
<p className='content'>我是内容, 哈哈哈</p>
</AppWrapper>
)
}
}
export default App
3.props接收数据
获取props需要通过${}传入一个插值函数,props会作为该函数的参数

4. attrs属性
用于传递一组额外的属性到组件中,通常与props一起使用
当传过来的有值时使用传过来的,没值时设置一个默认样式
javascript
export const SectionWrapper = styled.div.attrs(props => ({
tColor: props.color || "blue"
}))`
border: 1px solid red;
.title {
font-size: ${props => props.size}px;
color: ${props => props.tColor};
&:hover {
background-color: purple;
}
}
}
`
5.styled高级特性
支持样式继承
javascript
import styled from "styled-components";
const HYButton = styled.button`
border: 1px solid red;
border-radius: 5px;
`
export const HYButtonWrapper = styled(HYButton)`
background-color: #0f0;
color: #fff;
`
styled设置主题
可用来设置和管理主题样式

classnames库
我们在react中可借用一个第三方库classnames来动态添加某些类
step1:安装
npm install classnames
step2:使用
javascript
import React, { PureComponent } from 'react'
import classNames from 'classnames'
export class App extends PureComponent {
constructor() {
super()
this.state = {
isbbb: true,
isccc: true
}
}
render() {
const { isbbb, isccc } = this.state
const classList = ["aaa"]
if (isbbb) classList.push("bbb")
if (isccc) classList.push("ccc")
const classname = classList.join(" ")
return (
<div>
<h2 className={`aaa ${isbbb ? 'bbb': ''} ${isccc ? 'ccc': ''}`}>哈哈哈</h2>
<h2 className={classname}>呵呵呵</h2>
<h2 className={classNames("aaa", { bbb:isbbb, ccc:isccc })}>嘿嘿嘿</h2>
<h2 className={classNames(["aaa", { bbb: isbbb, ccc: isccc }])}>嘻嘻嘻</h2>
</div>
)
}
}
export default App