react怎麼實現滑動

2022-12-30 14:00:30

react實現滑動的方法:1、在onTouchStart事件找到touches,根據identifier中記錄新的touch出現;2、在onTouchMove事件中根據identifier來記錄每個touch經過的點的座標;3、在onTouchEnd事件中,找到結束的touch事件,然後通過結束的touch事件劃過的點來計算要執行的手勢即可。

本教學操作環境:Windows10系統、react18.0.0版、Dell G3電腦。

react怎麼實現滑動?

react 實現左右滑動效果

React 中滑動手勢的實現

35ad8859e3a5ad94b0e23e5210bd91d5.png

最近做了一點關於react在行動端滑動翻頁的功能。

開始搜尋了一下,發現居然沒找到合適的庫,唯一找到了名字叫react-touch的庫,一看,前端世界四五百star===自己擼,而且似乎也不是想要的功能,算了自己寫點吧。

看了下原理,基本就是配合onTouchStart,onTouchMove和onTouchEnd這三個事件,來記錄滑動過的點,然後來計算手勢。

顯然對於多點觸控,需要找到每個點觸控的路徑,所以有如下幾步:

  • 在onTouchStart事件找到touches,根據identifier中記錄新的touch出現。

  • 在onTouchMove事件中根據identifier來記錄每個touch經過的點的座標。

  • 在onTouchEnd事件中,找到結束的touch事件,然後通過結束的touch事件劃過的點來計算要執行的手勢。

對於我來說我只是想要上下滑動的功能那麼我就只關注單點觸控的情況。

接下來準備上程式碼。哦,不對,首先要想想要怎麼封裝。開始自問自答:

我想用一個單例模式。

是不是使用有點太麻煩了,還要先範例化一下?

那用靜態類?

都js了還要啥靜態類,輸出個字典完事。

那好吧,開始擼吧。

資料部分

const touchData = { touching: false, trace: [] };
// 單點觸控,所以只要當前在觸控中,就可以把劃過的點記錄到trace中了
function* idGenerator() {
  let start = 0;
  while (true) {
    yield start;
    start += 1;
  }
}
//這個生成器用來生成不同事件回撥的id,這樣我們可以註冊不同的回撥,然後在不需要的時候刪掉。
const callbacks = {
  onSlideUpPage: { generator: idGenerator(), callbacks: {} },
  onSlideDownPage: { generator: idGenerator(), callbacks: {} }
};
//儲存向上、下換頁的回撥函數
登入後複製

記錄觸控部分

這裡的事件處理的是react的合成事件,並非原生事件。

function onTouchStart(evt) {
  if (evt.touches.length !== 1) {
    touchData.touching = false;
    touchData.trace = [];
    return;
  }
  touchData.touching = true;
  touchData.trace = [{ x: evt.touches[0].screenX, y: evt.touches[0].screenY }];
}
//在onTouchStart事件,如果是多點觸控直接清空所有資料。如果是單點觸控,記錄第一個點,並設定狀態
function onTouchMove(evt) {
  if (!touchData.touching) return;
  touchData.trace.push({
    x: evt.touches[0].screenX,
    y: evt.touches[0].screenY
  });
}
//如果在單點觸控過程中,持續記錄觸控的位置。
function onTouchEnd() {
  if (!touchData.touching) return;
  let trace = touchData.trace;
  touchData.touching = false;
  touchData.trace = [];
  handleTouch(trace);  //判斷touch型別並呼叫適當回撥
}
//在觸控結束事件,中呼叫handleTouch函數來處理手勢判斷邏輯並執行回撥
登入後複製

handleTouch函數

function handleTouch(trace) {
  let start = trace[0];
  let end = trace[trace.length - 1];
  if (end.y - start.y > 200) {
    Object.keys(callbacks.onSlideUpPage.callbacks).map(key =>
      callbacks.onSlideUpPage.callbacks[key]()
    ); 
    // 向上翻頁
  } else if (start.y - end.y > 200) {
    Object.keys(callbacks.onSlideDownPage.callbacks).map(key =>
      callbacks.onSlideDownPage.callbacks[key]()
    );
    // 向下翻頁
  }
}
登入後複製

在這裡我只判斷了向上向下翻頁兩個事件,如果事件達成,則會呼叫所有註冊到該事件的回撥。如果有多個回撥可按照需求對回撥的執行順序進行調整。這裡應該是無序的。

介面部分

function addSlideUpPage(f) {
  let key = callbacks.onSlideUpPage.generator.next().value;
  callbacks.onSlideUpPage.callbacks[key] = f;
  return key;
}
//註冊向上滑動回撥並返回回撥id
function addSlideDownPage(f) {
  let key = callbacks.onSlideDownPage.generator.next().value;
  callbacks.onSlideDownPage.callbacks[key] = f;
  return key;
}
//註冊向下滑動回撥並返回回撥id
function removeSlideUpPage(key) {
  delete callbacks.onSlideUpPage.callbacks[key];
}
//使用回撥id刪除向上滑動回撥
function removeSlideDownPage(key) {
  delete callbacks.onSlideDownPage.callbacks[key];
}
//使用回撥id刪除向下滑動回撥
export default {
  onTouchEnd,
  onTouchMove,
  onTouchStart,
  addSlideDownPage,
  addSlideUpPage,
  removeSlideDownPage,
  removeSlideUpPage
};
//輸出所有介面函數
登入後複製

這沒啥說的,就是折麼簡單粗暴。接下來,就在react中使用吧!

在next.js中使用

我使用的next.js+create-next-app。在pages目錄下的_app.js檔案中繫結所有touch事件。

//pages/_app.js
import App, { Container } from "next/app";
import React from "react";
import withReduxStore from "../redux/with-redux-store";
import { Provider } from "react-redux";
import touch from "../components/touch";

class MyApp extends App {
  render() {
    const { Component, pageProps, reduxStore } = this.props;
    return (
      <Container>
        <Provider store={reduxStore}>
          <div
            onTouchEnd={touch.onTouchEnd}
            onTouchStart={touch.onTouchStart}
            onTouchMove={touch.onTouchMove}
          >
            <Component {...pageProps} />
          </div> 
{
// 將所有匯出的touch事件繫結在最外層的div上
// 這樣就可以全域性註冊事件了
}
        </Provider>
      </Container>
    );
  }
}

export default withReduxStore(MyApp);
登入後複製

接下來看看如何使用。

import React, {useEffect} from "react";
import touch from "../touch";

const Example = () => {
  useEffect(() => {
    let key = touch.addSlideDownPage(() => {
      console.log("try to slideDownPage!!")
    });
    return () => {
      touch.removeSlideDownPage(key)
      // 用完別忘了刪除事件
    };
  }, []);
  return (
    <div>This is an example!!</div>
  );
};
登入後複製

在原生react中使用

這個專案使用create-react-app生成的

//src/App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import touch from "./components/touch";

function App() {
  return (
    <div className="App"
      onTouchEnd={touch.onTouchEnd}
      onTouchStart={touch.onTouchStart}
      onTouchMove={touch.onTouchMove}
    >
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
登入後複製

結語

如果真的有人仔細看了程式碼,可能會有個問題,這個touch.js裡的內容除了使用了react的合成事件,然後就沒react什麼事了,好像不太常規。

的確是這樣,就沒關react什麼事了。解釋就是這些資料不用通過react的state或者redux的state太傳遞,一來是在效能上,一更新redux或者react的state就會觸發react的重新渲染,沒有必要,二就是希望可以全域性使用這些介面,所以就並沒有藉助react的機制。其實這就像是react所說的uncontrolled components。

最後附上完整的touch.js

//touch.js
const touchData = { touching: false, trace: [] };

function* idGenerator() {
  let start = 0;
  while (true) {
    yield start;
    start += 1;
  }
}

const callbacks = {
  onSlideUpPage: { generator: idGenerator(), callbacks: {} },
  onSlideDownPage: { generator: idGenerator(), callbacks: {} }
};

function onTouchStart(evt) {
  if (evt.touches.length !== 1) {
    touchData.touching = false;
    touchData.trace = [];
    return;
  }
  touchData.touching = true;
  touchData.trace = [{ x: evt.touches[0].screenX, y: evt.touches[0].screenY }];
}
function onTouchMove(evt) {
  if (!touchData.touching) return;
  touchData.trace.push({
    x: evt.touches[0].screenX,
    y: evt.touches[0].screenY
  });
}
function onTouchEnd() {
  if (!touchData.touching) return;
  let trace = touchData.trace;
  touchData.touching = false;
  touchData.trace = [];
  handleTouch(trace);
}
function handleTouch(trace) {
  let start = trace[0];
  let end = trace[trace.length - 1];
  if (end.y - start.y > 200) {
    Object.keys(callbacks.onSlideUpPage.callbacks).map(key =>
      callbacks.onSlideUpPage.callbacks[key]()
    );
  } else if (start.y - end.y > 200) {
    Object.keys(callbacks.onSlideDownPage.callbacks).map(key =>
      callbacks.onSlideDownPage.callbacks[key]()
    );
  }
}
function addSlideUpPage(f) {
  let key = callbacks.onSlideUpPage.generator.next().value;
  callbacks.onSlideUpPage.callbacks[key] = f;
  return key;
}
function addSlideDownPage(f) {
  let key = callbacks.onSlideDownPage.generator.next().value;
  callbacks.onSlideDownPage.callbacks[key] = f;
  return key;
}
function removeSlideUpPage(key) {
  delete callbacks.onSlideUpPage.callbacks[key];
}
function removeSlideDownPage(key) {
  delete callbacks.onSlideDownPage.callbacks[key];
}
export default {
  onTouchEnd,
  onTouchMove,
  onTouchStart,
  addSlideDownPage,
  addSlideUpPage,
  removeSlideDownPage,
  removeSlideUpPage
};
登入後複製

推薦學習:《》

以上就是react怎麼實現滑動的詳細內容,更多請關注TW511.COM其它相關文章!