【前端應(yīng)該知道的那些事兒】運(yùn)動學(xué)基礎(chǔ)
【寫在前面的話:】
前不久剛看到過一句話:說好的技術(shù)文章應(yīng)該讓讀者感覺增加信心,而不是失去信心。
有感于這句話是因?yàn)橐郧坝X得發(fā)一些貌似高深的,看起來nb的東西才算一篇好博文,可是多少有點(diǎn)炫技的成分。可是后來越發(fā)覺想把一個看起來簡單的問題說通透也著實(shí)不易。我希望今后的文章多少能帶給更多的讀者一些幫助吧。 這是我的目標(biāo)之一。
web前端,確實(shí)算編碼里面的挺特殊的一個職位,不僅僅要理性的編碼,還要感性的接觸UI,通常我都把這種工作叫做需要情商的碼字工作者。
要說前端有多難,我想會被很多做算法或者底層的同學(xué)所不齒。確實(shí),前臺的工作并不算難,尤其是web端的前臺,有困難的部分,那也是少數(shù)。所以在互聯(lián)網(wǎng)發(fā)展初期,都沒有前端這個職位,就算后來有了前端這個職位,也曾被當(dāng)作是門檻最低的IT類職位之一。很多同學(xué)學(xué)習(xí)前端相關(guān)的知識,初衷很簡單,因?yàn)楹脤W(xué),包括當(dāng)年的自己也是一樣 : )
當(dāng)然,如今隨著交互邏輯的不斷復(fù)雜,用戶體驗(yàn)的不斷提升,外帶很多后端的邏輯也都紛紛轉(zhuǎn)到前端來實(shí)現(xiàn)。前端的工作者們開始有了一些價(jià)值。當(dāng)然,你要擔(dān)當(dāng)?shù)母啵厝恍枰獣母唷K匀缃駥τ谝粋€優(yōu)秀的前臺編碼工作者來說,要求高了很多。
但是同樣,還是不能算難。因?yàn)槟呐虑岸藗冮_始接觸一些算法,一些數(shù)學(xué)物理的東西,但常用的,通常也僅限于初衷,高中的程度。所以神馬高等數(shù)學(xué),高等物理之類的。咱暫時(shí)還用不上,大家完全不用懼。
那么,比如:
【關(guān)于緩動】
我相信在DHTML時(shí)代,也就是所謂的動態(tài) html 的時(shí)候,那時(shí)候javascript 腳本除了用來做一些表單驗(yàn)證和提交之外,開始干起讓頁面動起來的事情。最常見的莫過于什么幻燈片,輪播banner之類的。甚至在當(dāng)時(shí)能夠手寫出一款好的,兼容的輪播插件成了一件非常niu的事兒。那么回想一下,我們最開始學(xué)習(xí),嘗試自己寫一個輪播插件的時(shí)候,遇到的頭疼的事兒,我想緩動應(yīng)該算一個了吧。
好吧,咱們就先來說說它。
緩動難嗎,不難,我們先說一個最簡單的。所謂“緩動”,無非就是運(yùn)動的越來越緩慢唄。那么怎么讓一個物體運(yùn)動的越來越緩慢呢。
我們用的計(jì)時(shí)器,不管你是用setInterval也好,setTimeout也好,或者 requestAnimationFrame也好,思路都一樣。反正把它想象成是單位時(shí)間重復(fù)調(diào)用某個函數(shù)就行。那就好,既然單位時(shí)間是一樣,那么我們讓單位時(shí)間內(nèi) 物體運(yùn)行的距離 越來越小不就成了“緩動”了嗎。
ok,咱們可以試試看:
假如我們有一段固定的路程100米,然后讓物體 每個單位時(shí)間里面運(yùn)動的距離都是 它距離目的地剩余距離的1/10, 什么意思呢。 即物體最開始距離目的地100米,那么它第一個單位時(shí)間里朝目的地運(yùn)動 100*1/10 ,即10米, 于是,第二個單位時(shí)間里,它距離目的地 就只有90米了,那么第二次運(yùn)動 90*1/10 ,即9米,... 不斷疊加下去,由于物體總是距離目的地越來越近,那么 它單位時(shí)間里運(yùn)動的距離必然越來越小。 這不就達(dá)到了 我們緩動的目的的了么。
(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();
})();
當(dāng)然,這是最簡單的模式,咱們接著往下看。
那么,那些看起來高深的緩動公式是怎么來的呢??
其實(shí)也很簡單,想想我們初中,高中學(xué)的數(shù)學(xué)吧, 二次函數(shù),三角函數(shù)之類的。
先看二次函數(shù),也就是我們的拋物線:

為什么我要說先看二次函數(shù)或者三角函數(shù)呢。他們的軌跡跟 緩動有什么關(guān)系?我們接著往下看:
拿上面的那個最簡單的demo舉例,我們把 方塊的運(yùn)行距離s 和時(shí)間 t的運(yùn)動關(guān)系 畫出來,看會是什么樣子的。
看這個demo:
Tween Demo 2
這里面的緩動算法跟上面那個最簡單的模式一模一樣。我們把它的 t-s 路線圖畫出來,可以看出一點(diǎn)端倪了吧。沒看出來的同學(xué),把它旋轉(zhuǎn)一下,想象成 x軸 時(shí)間, y軸位移。那么是不是就跟 我上面畫那個二次函數(shù) 的左半部分 的形狀很像。
所以,到此為止,相信不難理解,為什么緩動的公式通常和二次函數(shù)或者三角函數(shù)有關(guān),直觀一點(diǎn)的話說,就是在某一個區(qū)間內(nèi) 位移的變化率 是隨著時(shí)間遞減的。 那么這種 軌跡都可以用作 緩動公式。
那么,
我們怎么用二次函數(shù)來做緩動呢?很簡單,大家隨著我的思路來。我們要設(shè)計(jì)一個緩動的接口api,假如是類似下面這樣,我們先想一個最簡單的方式。
【已知】:一物體要從 0 運(yùn)行到 400, 運(yùn)行時(shí)間為1秒(1000ms)。
那么我們怎么為它來設(shè)計(jì)一個二次函數(shù)的緩動呢。我們先畫一個示意圖:

那么求的 方程 的系數(shù) a = 0.00005, b = -0.1;
那么方程就出來了 s = 0.00005*t^2 - 0.1*t (0 < t <=1000);
剩下的就好辦了,把每個時(shí)間點(diǎn)的位置渲染出來就好了。
例如,我們做個例子,設(shè)計(jì)一個api:
// from 表示起始點(diǎn) // to 表示到達(dá)位置 // t 表示運(yùn)行總時(shí)間 tween(from, to, t)
按照上面說的思路,其實(shí)就是已知運(yùn)行距離(from-to)和運(yùn)行時(shí)間t ,求一個二次函數(shù)公式而已。
// 二次函數(shù) s = a*t^2 + b*t;
// 頂點(diǎn): (to-from) = a*t^2 + b*t
// 右側(cè)x軸交點(diǎn): 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生成的點(diǎn)圖,會占內(nèi)存,請不要測試過多次 ^^).
原理其實(shí)就是那么簡單,其實(shí)大家可以自己試一下,熟悉了之后完全可以封裝出自己的好用的,易用的緩動方法。
用這類的二次函數(shù),還有一個很常見的場景,就是“重力系統(tǒng)”。
我們知道,如果忽略所謂的空氣阻力和一些外界干擾因素,重力系統(tǒng)其實(shí)就完全可以簡化成 二次函數(shù)(拋物線)問題。
比如我們做個小游戲,系統(tǒng)有固定的向下的重力 g ,那么由用戶操作的主角 在像上跳的過程中,就完全可以按 上面說的方式來考慮。
基本思路上面都說了,這次我們換個思路。都說數(shù)學(xué)和物理是相通的,那么這次已知 在一個重力系統(tǒng)中,跳起初速度和重力大小。那么假設(shè)一個物體跳起該怎么運(yùn)動呢?
S = v0*t + a*t^2
v0 = a*t
這兩個應(yīng)該是初中的物理公式吧。已知初速度v0 和加速度 a ,求位移還不簡單。
其他的我就不多說了,看一個簡單的demo吧:
彈跳Demo
看完了【二次函數(shù)】,咱們再看看【三角函數(shù)】,其實(shí)在我們常用的特效中,三角函數(shù)能做的事情比二次函數(shù)多很多。但是今天就只講跟【緩動】相關(guān)的。

前面說了,凡是大家看到類似這種“山坡”形狀的圖,基本都可以做成類似的緩動。那么我們?nèi)in函數(shù)的前 PI/2 部分,可以看出他也完全滿足所謂的 緩動的圖形條件。
而且,基于sin函數(shù)做的緩動公式 相對于二次函數(shù)而言,思路更簡單。因?yàn)楦菀椎贸?位移相對時(shí)間的公式S-T:
/**
我們假設(shè) 每一幀 間隔時(shí)間為 dt, 那么在這個dt時(shí)間內(nèi) 運(yùn)動的距離為ds
那么,假設(shè)一個物體 從 from 移動到 to 所花的時(shí)間為t, 則容易得出 在這個時(shí)間區(qū)間內(nèi)用sin公式得到 每個dt的位移公式ds
*/
function tween (from, to, t) {
// sin函數(shù); ds = (to-from)*Math.sin(Math.PI*dt/(2*t));
}
剩下的工作,就是把計(jì)算出來的當(dāng)前位置渲染到頁面即可。我們這里還是以類似的例子為例:
sin 緩動 demo
【寫在后面的話】
不知不覺也寫了這么多了,所謂“會者不難”,本文說到底其實(shí)涉及的技術(shù)技巧其實(shí)并不多,我花這么大篇幅來說也是希望能給對運(yùn)動學(xué)還不甚了解的同學(xué)一點(diǎn)幫助吧。
我希望我能把簡單的東西說明白,至于有沒有達(dá)到這個目的我也不得而知。
其實(shí)在前端的工作里,還有一些常用的數(shù)學(xué)和物理知識,但是都不難。說起來都很簡單。比如前一陣的mac QQ瀏覽器的logo 周圍的閃動旋轉(zhuǎn)的星星。就是用了簡單的橢圓公式。
http://hongru.github.com/test/qqbrowser/index.html
浙公網(wǎng)安備 33010602011771號