前端框架文章React

用react-motion实现react动画

2017-08-25  本文已影响774人  我只是个NPC

怎么写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

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
上一篇下一篇

猜你喜欢

热点阅读