react实现滑动的方法:1、在onTouchStart事件找到touches,根据identifier中记录新的touch出现;2、在onTouchMove事件中根据identifier来记录每个touch经过的点的坐标;3、在onTouchEnd事件中,找到结束的touch事件,然后通过结束的touch事件划过的点来计算要执行的手势即可。
本教程操作环境:Windows10系统、react18.0.0版、Dell G3电脑。
react怎么实现滑动?
react 实现左右滑动效果
React 中滑动手势的实现
最近做了一点关于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视频教程》