more
Back

细节再细节,弹性滚动效果的代码实现

一个稍微懂点代码的设计师大多都可以在自己的圈子力里得瑟一下,技术上的雕虫小技也可以被放大到脑袋顶着光环,我在工作的前两年占尽了这个便宜。但这两年原型工具的大爆发把以前需要几百行的代码翘着双腿点个鼠标就实现了,颇有不爽,所以不服只能来硬的,动手。

随即就有了这个,一个模仿iOS 8 消息列表的动态交互效果,细节上有一点小难度,值得一试。这里仍然使用我唯一了解的AS3脚本语言来写,第一个是iPhone消息列表的录屏,第二个是我代码实现的效果。

实现这个效果,有三个步骤,一样样来看。

1. 描述操作-响应背后的规则: 界面中有多个图形元素组成的列表,当我上下滑动列表时,手指点击的图形元素时时跟随我的手指,而较远的图形元素会延时跟随,距离越远延时愈多(且越远处的元素越趋于一致的延时),速度越快,运动中图形间距越大。

2.把这个规则用逻辑关系及数学公式表达: a)既然规则是距离越远延时越多,那么算法就是先测定图形和手指点击的图形的距离,然后视距离在这个图形运动到最终位置的过程中加一些帧让这个图形晚一点到达。b)因为越远的元素越趋于一致的延时,需要在刚才算出的距离上再加一个函数使得很远处的图形延时是趋于一致的,什么函数是x变化时,y开始变化大,后来几乎不变呢?哈哈哈,这就是我很早想到但一直没用的反正切了,查看曲线点击这里。c)怎样保证速度越快,图形间距越大: 因为我们用的是延迟帧的方法,这样当你速度快的时候,一帧内你拖动的距离越大,延迟一帧会自然导致间距越大,效果自然达成,不劳而获。

3 用代码具体化这些关系并和实际图形创建关联。见以下

import flash.events.MouseEvent;
import flash.events.Event;
import caurina.transitions.Tweener;
// 调用需要用到的类
var beginPoint:Number;
var bias:Number;
var mode1OriginalY:Number = mode1_mc.y;
var mode2OriginalY:Number = mode2_mc.y;
var mode3OriginalY:Number = mode3_mc.y;
// 变量定义,定义beginPoint和Bias用于计算鼠标按下后将页面拖动了多远距离,mode*OriginalY记录下几个图形元素的初始位置,mode*_mc.y就是几个图形元素当前的位置。
this.addEventListener(MouseEvent.MOUSE_DOWN, DragBegin);
function DragBegin(e:MouseEvent):void {
 this.removeEventListener(MouseEvent.MOUSE_DOWN, DragBegin);
 this.addEventListener(MouseEvent.MOUSE_UP, DragEnd);
 beginPoint = mouseY;
 this.addEventListener(Event.ENTER_FRAME, modesMove);
}
// 基本事件定义,鼠标按下时开始瞬间记录当前鼠标位置放在beginPoint变量中,用于之后差值计算,然后给整体加上ENTER_FRAME事件来做动画了
function modesMove(e:Event):void{
 bias=(mouseY-beginPoint);
 
 mode1_mc.y =mode1_mc.y+ ((mode1OriginalY+bias)-mode1_mc.y)/(2+2*Math.abs(Math.atan((beginPoint-mode1OriginalY)/300)*2/Math.PI));
 mode2_mc.y =mode2_mc.y+ ((mode2OriginalY+bias)-mode2_mc.y)/(2+2*Math.abs(Math.atan((beginPoint-mode2OriginalY)/300)*2/Math.PI));
 mode3_mc.y =mode3_mc.y+ ((mode3OriginalY+bias)-mode3_mc.y)/(2+2*Math.abs(Math.atan((beginPoint-mode3OriginalY)/300)*2/Math.PI));
 
}
// 动画实现, 弹性效果关键就在这里了。这个function用于每一帧时时计算出图形元素的位置,拿mode1_mc举例子,它下一帧运动到哪就是橙色代码得出的值。首先它最终目的地:mode1OriginalY+bias.即它初始位置加上鼠标拖动了多远。它的下一帧显然不能直接运动到最终目的地(那样就没动画了),所以它每个下一帧都运动到它与最终目的地之间的一个位置上。这个位置等于它现在的位置model_mc.y 加上它到目的地的距离(mode1OriginalY+bias-mode1_mc.y)除以一个系数。这样随着它与目的地距离越来越近它会运动得越来越慢进而有了缓动的效果。而弹性的关键就在这个系数,系数越大,它需要越长的时间运动到最终位置,这样我们让离鼠标近的图形系数小,而远处一点的系数大,这样产生的滞后感就是弹性效果了。

// 这个系数就是用到了反正切的(2+2*Math.abs(Math.atan((beginPoint-mode1OriginalY)/300)*2/Math.PI));看着长不难懂。beginPoint-mode1OriginalY是图形离鼠标按下位置的距离,以像素为单位随随便便就几百,所以除以300以便反正切Math.atan()得到一个敏感的变化范围,这个范围在正负π/2之间,那么它乘以2/π后我们得到的范围是正负1。再加上Math.abs取绝对值我们得到了0-1的范围。

//这个0-1的范围非常有用也很特别,当x在0到1变化时,y在0-0.76间变化,当x在0到3变化时,y再0-0.99变化,x无穷大时,y=1. 也就是说对于3以上的值,y是不敏感的,这样就解决了很远的图形随数值变化可能行为过激的问题。将这个得出的系数乘以2使得差别更大(其实这个2是为了后边调来调去看效果的)之后加2保证系数的最终范围是2+0到2+2 也就是2-4之间变化,从而实现弹性效果。

好,大功告成!稍后附上源代码。

 

 

more
Back