用react-motion实现react动画

怎么写react的动画一直是让我比较头疼的问题,之前一直在使用官方的react-transition-group,感觉并不是很好用,也没有找到很好的替代方案。恰巧最近在知乎看了这样一篇文章(https://zhuanlan.zhihu.com/p/28500217),也就借着机会研究了下react-motion

react-motion

官方文档: https://github.com/chenglou/react-motion

相比较react-transition-group,用react-motion进行动画的编写显得要直观很多,写法类似这样

<Motion defaultStyle={{left: 0}} style={{left: spring(10)}}>
  {interpolatingStyle => <div style={interpolatingStyle} />}
</Motion>

指定一个初始style(defaultStyle),然后赋值一个目标style(style),中间每帧都会由react-motion计算出对应的style,用户只管使用生成的style(interpolatingStyle),不用关心物理效果的实现,动画中断的处理,一切事情都交给react-motion

基本概念

spring

spring是react-motion最简单也是最深奥的一个函数,是react-motion构筑动画的基石,用户可以通过spring实现各种物理效果,下面是官方文档中对spring的描述:

image.png

接受两个参数,val和config

  • val: 终点值,即你希望达到的最后状态的数值,number类型
  • config: 用于生成物理效果的配置,关于stiffness和damping,下面着重介绍,precision用于配置val的精确度,一般我们用默认的0.01就行

stiffness 和 damping

我们可以通过spring(val, {stiffness: ?, damping: ?})来生成各种缓动效果,关于stiffness和damping,如果用书面形式说明可能会比较难以理解,所以请点击下面的官方链接感受下

http://chenglou.github.io/react-motion/demos/demo5-spring-parameters-chooser/

如果把我们要设置动画的物体想象成弹簧,stiffness相当于弹簧的强度,其影响的是弹簧回弹的速度,相同damping情况下,stiffness越大,回弹速度越快;damping是弹簧的减震性,其影响的是弹簧的回弹次数,相同stiffness情况下,damping越大,回弹次数越少

presets

一般情况下,大多数用户其实是不想手动去配置stiffness和damping的,所以react-motion也单独提供了一些预设值,可以通过下面的方式进行使用

import {presets} from 'react-motion'
//...
<Motion defaultStyle={{left: 0}} style={{left: spring(10, presets.wobbly)}}>
  {interpolatingStyle => <div style={interpolatingStyle} />}
</Motion>

具体有哪些配置,请参考这里

ok,基本概念已经讲完了,下面开始正式开始用react-motion实现动画

实现动画

react-motion一共提供了三个组件来方便用户进行动画的实现,<Motion />适合编写单个组件的形变动画,<StaggeredMotion />用于编写一串有相互关联关系的实体的动画, <TransitionMotion />则是用来编写组件mount和unmount的动画,下面挨个来讲

<Motion/>

我们用Motion来实现一个简单的平移动画,效果是这样的

motion.gif

代码如下:

/**
 * react-motion 的测试
 * 简单的demo Motion
 */
import React, {Component} from 'react'
import {Motion, spring, presets} from 'react-motion'

import './test.css'

class Test1 extends Component {
  state = {
    left: 0
  }

  clickHandler() {
    let targetX = 0
    if(this.state.left === 0) {
      targetX = 200
    } else {
      targetX = 0
    }

    this.setState({
      left: targetX
    })
  }

  componentDidMount() {
    this.clickHandler()
  }

  render() {

    return (
      <div className="container">
        <Motion style={{x: spring(this.state.left, presets.wobbly)}}>
          {interpolatingStyle => {
            // debugger
            return (
              <div style={{transform: `translateX(${interpolatingStyle.x}px)`}} className='box'></div>
            )
          }}
        </Motion>
        <button onClick={this.clickHandler.bind(this)}>run</button>
      </div>
    )
  }
}

export default Test1

<StaggeredMotion />

用StaggeredMotion 实现一个联动动画,链式反应的效果,如下:

staggeredmotion .gif

代码如下:

/**
 * react-motion 的测试
 * 简单的demo StaggeredMotion
 */
import React, { Component } from 'react'
import { StaggeredMotion, spring, presets } from 'react-motion'

import './test.css'

class Test2 extends Component {
  state = {
    length: 10
  }

  addLength() {
    let newLength
    if (this.state.length) {
      newLength = 0
    } else {
      newLength = 10
    }

    this.setState({
      length: newLength
    })
  }

  render() {
    let boxes = []
    for (let i = 0, len = this.state.length; i < len; i++) {
      boxes.push({
        scale: 0
      })
    }

    return (
      <div>
        <div>
          {this.state.length > 0 ? (
            <StaggeredMotion defaultStyles={boxes}
              styles={prevStyles => prevStyles.map((item, i) => {
                return i === 0
                  ? { scale: spring(1, { ...presets.noWobble }) }
                  : prevStyles[i - 1]
              })}>
              {interpolatingStyles =>
                <div>
                  {interpolatingStyles.map((item, i) => {
                    return (
                      <div className="box2"
                        key={i}
                        style={{
                          transform: `scale(${item.scale}, ${item.scale})`
                        }}></div>
                    )
                  })}
                </div>
              }
            </StaggeredMotion>
          ) : null}
        </div>
        <button onClick={this.addLength.bind(this)}>run</button>
      </div>
    )
  }
}

export default Test2

<TransitionMotion />

实现一个简单的进出场动画,效果如下:

transition-motion.gif

代码如下:

/**
 * react-motion 的测试
 * 简单的demo TransitionMotion
 */
import React, { Component } from 'react'
import { TransitionMotion, spring } from 'react-motion'

import './test.css'

class Test3 extends Component {
  state = {
    show: true
  }

  componentDidMount() {
    this.setState({
      show: false
    })
  }

  clickHandler() {
    this.setState({
      show: !this.state.show
    })
  }

  willEnter(styleThatEnter) {
    return { scale: 0 }
  }

  willLeave(styleThatLeft) {
    return { scale: spring(0) }
  }

  render() {
    return (
      <div>
        <button onClick={this.clickHandler.bind(this)}>run</button>
        <TransitionMotion styles={this.state.show ? [{
          key: 'test',
          style: { scale: spring(1) }
        }] : []}
          willEnter={this.willEnter}
          willLeave={this.willLeave}>
          {inStyles => (
              inStyles[0] ? (
                <div className="box3"
                  key={inStyles[0].key}
                  style={{
                    transform: `scale(${inStyles[0].style.scale},${inStyles[0].style.scale})`
                  }}></div>
              ) : null
          )}
        </TransitionMotion>

      </div>
    )
  }

}

export default Test3
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,617评论 4 59
  • 时常在编辑公众号排版的时候,按照平时统一的排版格式会觉得编辑特别的顺手。但是在某时候,往往已经排版结束了之时,又突...
    独怜幽竹阅读 224评论 0 7
  • 她叫可乐 王可乐 可爱又帅气的小母狗 三胖子和万二二她们去陪你打针 陪你玩的时候我都没有去过 并不是不喜欢你 因为...
    JlYoung阅读 293评论 0 0
  • 主题 数据处理 csv文件 json文件 xml: xpath excel 1. CSV: 逗号分隔值,其文件以纯...
    谢小路阅读 723评论 2 5