首页

在卡片上左右拖动,查看效果

代码解释:

package;
 
class MainApplication extends TouchSprite {
 
    private static inline var SCREEN_WIDTH:Int=1920; //定义屏幕宽度
    private static inline var SPEED_FACTOR:Int=100; //值越小,手指离开后卡片飞行的距离越远
    private static inline var OFFSET_FACTOR:Int=300; //值越小,手指拖动时卡片跟手移动的距离越远
 
    /*
    一个指示器,用id的形式抽象表达了目前哪个卡片的位置处在屏幕中间
    比如__current=0代表了第一个卡出于屏幕中间,__current=1代表了第一个卡出于屏幕中间,
    __current=0.5代表了第一个卡和第二个卡的间隙出于屏幕中间,
    __current从最小值__min也就是0,到最大值__max的过程就是卡片从一个飞行到最后一个的过程
    */
    private var __current:Float;
 
    private var __onTouch:Float; //手指按下后,临时记录一下__current
    private var __min:Float; //__current最小值
    private var __max:Float; //__current最大值
    private var __cardsArr:Array<Card>; //保存卡片引用的数组
    private var __depthsArr:Array<Card>; //卡片深度的数组
 
    private var __offsetX:Float=0; //存储每次手指位移量,单位是px
    private var __speedX:Float=0; //存储每次手指位移速度,单位是px/帧
    private var __dist:Float=0; //手指放开后,__current需要变化到的值
    private var __isTouch:Bool=false; //手指当前是否按在屏幕上
 
    public function new () {
 
        super ();
 
        initUI();
        initTouch();
        initTicker();
    }
 
    private function initUI():Void
    {
        __max=12; //随意加入13张卡片,即最大的卡片id为12
        __min=0;
        __cardsArr=new Array<Card>();
        __depthsArr=new Array<Card>();
 
		var i:Int=0;
        while(i<(__max+1))
        {
            /*
            在demo中,Card对象就是一个简单的显示对象,
            内部逻辑就是加载一张贴图,并将自身的缩放点调整到自身的中心,而非左上角
            */
            var card:Card=new Card(i%8);
 
            __cardsArr.push(card); //将卡片放入引用数组
            __depthsArr.push(card); //将卡片放入深度数组
            this.addChild(card); //将卡片放入场景显示出来
 
            i++;
        }
 
        __current=__dist=0;
        updateCards();
        sortDepth();
    }
 
    private function initTicker():Void
    {
        this.addEventListener(Event.ENTER_FRAME, onTicker); //注册一个每帧都会执行的方法onTicker
    }
 
    /*
    每一帧都会执行这个方法,当手放开时,让__current渐渐过渡到__dist,
    即让卡片飞行一段距离再停下,让人有拖拽惯性的感觉
    */
    private function onTicker(e:Event):Void
    {
        if(!__isTouch)
        {
            __current+=(__dist-__current)*.2;
 
            updateCards();
            sortDepth();
        }
    }
 
    /*
    注册触摸事件侦听器
    FingerEvent.FINGER_BEGIN 是手指刚开始按在屏幕的事件,会一次性触发beginHandler
    FingerEvent.FINGER_END 是手指离开屏幕的事件,会一次性触发endHandler
    FingerGestureEvent.GESTURE_PAN 是手指在屏幕上划动的事件,在划动整个过程中,每一帧都会触发panHandler
 
    如果是多点触摸屏幕,FingerEvent.FINGER_BEGIN 只在第一个手指接触屏幕时触发
    FingerEvent.FINGER_END 只在所有手指全部离开屏幕时触发
    FingerGestureEvent.GESTURE_PAN 在第一个手指划动时触发
 
    这里是对开发平台依赖度比较强的地方,每个平台的触摸事件定义均有所不同,只要能达到相同的目的即可
    */
    private function initTouch():Void
    {
        this.addEventListener(FingerEvent.FINGER_BEGIN, beginHandler);
        this.addEventListener(FingerGestureEvent.GESTURE_PAN, panHandler);
        this.addEventListener(FingerEvent.FINGER_END, endHandler);
    }
    private function beginHandler(e:FingerEvent):Void
    {
        __onTouch=__current;
 
        __isTouch=true;
    }
    private function endHandler(e:FingerEvent):Void
    {
        /*
        手指离开屏幕后,结束触摸,根据最后一次记录的速度计算卡片需要飞行的距离__dist
        */
        __dist=__current-__speedX/SPEED_FACTOR;
 
        /*
        将__dist限制为__min到__max之间的整数
        */
        if(__dist<__min)
        {
            __dist=__min;
        }
        else if(__dist>__max)
        {
            __dist=__max;
        }
        else
        {
            __dist=Math.round(__dist);
        }
 
        __isTouch=false;
    }
    private function panHandler(e:FingerGestureEvent):Void
    {
        /*
        在手指划动过程中,记录此次划动手指位移,位移的距离=当前帧手指x-刚一接触屏幕时的手指x
        */
        __offsetX=e.offsetX;
 
        /*
        在手指划动过程中,时刻记录划动手指位移速度,速度=当前帧手指x-前一帧手指x
        */
        __speedX=e.speedX;
 
        /*
        手指划动时,计算卡片需要位移的距离,更新当前的__current
        */
        var dist:Float=__onTouch-__offsetX/OFFSET_FACTOR;
        if(dist<__min)
        {
            __current=__min-Math.sqrt(__min-dist)*.5; //当划动到最左端,给一个弹性的感觉
        }
        else if(dist>__max)
        {
            __current=__max+Math.sqrt(dist-__max)*.5; //当划动到最右端,给一个弹性的感觉
        }
        else
        {
            __current=dist;
        }
 
        updateCards();
        sortDepth();
    }
 
    /*
    计算所有卡片x坐标,缩放,透明度的方法
    其中所有卡片都是以卡片中心进行缩放,而非左上角
    */
    private function updateCards():Void
    {
        var i:Int=0;
        var l:Int=__cardsArr.length;
        while(i<l)
        {
            var card:Card=__cardsArr[i];
            var idist:Float=i-__current;
 
            var distX:Float=idist*300+1920/2; //根据当前卡片的固定id,以及__current计算卡片x坐标
            var scaleRatio:Float=Math.abs(idist)/2; //计算卡片缩放值
            scaleRatio=1-NumberUtil.clamp(scaleRatio, 0, 1); //将计算出的卡片缩放值限制在0到1之间
            scaleRatio=.4+.6*scaleRatio;  //将卡片缩放值限制在0.4到1之间
 
            /*
            NumberUtil.clamp(value, min, max)方法是一个简单的数学方法,需要自行实现,
            将value限制在min与max之间,如果小于min,输出min,如果大于max,输出max,如果在min,max之间,输出value
            */
 
            var alphaRatio:Float=Math.abs(idist)-3;  //计算卡片透明度,只有屏幕最中间的5个alpha是1,其余变化到0
            alphaRatio=alphaRatio>0?0:NumberUtil.clamp(Math.abs(alphaRatio), 0, 1);
 
            card.x=distX;
            card.y=540;
            /*
            给卡片缩放赋予一个差值,让靠近屏幕中心的缩放更平滑
            */
            card.scaleX=card.scaleY=circular(scaleRatio, 0, 1, 1);
            card.alpha=alphaRatio;
 
            i++;
        }
    }
 
    /*
    计算所有卡片前后遮挡关系的方法,原理是遍历__depthsArr,比较卡片之间的缩放值
    缩放值越大,在场景中的深度值越大,越靠前
    */
    private function sortDepth():Void
    {
        __depthsArr.sort(sortByScale);
 
        var i:Int=__depthsArr.length;
		while((i--)>0)
		{
            this.addChild(__depthsArr[i]);
		}
    }
    /*
    根据缩放值确定卡片A,卡片B的前后遮挡关系,给sortDepth方法提供比较结果
    */
    private function sortByScale(a:DisplayObject, b:DisplayObject):Int
    {
		if (ReflectUtil.getField(a, "scaleX") > ReflectUtil.getField(b, "scaleX"))
			return -1;
		else if (ReflectUtil.getField(a, "scaleX") < ReflectUtil.getField(b, "scaleX"))
			return 1;
 
		return 0;
    }
    private function circular(t:Float, b:Float, c:Float, d:Float):Float
    {
		return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
    }
}