【写在前面的话:】
前不久刚看到过一句话:说好的技术文章应该让读者感觉增加信心,而不是失去信心。
有感于这句话是因为以前觉得发一些貌似高深的,看起来nb的东西才算一篇好博文,可是多少有点炫技的成分。可是后来越发觉想把一个看起来简单的问题说通透也着实不易。我希望今后的文章多少能带给更多的读者一些帮助吧。 这是我的目标之一。
web前端,确实算编码里面的挺特殊的一个职位,不仅仅要理性的编码,还要感性的接触UI,通常我都把这种工作叫做需要情商的码字工作者。
要说前端有多难,我想会被很多做算法或者底层的同学所不齿。确实,前台的工作并不算难,尤其是web端的前台,有困难的部分,那也是少数。所以在互联网发展初期,都没有前端这个职位,就算后来有了前端这个职位,也曾被当作是门槛最低的IT类职位之一。很多同学学习前端相关的知识,初衷很简单,因为好学,包括当年的自己也是一样 : )
当然,如今随着交互逻辑的不断复杂,用户体验的不断提升,外带很多后端的逻辑也都纷纷转到前端来实现。前端的工作者们开始有了一些价值。当然,你要担当的更多,必然需要会的更多。所以如今对于一个优秀的前台编码工作者来说,要求高了很多。
但是同样,还是不能算难。因为哪怕前端们开始接触一些算法,一些数学物理的东西,但常用的,通常也仅限于初衷,高中的程度。所以神马高等数学,高等物理之类的。咱暂时还用不上,大家完全不用惧。
那么,比如:
【关于缓动】
我相信在DHTML时代,也就是所谓的动态 html 的时候,那时候javascript 脚本除了用来做一些表单验证和提交之外,开始干起让页面动起来的事情。最常见的莫过于什么幻灯片,轮播banner之类的。甚至在当时能够手写出一款好的,兼容的轮播插件成了一件非常niu的事儿。那么回想一下,我们最开始学习,尝试自己写一个轮播插件的时候,遇到的头疼的事儿,我想缓动应该算一个了吧。
好吧,咱们就先来说说它。
缓动难吗,不难,我们先说一个最简单的。所谓“缓动”,无非就是运动的越来越缓慢呗。那么怎么让一个物体运动的越来越缓慢呢。
我们用的计时器,不管你是用setInterval也好,setTimeout也好,或者 requestAnimationFrame也好,思路都一样。反正把它想象成是单位时间重复调用某个函数就行。那就好,既然单位时间是一样,那么我们让单位时间内 物体运行的距离 越来越小不就成了“缓动”了吗。
ok,咱们可以试试看:
假如我们有一段固定的路程100米,然后让物体 每个单位时间里面运动的距离都是 它距离目的地剩余距离的1/10, 什么意思呢。 即物体最开始距离目的地100米,那么它第一个单位时间里朝目的地运动 100*1/10 ,即10米, 于是,第二个单位时间里,它距离目的地 就只有90米了,那么第二次运动 90*1/10 ,即9米,... 不断叠加下去,由于物体总是距离目的地越来越近,那么 它单位时间里运动的距离必然越来越小。 这不就达到了 我们缓动的目的的了么。
(function () { var moveDis = 0, conEl = document.getElementById('container'), maxDis = (conEl.offsetWidth-22) || (800-20), // 总距离 moveEl = document.getElementById('move'); function step () { var nowLeft = parseInt(moveEl.style['left']), leftDis = maxDis - nowLeft, // 获取距离目的地的距离 stepDis = Math.ceil(leftDis*.015); // 每次移动 剩余距离的 固定百分比。 nowLeft += stepDis; // 不断叠加 moveEl.style['left'] = nowLeft + 'px'; requestAnimFrame(step); // repeat } step(); })();
当然,这是最简单的模式,咱们接着往下看。
那么,那些看起来高深的缓动公式是怎么来的呢??
其实也很简单,想想我们初中,高中学的数学吧, 二次函数,三角函数之类的。
先看二次函数,也就是我们的抛物线:
为什么我要说先看二次函数或者三角函数呢。他们的轨迹跟 缓动有什么关系?我们接着往下看:
拿上面的那个最简单的demo举例,我们把 方块的运行距离s 和时间 t的运动关系 画出来,看会是什么样子的。
看这个demo:
Tween Demo 2
这里面的缓动算法跟上面那个最简单的模式一模一样。我们把它的 t-s 路线图画出来,可以看出一点端倪了吧。没看出来的同学,把它旋转一下,想象成 x轴 时间, y轴位移。那么是不是就跟 我上面画那个二次函数 的左半部分 的形状很像。
所以,到此为止,相信不难理解,为什么缓动的公式通常和二次函数或者三角函数有关,直观一点的话说,就是在某一个区间内 位移的变化率 是随着时间递减的。 那么这种 轨迹都可以用作 缓动公式。
那么,
我们怎么用二次函数来做缓动呢?很简单,大家随着我的思路来。我们要设计一个缓动的接口api,假如是类似下面这样,我们先想一个最简单的方式。
【已知】:一物体要从 0 运行到 400, 运行时间为1秒(1000ms)。
那么我们怎么为它来设计一个二次函数的缓动呢。我们先画一个示意图:
那么求的 方程 的系数 a = 0.00005, b = -0.1;
那么方程就出来了 s = 0.00005*t^2 - 0.1*t (0 < t <=1000);
剩下的就好办了,把每个时间点的位置渲染出来就好了。
例如,我们做个例子,设计一个api:
// from 表示起始点 // to 表示到达位置 // t 表示运行总时间 tween(from, to, t)
按照上面说的思路,其实就是已知运行距离(from-to)和运行时间t ,求一个二次函数公式而已。
// 二次函数 s = a*t^2 + b*t; // 顶点: (to-from) = a*t^2 + b*t // 右侧x轴交点: 0 = a*(2t)^2 + b*2t // 得出 a = -(to-from)/t^2; b = 2(to-from)/t; var left = a*st*st + b*st; o.style['left'] = from + left + 'px';
看demo:
Tween Demo 3 (demo里面由于用的普通的dom生成的点图,会占内存,请不要测试过多次 ^^).
原理其实就是那么简单,其实大家可以自己试一下,熟悉了之后完全可以封装出自己的好用的,易用的缓动方法。
用这类的二次函数,还有一个很常见的场景,就是“重力系统”。
我们知道,如果忽略所谓的空气阻力和一些外界干扰因素,重力系统其实就完全可以简化成 二次函数(抛物线)问题。
比如我们做个小游戏,系统有固定的向下的重力 g ,那么由用户操作的主角 在像上跳的过程中,就完全可以按 上面说的方式来考虑。
基本思路上面都说了,这次我们换个思路。都说数学和物理是相通的,那么这次已知 在一个重力系统中,跳起初速度和重力大小。那么假设一个物体跳起该怎么运动呢?
S = v0*t + a*t^2
v0 = a*t
这两个应该是初中的物理公式吧。已知初速度v0 和加速度 a ,求位移还不简单。
其他的我就不多说了,看一个简单的demo吧:
弹跳Demo
看完了【二次函数】,咱们再看看【三角函数】,其实在我们常用的特效中,三角函数能做的事情比二次函数多很多。但是今天就只讲跟【缓动】相关的。
前面说了,凡是大家看到类似这种“山坡”形状的图,基本都可以做成类似的缓动。那么我们取sin函数的前 PI/2 部分,可以看出他也完全满足所谓的 缓动的图形条件。
而且,基于sin函数做的缓动公式 相对于二次函数而言,思路更简单。因为更容易得出 位移相对时间的公式S-T:
/** 我们假设 每一帧 间隔时间为 dt, 那么在这个dt时间内 运动的距离为ds 那么,假设一个物体 从 from 移动到 to 所花的时间为t, 则容易得出 在这个时间区间内用sin公式得到 每个dt的位移公式ds */ function tween (from, to, t) { // sin函数; ds = (to-from)*Math.sin(Math.PI*dt/(2*t)); }
剩下的工作,就是把计算出来的当前位置渲染到页面即可。我们这里还是以类似的例子为例:
sin 缓动 demo
【写在后面的话】
不知不觉也写了这么多了,所谓“会者不难”,本文说到底其实涉及的技术技巧其实并不多,我花这么大篇幅来说也是希望能给对运动学还不甚了解的同学一点帮助吧。
我希望我能把简单的东西说明白,至于有没有达到这个目的我也不得而知。
其实在前端的工作里,还有一些常用的数学和物理知识,但是都不难。说起来都很简单。比如前一阵的mac QQ浏览器的logo 周围的闪动旋转的星星。就是用了简单的椭圆公式。
http://hongru.github.com/test/qqbrowser/index.html