在卡片上左右拖动,查看效果
代码解释:
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; } }