creator中scrollview实现左右滑动cell时,上下不滚动

  • Post author:
  • Post category:其他




前言:

前段时间接到产品一个需求,优化游戏中游戏好友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版本是最终代码版本,功能虽然已实现,但个人觉得实现的方法不是很好,还可以进一步优化。

参考文章:

JS如何使用Math.atan2获取两点之间角度的实践案例



版权声明:本文为Dream_GentleMan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。