以前一直投入在
中,写动画的时候不是用
- React Native
,就是依赖像
- CSS 中的 transitions / animations
这样的库,最近转向
- GreenSock
,在
- Web
得到很多大佬关于
- Tweet
的回应,于是决定分享给大家,如有其他见解,非常欢迎在下面
- React Web 动画
中交流
- 评论
以下便是本文要分享的创建
的几种方式
- React 动画
下面,勒次个特斯大特一特
给元素添加
是最简单,最常见的书写方式。如果你的
- class
正在使用
- app
,那么这将是你最愉快的选择
- CSS
: 我们只需修改
- 赞同者
和
- opacity
这样的属性,就可构建基本的动画,而且,在组件中,我们可以非常容易地通过
- transform
去更新这些值
- state
:这种方式并
- 反对者
,在
- 不跨平台
中就不适用,而且,对于较复杂的动画,这种方式
- React Native
- 难以控制
接下来,我们通过一个实例来体验一下这种创建方式:
- 当 input focus 的时候,我们增加它的宽度
首先,我们要创建两个
要用到的
- input
- class
- .input {
- width: 150px;
- padding: 10px;
- font - size: 20px;
- border: none;
- border - radius: 4px;
- background - color: #dddddd;
- transition: width.35s linear;
- outline: none;
- }
- .input - focused {
- width: 240px;
- }
一个是它
的样式,一个是它
- 原始
后的样式
- focus
下面,我们就开始书写我们的
- React 组件
在此,推荐一个 在线的 React VS Code IDE ,真的很强大,读者不想构建自己的
,可以在其中检验以下代码的正确性
- React app
- class App extends Component {
- state = {
- focused: false,
- }
- componentDidMount() {
- this._input.addEventListener('focus', this.focus);
- this._input.addEventListener('blur', this.focus);
- }
- focus = () = >{
- this.setState(prevState = >({
- focused: !prevState.focused,
- }));
- }
- render() {
- return ( < div className = "App" > <div className = "container" > <input ref = {
- input = >this._input = input
- }
- className = { ['input', this.state.focused && 'input-focused'].join(' ')
- }
- />
- </div > </div>
- );
- }
- }/
的
- focused
,初始值为
- state
,我们通过
- false
来创建我们的动画
- 更新该值
中,我们添加两个
- componentDidMount
,一个
- 监听器
,一个
- focus
,指定的
- blur
是
- 回调函数都
- focus
方法会获取之前
- focus
的值,并负责
- focused
该值
- 切换
中,我们通过
- render
来改变
- state
的
- input
,从而实现我们的动画
- classNames
跟
- JavaScipt styles
类似,在
- CSS 中的 classes
文件中,我们就可以拥有所有逻辑
- JS
:跟
- 赞同者
一样,且它的表现更加清晰。它也不失为一个好方法,可以不必依赖任何
- CSS 动画
- CSS
:跟
- 反对者
一样,也是
- CSS 动画
的,且动画一旦复杂,也
- 不跨平台
- 难以控制
在下面的实例中,我们将创建一个
,当用户输入时,我们将一个
- input
从
- button
转变为
- disable
- enable
- class App extends Component {
- state = {
- disabled: true,
- }
- onChange = (e) => {
- const length = e.target.value.length;
- if (length > 0) {
- this.setState({ disabled: false });
- } else {
- this.setState({ disabled: true });
- }
- }
- render() {
- const { disabled } = this.state;
- const label = disabled ? 'Disabled' : 'Submit';
- return (
- <div style={styles.App}>
- <input
- style={styles.input}
- onChange={this.onChange}
- />
- <button
- style={Object.assign({},
- styles.button,
- !this.state.disabled && styles.buttonEnabled
- )}
- disabled={disabled}
- >
- {label}
- </button>
- </div>
- );
- }
- }
- const styles = {
- App: {
- display: 'flex',
- justifyContent: 'left',
- },
- input: {
- marginRight: 10,
- padding: 10,
- width: 190,
- fontSize: 20,
- border: 'none',
- backgroundColor: '#ddd',
- outline: 'none',
- },
- button: {
- width: 90,
- height: 43,
- fontSize: 17,
- border: 'none',
- borderRadius: 4,
- transition: '.25s all',
- cursor: 'pointer',
- },
- buttonEnabled: {
- width: 120,
- backgroundColor: '#ffc107',
- }
- }
的
- disabled
,初始值为
- state
- true
方法会获取用户的输入,当输入非空时,就切换
- onChange
的值
- disabled
的值,确定是否将
- disabled
添加到
- buttonEnabled
中
- button
是 Cheng Lou 书写的一个非常不错的开源项目。它的思想是你可以对
- React Motion
进行简单的
- Motion 组件
,然后你就可以在
- 样式设置
中通过这些值,享受动画带来的乐趣
- 回调函数
对于绝大多数的动画组件,我们往往不希望对
(宽高、颜色等)的变化时间做
- 动画属性
处理,
- 硬编码
提供的
- react-motion
函数就是用来解决这一需求的,它可以逼真地模仿真实的物理效果,也就是我们常见的各类
- spring
- 缓动效果
下面是一个
的示例
- 森破
- <Motion style={{ x: spring(this.state.x) }}>
- {
- ({ x }) =>
- <div style={{ transform: `translateX(${x}px)` }} />
- }
- </Motion>
这是官方提供的几个
,真的可以是
- demo
- 不看不知道,一看吓一跳
:
- 赞同者
可以在
- React Motion
中使用,也可以在
- React Web
中使用,因为它是跨平台的。其中的
- React Native
概念最开始对我来说感觉挺陌生,然而上手之后,发现它真的很
- spring
,并且,它有很详细的
- 神奇
- API
:在某些情况下,他不如
- 反对者
,虽然它有不错的
- 纯 CSS / JS 动画
,容易上手,但也需要学习成本
- API
为了使用它,首先我们要用
或
- yarn
安装它
- npm
- yarn add react - motion
在下面的实例中,我们将创建一个
,当点击按钮时,下拉菜单友好展开
- dropdown 菜单
- class App extends Component {
- state = {
- height: 38,
- }
- animate = () = >{
- this.setState((state) = >({
- height: state.height === 233 ? 38 : 233
- }));
- }
- render() {
- return ( < div className = "App" > <div style = {
- styles.button
- }
- onClick = {
- this.animate
- } > Animate < /div>
- <Motion
- style={{ height: spring(this.state.height) }}
- >
- {
- ({ height }) =>
- <div style={Object.assign({}, styles.menu, { height } )}>
- <p style={styles.selection}>Selection 1</p > <p style = {
- styles.selection
- } > Selection 2 < /p>
- <p style={styles.selection}>Selection 3</p > <p style = {
- styles.selection
- } > Selection 4 < /p>
- <p style={styles.selection}>Selection 5</p > <p style = {
- styles.selection
- } > Selection 6 < /p>
- </div >
- } < /Motion>
- </div > );
- }
- }
- const styles = {
- menu: {
- marginTop: 20,
- width: 300,
- border: '2px solid #ddd',
- overflow: 'hidden',
- },
- button: {
- display: 'flex',
- width: 200,
- height: 45,
- justifyContent: 'center',
- alignItems: 'center',
- border: 'none',
- borderRadius: 4,
- backgroundColor: '#ffc107',
- cursor: 'pointer',
- },
- selection: {
- margin: 0,
- padding: 10,
- borderBottom: '1px solid #ededed',
- },
- }
``
中 import
- react-motion
和
- Motion
- spring
的
- height
,初始值为
- state
,代表
- 38
的高度
- menu
方法设置
- animate
的
- menu
,如果
- height
为
- 原 height
,则设置
- 38
为
- 新 height
,如果
- 233
为
- 原 height
,则设置
- 233
为
- 新 height
- 38
中,我们使用
- render
包装整个
- Motion 组件
列表,将
- p 标签
的当前值设为组件的
- this.state.height
,然后在组件的
- height
中使用该值作为整个下拉的高度
- 回调函数
切换下拉的高度
- this.animate
是基于
- Animated
使用的同一个动画库建立起来的
- React Native
它背后的思想是创建
,通过
- 声明式动画
- 传递配置对象来控制动画
:
- 赞同者
,它在
- 跨平台
中已经非常稳定,如果你在
- React Native
中使用过,那么你将不用再重复学习。其中的
- React Native
是一个神奇的插值函数,我们将在下面看到
- interpolate
:基于
- 反对者
的交流,它目前貌似不是
的稳定,在老的浏览器中的,存在
- 100%
和
- 前缀
的问题,而且,它也有学习成本
- 性能
为了使用
,我们首先还是要用
- Animated
或
- yarn
安装它
- npm
- yarn add animated
在下面的实例中,我们将模拟在提交表单成功后显示的动画
- message
- import Animated from 'animated/lib/targets/react-dom';
- import Easing from 'animated/lib/Easing';
- class AnimatedApp extends Component {
- animatedValue = new Animated.Value(0);
- animate = () = >{
- this.animatedValue.setValue(0);
- Animated.timing(this.animatedValue, {
- toValue: 1,
- duration: 1000,
- easing: Easing.elastic(1),
- }).start();
- }
- render() {
- const marginLeft = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [ - 120, 0],
- });
- return ( < div className = "App" > <div style = {
- styles.button
- }
- onClick = {
- this.animate
- } > Animate < /div>
- <Animated.div
- style={
- Object.assign(
- {},
- styles.box,
- { opacity: this.animatedValue, marginLeft })}
- >
- <p>Thanks for your submission!</p > </Animated.div>
- </div > -我们将`animatedValue`和`marginLeft`作为`Animated.div`的`style`属性,);
- }
- }
- const styles = {
- button: {
- display: 'flex',
- width: 125,
- height: 50,
- justifyContent: 'center',
- alignItems: 'center',
- border: 'none',
- borderRadius: 4,
- backgroundColor: '#ffc107',
- cursor: 'pointer',
- },
- box: {
- display: 'inline-block',
- marginTop: 10,
- padding: '0.6rem 2rem',
- fontSize: '0.8rem',
- border: '1px #eee solid',
- borderRadius: 4,
- boxShadow: '0 2px 8px rgba(0,0,0,.2)',
- },
- }
中 import
- animated
和
- Animated
- Easing
创建一个值为
- new Animated.Value(0)
的类属性 -
- 0
- animatedValue
方法,处理所有的动画,首先通过
- animate
初始化动画值,实现的效果就是每次
- this.animatedValue.setValue(0)
该动画,然后调用
- 重新执行
,
- Animated.timing
作为第一个参数传递,
- animatedValue
作为第二个参数,一个设置
- 配置对象
,一个设置
- 最终动画值
,一个设置
- 持续时间
- 缓动效果
中,我们用
- render
方法创建
- interpolate
对象,包含
- marginLeft
和
- inputRange
数组,我们使用此对象作为
- outputRange
中
- UI
的
- message
属性
- style
替代默认的
- Animated.div
- div
和
- animatedValue
作为
- marginLeft
的
- Animated.div
属性
- style
是基于已经存在的
- Velocity React
建立起来的
- Velocity
:上手容易,
- 赞同者
简单明了,相对其他库更易于掌握
- API
:有些不得不克服的问题,比如
- 反对者
后动画并没有真正地起作用等,而且,它
- componentDidMount
- 不跨平台
下面是一个
的示例
- 森破
- <VelocityComponent
- animation={{ opacity: this.state.showSubComponent ? 1 : 0 }}
- duration={500}
- >
- <MySubComponent/>
- </VelocityComponent>
首先还是要用
或
- yarn
安装它
- npm
- yarn add velocity - react
在下面的实例中,我们将创建一个很酷的
- 动画输入
- import { VelocityComponent } from 'velocity-react';
- const VelocityLetter = ({ letter }) => (
- <VelocityComponent
- runOnMount
- animation={{ opacity: 1, marginTop: 0 }}
- duration={500}
- >
- <p style={styles.letter}>{letter}</p>
- </VelocityComponent>
- )
- class VelocityApp extends Component {
- state = {
- letters: [],
- }
- onChange = (e) => {
- const letters = e.target.value.split('');
- const arr = [];
- letters.forEach((l, i) => {
- arr.push(<VelocityLetter letter={l} />)
- });
- this.setState({ letters: arr });
- }
- render() {
- return (
- <div className="App">
- <div className="container">
- <input onChange={this.onChange} style={styles.input} />
- <div style={styles.letters}>
- {
- this.state.letters
- }
- </div>
- </div>
- </div>
- );
- }
- }
- const styles = {
- input: {
- marginBottom: 20,
- padding: 8,
- width: 200,
- height: 40,
- fontSize: 22,
- backgroundColor: '#ddd',
- border: 'none',
- outline: 'none',
- },
- letters: {
- display: 'flex',
- height: 140,
- },
- letter: {
- marginTop: 100,
- fontSize: 22,
- whiteSpace: 'pre',
- opacity: 0,
- }
- }
中 import
- velocity-react
- VelocityComponent
使用的组件来满足每个
- 可重复
的动画
- letter
的
- animation
设为
- opacity
,
- 1
设为
- marginTop
,这些值代表着传入子组件的
- 0
,即当组件被创建时,组件的
- 重写值
会由初始的
- opacity
变为
- 0
,
- 1
会由初始的
- marginTop
变为
- 100
,我们还设置了
- 0
的持续时间,最后值得一提的是
- 500 ms
属性,它的意思是在组件
- runOnMount
或
- 挂载
完后执行该动画
- 创建
方法会获取用户的每次输入,并创建一个由
- onChange
组成的新数组
- VelocityLetter
中,我们就使用该数组在
- render
中渲染
- UI
- letters
总的来说,基本的动画,我会选择
,复杂的动画,我更偏向
- JS style
。而对于
- React Motion
,我还是坚持使用
- React Native
,一旦
- Animated
成熟,在
- Animated
中可能也会投入使用,目前,我真的很享受
- Web
- React Motion
: React Animations in Depth
- 原文链接
来源: http://www.tuicool.com/articles/fyyaeij