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, 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
------- 我是小尾巴