RN 简单的触摸监听已经无法满足这个需求,所以 引入 手势监听(handler)
上代码:
handler定义:
let _this = this; let handlers = { onStartShouldSetResponder: () => true,onMoveShouldSetResponder: () => true,onResponderTerminationRequest: () => false,onResponderStart: () => { console.log('start') console.log(_this.props._isScrollIng) clearTimeout(_this.longTimer); _this.longTimer = setTimeout(() => { if (!_this.props._isScrollIng) { this.setState({ clickStatus: true,}) _this.props._onLongPress() _this.props._getLocationNow(_this.props.id,0) } },300);// 300 ms 之后触发长按 },onResponderMove: (evt) => { console.log('move') console.log(_this.props._isScrollIng) console.log(evt.nativeEvent.locationX) console.log(evt.nativeEvent.locationY) console.log(_this.props.id) if (!_this.props._isScrollIng && this.state.clickStatus) { // 条件:屏幕不滚动,触发长按 _this.props._getLocationNow(_this.props.id,evt.nativeEvent.locationX,evt.nativeEvent.locationY)//根据当前触发的handle对象,以及点击坐标与其相对位置,判断当前所处view } },onResponderRelease: () => { console.log(_this.props._isScrollIng) console.log('release') clearTimeout(_this.longTimer); this.setState({ clickStatus: false,}) _this.props._toggleIsActive();//让scrollView重新可以滚动,并且把屏幕滚动状态设置为false(针对短点表情触发的屏幕滚动结束) _this.props._onRefreshPreView();//让最后一个预览表情,离开预览状态 },};
handler 使用:
<View {...handlers} style={[{ width: iconWidth,height: iconWidth,},styles.iconWrap]}> </View>
表情Sticker组件的使用(自定义的,都有注释):
if (this.state.stickerArr.pagInfo) { this.state.iconObj.forEach((v,k) => { Icons.push(( <Sticker key={k} id = {k} //每个表情有自己的id标记 index_touch = {this.state.nowTouchIndex} //当前手势所处view的id animateIconUrl = {v} iconUrl = {this.state.gifObj[k]} _toggleIsActive={this._toggleIsActive.bind(this) } //手势释放,触发的方法定义在上层界面 _onLongPress = {this._onLongPress.bind(this) } //触发长按的方法,定义在上层界面 _getLocationNow = {this._onGetLocationNow.bind(this) } //获取当前手势坐标方法,定义在上层界面 _onRefreshPreView = {this._onRefreshPreview.bind(this) } //重置最后一个预览状态的表情方法,,定义在上层界面 _isScrollIng = {this.state.scrollIng} //当前界面是否在滚动状态 /> )); }) }
通过触发handler的id,及手势移动与其的相对偏移量,算出当前手势所在view:
上位置判断算法:
_onGetLocationNow(id,offsetX,offsetY) { let screenWidth = Dimensions.get('window').width; let iconWidth = (screenWidth - 5 * TAP) / 4; let row = 0; let col = 0; let index = undefined; let offsetXAbs = Math.abs(offsetX); let offsetYAbs = Math.abs(offsetY); if (offsetX <= iconWidth + TAP / 2 && offsetX >= 0 && offsetY <= iconWidth && offsetY >= 0) { index = id; } else { col = Math.ceil((offsetXAbs) / (iconWidth + TAP / 2)) - 1; row = Math.ceil((offsetYAbs) / (iconWidth)) - 1; console.log('row_1:' + row); if (offsetX < 0) { col = 0 - (col + 1); } if (offsetY < 0) { row = 0 - (row + 1); } } console.log('col_1:' + col); console.log('row_2:' + row); let rangetimes = Math.ceil((id + 1) / 4); // 超出范围判定 if (col >= 0 && (col + id) >= rangetimes * 4 - 1) { col = rangetimes * 4 - 1 - id; } else { if (col < 0 && (id + col) <= (rangetimes - 1) * 4) { col = 0 - (id - (rangetimes - 1) * 4); } } console.log('col_2:' + col); console.log('row_3:' + row); index = id + row * 4 + col; if (index < 0 || index >= this.state.iconObj.length) { index = undefined; } console.log('index:' + index); console.log('col:' + col); this.setState({ nowTouchIndex: index,}) }
触发长按要屏蔽上层scrollView的滚动以及滚动要屏蔽view的触发长按:
这里代码杂:说个里面有的一个坑,就是scrollView 滚动结束 并不会调用onAnimatedScrollEnd(文档上说调用),想监听这个事件用:onMomentumScrollEnd
差不多了,全套代码肯定是没有的,毕竟是项目,大概理解下,想深入交流就加一下群:429307812
------- 我是小尾巴
原文链接:https://www.f2er.com/react/305600.html