RN手勢

React Native框架底層的手勢響應系統(tǒng)提供了響應處理器,PanResponder API將這些手勢響應處理器再次進行封裝,便于開發(fā)者對手勢進行處理。
PanResponser API的基本思想就是:監(jiān)視屏幕上指定的位置的矩形區(qū)域。對手指觸發(fā)的事件作出響應。
一、利用PanResponser API監(jiān)視的步驟
1、指定監(jiān)視區(qū)域
為了監(jiān)視一個區(qū)域,我們需要準備一個view或者是從view組件擴展而來的組件。(注意:如果要監(jiān)視兩個區(qū)域,一定不能讓他們重疊,不然監(jiān)視器無法工作)
2、定義監(jiān)視器的相關變量
指向監(jiān)視器的變量(必須)。
用來指向監(jiān)視器監(jiān)視區(qū)域的變量,可以不定義。
用來記錄監(jiān)視區(qū)域左上角頂點坐標的兩個數(shù)值變量??梢圆欢x。但當觸摸發(fā)生需要給用戶視覺上的反饋時,有這個變量可以很容易實現(xiàn)反饋。
上一次觸摸點的橫、縱坐標變量??梢圆欢x,但這兩個變量可以便于分析、處理觸摸事件。
3、準備監(jiān)視器的事件處理函數(shù)
一共有13個這樣的函數(shù),比如說onMoveShouldSetPanResponder用來判斷是否要監(jiān)視這個區(qū)域,onMoveShouldSetPanResponderCapture等。那我們只需要挑選出自己需要的函數(shù)來開發(fā)就可以了。
以下是監(jiān)視器的13個事件處理函數(shù)

 onMoveShouldSetPanResponder
 onMoveShouldSetPanResponderCapture
 onStartShouldSetPanResponder
 onStartShouldSetPanResponderCapture
 onPanResponderReject
 onPanResponderGrant
 onPanResponderStart
 onPanResponderEnd
 onPanResponderRelease
 onPanResponderMove
 onPanResponderTerminate
 onPanResponderTerminateRequest
 onShouldBlockNativeResponder

4、建立監(jiān)視器
用PanResponder API提供的靜態(tài)函數(shù)create,建立監(jiān)聽器

this.watcher = PanResponder.create({
  ……
})

5、將監(jiān)視器和監(jiān)視區(qū)域掛接
我們先假設一下,監(jiān)視器就叫watcher。

{...this.watcher.panHandlers}

二、監(jiān)視事件的生命周期
一般來說,在點擊的生命周期我們自定義的被回調(diào)的函數(shù)都會收到兩個參數(shù),一個是原生事件,另一個是手勢狀態(tài)。
而這里面會有很多的成員變量比如說觸摸點的位置,比如說手勢狀態(tài)的ID.
手勢狀態(tài)有以下變量

stateID—觸摸狀態(tài)的ID,在屏幕上至少有一個點的情況下,這個id會一直存在。
moveX—最近一次移動時的屏幕橫坐標
moveY—最近一次移動時的屏幕縱坐標
x0—當響應器產(chǎn)生時的屏幕坐標
y0—當響應器產(chǎn)生時的屏幕坐標
dx—從觸摸開始累積的橫向路程
dy—從觸摸操作開始累積縱向路程
vx—當前的橫向移動速度
vy—當前的縱向移動速度
numberActiveTouches—當前在屏幕上的有效觸摸點的數(shù)量。

三、單次點擊事件的生命周期
onStartShouldSetPanResponderCapture:是否設置開始捕捉這次事件
onStartResponderStart:將這個事件視為點擊事件的開始點
onPanResponderEnd:將這個事件視為點擊事件的結束點。
這里列舉出的三個生命周期方法是最常見的,但是其實它還有其他很多的方法。不過我們平常用的單次點擊事件就是這三個。
在移動手勢中,也有它自己的生命周期方法。這里不做詳解。通過下面一個小的案例進行解說。

四、案例
滑動解鎖:手指按壓的滑塊跟隨手指移動,按壓的監(jiān)視區(qū)域隨著手指移動而變化

75353037-EE9F-4BA8-8283-8B2F9528F7BF.png

從圖中我們可以看到,在這個RN界面中需要返回一個頂級元素view,然后在里面添加一個滑塊槽,之后是按鈕。
這個按鈕會有一個樣式,我們可以將它切成一個圓的樣子。并且,這個按鈕是需要滑動的,所以要給它添加一個表示距離滑動槽原點的位置。而這個樣式是需要及時改變的,所以我們可以定義一個狀態(tài)機。用leftPoint來表示它的位置。

export default class GusDemo extends Component {
  constructor(props){
    super(props);
    this.watcher = null;   //監(jiān)視器
    this.startX = 0; //開始的左邊
    this.state = {leftPoint:1}  //狀態(tài)機變量用來保存最左邊的卡槽
}

返回的UI界面

render() {
    return (
      <View style={styles.container}>
         <View style = {styles.barViewStyle}>
            <View style = {[styles.buttonViewStyle,{left:this.state.leftPoint}]}
                  {...this.watcher.panHandlers}  //將監(jiān)視器與監(jiān)視區(qū)域掛接
            />
        </View>
      </View>
    );
  }

設置樣式
首先要獲取寬度。

var Dimensions = require('Dimensions');
var totalWidth = Dimensions.get('window').width;  //寬度

設置樣式

const styles = StyleSheet.create({
  container: {
    flex: 1,

    backgroundColor: '#F5FCFF',
  },
  barViewStyle: {
      width:totalWidth - 40,
      height:50,
    backgroundColor:'grey',
    borderRadius:25,
    left:20,
    top:50,
    flexDirection:'row',
  },
  buttonViewStyle: {
      width:48,
      height:48,
     borderRadius:24,
    backgroundColor:'pink',
    left:1,
    top:1,
  }
});

自此,所有的UI部分已經(jīng)構建完畢,現(xiàn)在要做的就是到componentWillMount()方法里面去建立監(jiān)視器。為啥要在這個方法里面呢,是因為這個方法在UI渲染之前運行的,我們可以讓它來做一些定義變量或賦值的操作。所以我們將事件的按下、移動和結束的方法都寫到這邊來。分別給這幾個屬性各自定義一個方法。

componentWillMount(){
    this.watcher = PanResponder.create({  //建立監(jiān)視器
      onStartShouldSetPanResponder:()=>true,  //判斷是否要監(jiān)聽,這里直接返回true
      onPanResponderGrant:this._onPanResponderGrant,  //事件,按下
      onPanResponderMove:this._onPanResponderMove,  //移動
      onPanResponderEnd:this._onPanResponderEnd,   //結束

    });
  }

這些方法我們都用簡寫的方式,所以到構造函數(shù)中將它們綁定一下。

export default class GusDemo extends Component {
  constructor(props){
    super(props);
    this.watcher = null;   //監(jiān)視器
    this.startX = 0; //開始的左邊
    this.state = {leftPoint:1}  //狀態(tài)機變量用來保存最左邊的卡槽
    this._onPanResponderGrant = this._onPanResponderGrant.bind(this);
    this._onPanResponderEnd = this._onPanResponderEnd.bind(this);
    this._onPanResponderMove = this._onPanResponderMove.bind(this);
  }

現(xiàn)在來具體實現(xiàn)自定義的方法。雖然我們看到的是簡寫的方法,但是實際上,系統(tǒng)按下的方法會給我們自定義的這個方法傳入兩個參數(shù),一個是事件,而另外一個是手指觸摸的位置。在開始的時候,我們要將開始偏移的位置給記錄下來。因為每次開始滑動的時候位置其實都是不一樣的。

 _onPanResponderGrant(e,gestureState){
    this.startX = gestureState.x0;   //按住滑塊的時候,記錄偏移量
  }

下面來寫移動按鈕的時候的邏輯

_onPanResponderMove(e,gestureState){
    let leftPoint;   //用一個變量記錄滑動的偏移值
    if(gestureState.moveX > totalWidth-42-48+this.startX){  //正常位置
      leftPoint = totalWidth - 42 - 48;
    }else{
      leftPoint = gestureState.moveX - this.startX; //在后面可以寫解鎖或者是跳轉(zhuǎn)效果。
    }
    this.setState(()=>{
      return {leftPoint};    //改變狀態(tài)機
    })
  }

當手指松開之后,我們在這里不做復雜的判斷,直接讓它移動到最原始的位置。

 _onPanResponderEnd(e,gestureState){
      let leftPoint = 1;
    this.setState(()=>{
      return {leftPoint};   //改變狀態(tài)機到1的位置
    })
  }

滑動解鎖的案例就完成了。

下面是源碼index.ios.js

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  PanResponder
} from 'react-native';
var Dimensions = require('Dimensions');
var totalWidth = Dimensions.get('window').width;  //寬度
export default class GusDemo extends Component {
  constructor(props){
    super(props);
    this.watcher = null;   //監(jiān)視器
    this.startX = 0; //開始的左邊
    this.state = {leftPoint:1}  //狀態(tài)機變量用來保存最左邊的卡槽

    this._onPanResponderGrant = this._onPanResponderGrant.bind(this);
    this._onPanResponderEnd = this._onPanResponderEnd.bind(this);
    this._onPanResponderMove = this._onPanResponderMove.bind(this);


  }

  componentWillMount(){
    this.watcher = PanResponder.create({  //建立監(jiān)視器
      onStartShouldSetPanResponder:()=>true,
      onPanResponderGrant:this._onPanResponderGrant,  //事件,按下
      onPanResponderMove:this._onPanResponderMove,  //移動
      onPanResponderEnd:this._onPanResponderEnd,   //結束

    });
  }

  //移動的邏輯
  _onPanResponderGrant(e,gestureState){
    this.startX = gestureState.x0;   //按住滑塊的時候,記錄偏移量
  }
  _onPanResponderMove(e,gestureState){
    let leftPoint;   //用一個變量記錄滑動的偏移值
    if(gestureState.moveX > totalWidth-42-48+this.startX){  //正常位置
      leftPoint = totalWidth - 42 - 48;
    }else{
      leftPoint = gestureState.moveX - this.startX; //在后面可以寫解鎖或者是跳轉(zhuǎn)效果。
    }
    this.setState(()=>{
      return {leftPoint};    //改變狀態(tài)機
    })
  }
  _onPanResponderEnd(e,gestureState){
      let leftPoint = 1;
    this.setState(()=>{
      return {leftPoint};   //改變狀態(tài)機到1的位置
    })
  }
  render() {
    return (
      <View style={styles.container}>
         <View style = {styles.barViewStyle}>
            <View style = {[styles.buttonViewStyle,{left:this.state.leftPoint}]}
                  {...this.watcher.panHandlers}
            />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,

    backgroundColor: '#F5FCFF',
  },
  barViewStyle: {
      width:totalWidth - 40,
      height:50,
    backgroundColor:'grey',
    borderRadius:25,
    left:20,
    top:50,
    flexDirection:'row',
  },
  buttonViewStyle: {
      width:48,
      height:48,
     borderRadius:24,
    backgroundColor:'pink',
    left:1,
    top:1,
  }
});

AppRegistry.registerComponent('GusDemo', () => GusDemo);

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,677評論 25 708
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,155評論 4 61
  • 頸椎病誤區(qū):"米字操" 在電腦前坐久了,不少人有喜歡“搖頭晃腦”的習慣,有的稱之為“米字操”,目的是為了防頸椎病。...
    文五閱讀 471評論 0 1
  • 我深深懂得:給別人留下尊重和尊嚴,給別人留下空間和余地,才會給自己留下最美好的東西,還有值得懷念的友誼、親情和愛情!
    簡俊智閱讀 146評論 0 0
  • 打開籠子 一只白鴿飛遠了 從此 我 一個人 成了深深的孤獨患者
    伍月的晴空閱讀 199評論 7 4