前言:
前段时间接到产品一个需求,优化游戏中游戏好友cell滑动效果。
问题描述
:点击好友cell左 右滑动时,也会伴随上下移动
产品需求
:优化滑动方式,使其左右滑动时上下不移动,增强玩家游戏体验
分析原因
:好友cell滑动功能是用creator中ScrollView实现的(Cocos官方
ScrollView 组件参考
),cell是单独的节点事件监听(
Cocos官方节点系统事件参考
),但是没有屏蔽触摸事件向上冒泡,所以ScrollView监听到触摸后也会随之滚动。
根据原因想到的方案
:当触摸子节点cell时,先关闭ScrollView的滚动功能,通过触摸点坐标变化的绝对差值判断移动方向,然后决定ScrollView是否需要滚动。
实现逻辑
:1、在父节点ScrollView脚本中注册节点触摸事件监听函数
onScrollTouchStart
和
onScrollTouchMove
,在子节点cell中注册事件监听函数
onTouchStart
、
onTouchMove
和
onTouchEnd
。
this.scrollView.node.on(cc.Node.EventType.TOUCH_START, this.onScrollTouchStart, this);
this.scrollView.node.on(cc.Node.EventType.TOUCH_MOVE, this.onScrollTouchMove, this);
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
2、当触摸子节点cell时,事件冒泡到父节点ScrollView的
onScrollTouchStart
函数中,暂时关闭ScrollView的滚动,即将scrollview的
vertical
(默认上下滚动)设置为
false
,
onScrollTouchMove
函数中再将
vertical
设置为
true
。子节点cell的
onTouchMove
函数中通过
event.stopPropagation()
关闭move事件冒泡,判断完子节点cell的滚动方向后再通过派发事件通知父节点打开ScrollView的滚动功能,即当触摸是上下滑动时需scrollview也跟随滚动。
onScrollTouchStart: function (event) {
this.scrollView.vertical = false;
},
onScrollTouchMove: function (event) {
if (!this.scrollView.vertical) {
this.scrollView.vertical = true;
}
},
注:
在onScrollTouchMove中打开vertical 是以防玩家没有点击cell而是直接滑动scrollview时没有响应。
3、在父节点ScrollView脚本中注册通知事件监听函数,这里有两种方式可以注册,一种是自己项目中封装的方法,另一种是creator提供的节点dispatchEvent方式(
Cocos官方发射和监听事件参考
),我用的是项目中封装的全局方法。
ty.Notify.listen("cell_scroll_direction",this.confirmScrollDirection,this);
confirmScrollDirection: function (params) {
if (params && !this.scrollView.vertical){
this.scrollView.vertical = params;
}
}
实现代码
:主要的代码逻辑在子节点cell脚本三个触摸监听事件函数中,实现的过程花费了一些时间,经历几次迭代优化,才达到较为满意的结果。以下是几种方案的代码,代码下面是方案的缺陷。
方案一:
onTouchStart(event){
this.touchStartX = event.getLocationX();
this.touchStartY = event.getLocationY();
},
onTouchMove(event){
event.stopPropagation();
let isScroll = false;
let touchMoveX = event.getLocationX();
let touchMoveY = event.getLocationY();
let relative_distance_y = Math.abs(this.touchStartY - touchMoveY);
let relative_distance_x = this.touchStartX - touchMoveX;
//用于移动距离判断
let distance_y = 15,
distance_x = 10;
if(relative_distance_x >= distance_x && relative_distance_y <= distance_y){
//左滑TODO
}else if(relative_distance_x <= -distance_x && relative_distance_y <= distance_y){
//右滑TODO
}
//当上下移动距离大于固定值时,默认是想上下滑动
if (relative_distance_y > distance_y ) {
isScroll = true;
}
if (isScroll) {
ty.Notify.push("cell_scroll_direction",isScroll);
}
},
onTouchEnd: function (event) {
},
缺点
:在判断的过程中游戏界面会有延迟卡顿现象,效果上也并没有实现产品的要求,是一次很失败的尝试😓。
方案二:
onTouchStart(event){
this.isRegisterSchedule = false
this.touchStartX = event.getLocationX();
this.touchStartY = event.getLocationY();
this.touchMoveX = this.touchStartX;
this.touchMoveY = this.touchStartY;
this.touchEndX = null;
this.touchEndY = null;
},
onTouchMove(event){
event.stopPropagation();
let isScroll = false;
this.touchMoveX = event.getLocationX();
this.touchMoveY = event.getLocationY();
if (!this.isRegisterSchedule) {
this.scheduleOnce(function (param) {
//考虑用户在延迟时间内可能完成了一次触摸,所以加了touchEnd的判断
let disY = (this.touchEndY ? this.touchEndY : this.touchMoveY);
let disX = (this.touchEndX ? this.touchEndX : this.touchMoveX);
let relative_distance_y = Math.abs(this.touchStartY - disY);
let relative_distance_x = this.touchStartX - disX;
let distance_y = 15;
let distance_x = 10;
if(relative_distance_x >= distance_x && relative_distance_y <= distance_y ){
//左滑TODO
}else if(relative_distance_x <= -distance_x && relative_distance_y <= distance_y){
//右滑TODO
}
//当上下移动距离大于固定值时,默认是想上下滑动
if (relative_distance_y > distance_y ) {
isScroll = true;
}
if (isScroll) {
ty.Notify.push("cell_scroll_direction",isScroll);
}
},0.1);
this.isRegisterSchedule = true
}
},
onTouchEnd: function (event) {
this.isRegisterSchedule = false
this.touchEndX = event.getLocationX();
this.touchEndY = event.getLocationY();
},
这个方案是在方案一的基础上改版而来,加了计时器功能,而不是移动过程中实时判断
缺点
:方案二虽然视觉上降低了卡顿的现象,但还是会有,而且一旦用户在触摸点停留时间过长,判断会失去作用,没有真正解决问题
方案三 :
//1.0版通过坐标角度 判断是否左右滑动
onTouchStart(event){
this.isJudge = false//是否已经判断过
this.touchStartX = event.getLocationX();
this.touchStartY = event.getLocationY();
},
onTouchMove(event){
event.stopPropagation();
let isScroll = false;
let touchMoveX = event.getLocationX();
let touchMoveY = event.getLocationY();
var diff_x = touchMoveX - this.touchStartX,
diff_y = touchMoveY - this.touchStartY;
let angle = 360*Math.atan2(diff_y,diff_x)/(2*Math.PI);
if (!this.isJudge) {
let relative_distance_x = this.touchStartX - touchMoveX;
if ((angle >= 0 && angle <= 45) || (angle < 0 && angle >= -45) || (angle >= 135 && angle <= 180) || angle > -180 && angle <= -135) { //45度
if(relative_distance_x > 0 ){
//左滑TODO
}else if(relative_distance_x < 0 ){
//右滑TODO
}
}else {
isScroll = true;
}
if (isScroll) {
ty.Notify.push("cell_scroll_direction",isScroll);
}
this.isJudge = true;
}
},
onTouchEnd: function (event) {
this.isJudge = false
},
缺点:
可以实现产品要求,但是效果有时会不好,后来测试发现onTouchMove中最开始的触摸移动坐标和onTouchStart中相同,导致判断的角度可能是0,relative_distance_x也可能是0。
//1.5版通过坐标角度 判断是否左右滑动
onTouchStart(event){
this.isRegisterSchedule = false
this.touchStartX = event.getLocationX();
this.touchStartY = event.getLocationY();
},
onTouchMove(event){
event.stopPropagation();
let isScroll = false;
let touchMoveX = event.getLocationX();
let touchMoveY = event.getLocationY();
let relative_distance_x = this.touchStartX - touchMoveX;
if (!this.isRegisterSchedule) {
this.scheduleOnce(function (params) {
let diff_x = touchMoveX - this.touchStartX,
diff_y = touchMoveY - this.touchStartY;
let angle = 360*Math.atan2(diff_y,diff_x)/(2*Math.PI);
if ((angle >= 0 && angle <= 45) || (angle < 0 && angle >= -45) || (angle >= 135 && angle <= 180) || angle > -180 && angle <= -135) { //45度
if(relative_distance_x > 0 ){
//左滑TODO
}else if(relative_distance_x < 0 ){
//右滑TODO
}
}else {
isScroll = true;
}
if (isScroll) {
ty.Notify.push("cell_scroll_direction",isScroll);
}
},0.08);
this.isRegisterSchedule = true;
}
},
onTouchEnd: function (event) {
this.isRegisterSchedule = false
},
缺点:
在1.0版本基础上改良,delayTime如何设置还是会出现方案二中延迟现象,与产品要求不符。
//2.0版通过坐标角度 判断是否左右滑动
onTouchStart(event){
this.isJudge = false
this.touchStartX = event.getLocationX();
this.touchStartY = event.getLocationY();
},
onTouchMove(event){
event.stopPropagation();
let isScroll = false;
let touchMoveX = event.getLocationX();
let touchMoveY = event.getLocationY();
let relative_distance_x = this.touchStartX - touchMoveX;
let relative_distance_y = this.touchStartY - touchMoveY
if (relative_distance_x != 0 || relative_distance_y != 0) {
if (!this.isJudge) {
let diff_x = touchMoveX - this.touchStartX,
diff_y = touchMoveY - this.touchStartY;
let angle = 360*Math.atan2(diff_y,diff_x)/(2*Math.PI);
//右滑 第一象限和第四象限
if ((angle >= 0 && angle <= 45) || (angle < 0 && angle >= -45) ){
//TODO
}
//左滑 第二象限和第三象限
else if ( (angle >= 135 && angle <= 180) || angle >= -180 && angle <= -135) {
//TODO
}
else {
isScroll = true;
}
if (isScroll) {
ty.Notify.push("cell_scroll_direction",isScroll);
}
this.isJudge = true;
}
}
},
onTouchEnd: function (event) {
this.isJudge = false
},
总结:
方案三2.0版本是最终代码版本,功能虽然已实现,但个人觉得实现的方法不是很好,还可以进一步优化。