用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閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,597評論 25 707
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,148評論 4 61
  • 時常在編輯公眾號排版的時候,按照平時統一的排版格式會覺得編輯特別的順手。但是在某時候,往往已經排版結束了之時,又突...
    獨憐幽竹閱讀 265評論 0 7
  • 她叫可樂 王可樂 可愛又帥氣的小母狗 三胖子和萬二二她們去陪你打針 陪你玩的時候我都沒有去過 并不是不喜歡你 因為...
    JlYoung閱讀 321評論 0 0
  • 主題 數據處理 csv文件 json文件 xml: xpath excel 1. CSV: 逗號分隔值,其文件以純...
    謝小路閱讀 761評論 2 5