導語:
運動框架的實現思路運動,其實就是在一段時間內改變left、right、width、height、opacity的值,到達目的地之后停止。現在按照以下步驟來進行我們的運動框架的封裝:勻速運動、緩沖運動、多物體運動、任意值變化。
運動框架的實現思路
</br>
運動,其實就是在一段時間內改變left、right、width、height、opacity的值,到達目的地之后停止。
現在按照以下步驟來進行我們的運動框架的封裝:勻速運動、緩沖運動、多物體運動、任意值變化。
(一) 勻速運動
速度動畫
運動基礎
思考:如何讓 div 動起來?
如下:
設置元素絕對定位,只有絕對定位后,left,top等值才會生效。
定時器的使用(動態改變值),這里使用setInterval()每隔指定時間執行代碼。
計時器setInterval(函數,交互時間(毫秒)):在執行時,從載入頁面后每隔指定的時間執行代碼。
取消計時器clearInterval(函數)方法可取消由setInterval()設置的交互時間。
獲取當前的位置,大小等等。offsetLeft(當前元素相對父元素的位置)。
速度--物體運動的快慢
定時器間隔時間
改變值的大小
。。。
根據上面的信息我們就可以開始封裝運動框架創建一個變化的div了。
1. /**
2. * 運動框架-1-動起來
3. *@param {HTMLElement} element 進行運動的節點
4. * /
5. var timer = null;
6. function startMove(element){
7. timer = setInterval( function () { // 定時器
8. element.style.left = element.offsetLeft + 5 + "px;
9. },30);
10. }
你沒看錯,就是那么簡單。但是等等,what ? 怎么不會停?! WTF !
那是因為我們沒有運動終止條件。好在還是比較簡單,之間在定時器內部,判斷到達目標值,清除定時器就行啦!
1. /**
2. * 運動框架-2-運動終止
3. *@param {HTMLElement} element 進行運動的節點
4. *@param {number} iTarget 運動終止條件
5. */
6. var timer = null;
7. function startMove( element, iTarget) {
8. timer = setInterval ( function ( ) {
9. element.style.left = element.offsetLeft + 5 + "px";
10. if (element.offsetLeft ===iTarget) { // 停止條件
11. clearInterval ( timer );
12. };
13. }, 30);
14. }
就這樣是不是就完成了呢? no ,還有一些bug需處理。
運動中的bug
速度取到某些值會無法停止
到達位置之后再點擊還會動
重復點擊速度加快
速度無法更改
解決bug
速度取到某些值會無法停止(這個Bug稍后解決,在進化過程中自然解決)
把運動和停止隔開(if/else)
在開始運動時,關閉已有定時器
把速度用變量保存
1. /**
2. * 運動框架‐3‐解決Bug
3. */
4. vartimer=null;
5. function startMove ( element , iTarget ) {
6. clearInterval ( timer ) ; // 開始運動前關閉已有定時器
7. timer = setInterval ( function ( ) {
8. var iSpeed = 5 ;//把速度用變量保存
9. // 把運動和停止隔開( if / else )
10. if (element.offsetLeft == iTarget ) { // 結束運動
11. clearInterval ( timer );
12. } else {
13. element.style.left = element.offsetLeft + iSpeed + "px";
14. };
15. } , 30);
16. }
這樣一個簡單的運動框架就完成了。但是,再等等。只能向右走?別急,我們不是定義了把速度變成為了變量嗎?只需要對它進行一些處理就行啦!
var iSpeed = 5;
1. // 判斷距離目標位置,達到自動變化速度正負
2. var iSpeed = 0 ;
3. if (element.offsetLeft < iTarget) {
4. iSpeed = 5 ;
5. } else {
6. iSpeed = -5;
7. };
透明度動畫
用變量alpha儲存當前透明度。
把上面的element.offsetLeft改成變量alpha。
運動和停止條件部分進行更改。如下:
1. // 透明度瀏覽器兼容實現
2. if ( alpha === iTarget ) {
3. clearInterval ( timer ) ;
4. } else {
5. alpha += speed;
6. element.style.filter = 'alpha(opacity:' + alpha + ')' ; // 兼容IE
7. element.style.opacity = alpha / 100; // 標準
8. }
(二)緩沖動畫
思考:怎么樣才是緩沖動畫?
應該有以下幾點:
逐漸變慢,最后停止
距離越遠速度越大
速度由距離決定
速度=(目標值-當前值)/縮放系數
Bug :速度取整(使用Math方法),不然會閃
向上取整。Math.ceil(iSpeed)
向下取整。Math.floor(iSpeed)
還是對速度作文章:
1. /**
2. * 運動框架-4-緩沖動畫
3. */
4. functionstartMove ( element,iTarget ) {
5. clearInterval ( timer );
6. timer = setInterval ( function ( ) {
7. // 因為速度要動態改變,所以必須放在定時器中
8. var iSpeed = ( iTarget - element.offsetLeft ) / 10; // (目標值 - 當前值) / 縮放系數 = 速度
9. ISpeed = ISpeed > 0 ? Math.ceil ( iSpeed ) : Math.floor ( iSpeed ) ; // 速度取整
10. if (element.offsetLeft === iTarget ) // 結束運動
11. clearInterval ( timer ) ;
12. } else {
13. element.style.left = element.offsetLeft + iSpeed + "px";
14. }
15. }, 30);
16. }
做到這里,(速度取到某些值會無法停止)這個Bug就自動解決啦!
例子:緩沖菜單
跟隨頁面滾動的緩沖側邊欄
潛在問題目標值不是整數時
(三)多物體運動
思考:如何實現多物體運動?
單定時器,存在問題。每個div一個定時器
定時器作為對象的屬性
直接使用element.timer把定時器變成對象上的一個屬性。
參數的傳遞:物體/目標值
比較簡單把上面框架的進行如下更改:timer-->element.timer
就這樣就行啦!
(四)任意值變化
咳咳。我們來給div加個1px的邊框。boder :1px solid #000
然后來試試下面的代碼
1. setInterval ( function() {
2. oDiv.style.width=oDiv.offsetWidth‐1+"px";
3. },30)
嗯,神奇的事情發生了!what?我設置的不是寬度在減嗎?怎么尼瑪增加了! 不對啊,大兄弟。
究竟哪里出了問題呢?
一起找找資料,看看文檔,原來offset這一系列的屬性都會存在,被其他屬性干擾的問題。
好吧,既然不能用,那么我們就順便把任意值變化給做了吧。
第一步:獲取實際樣式
使用offsetLeft..等獲取樣式時, 若設置了邊框, padding, 等可以改變元素寬度高度的屬性時會出現BUG..
通過查找發現element.currentStyle(attr)可以獲取計算過之后的屬性。
但是因為兼容性的問題,需封裝getStyle函數。(萬惡的IE)
當然配合CSS的box-sizing屬性設為border-box可以達到一樣的效果 ? (自認為,未驗證)。
1. /**
2. * 獲取實際樣式函數
3. * @param {HTMLElement} element 需要尋找的樣式的html節點
4. * @param {String]}attr在對象中尋找的樣式屬性
5. * @returns {String} 獲 取 到 的 屬 性
6. */
7. functiongetStyle(element,attr){
8. //IE寫法
9. if ( element.currentStyle ) {
10. return element.currentStyle[attr];
11. // 標準
12. } else {
13. return getComputedStyle ( element , false ) [attr];
14. }
15. }
第二步:改造原函數
添加參數,attr表示需要改變的屬性值
更改element.offsetLeft為getStyle(element, attr)。
需要注意的是:getStyle(element, attr)不能直接使用,因為它獲取到的字符串,例:10px。
變量iCurrent使用parseInt(),將樣式轉成數字。
element.style.left為element.style[attr]。
1. /**
2. * 運動框架‐4‐任意值變化
3. * @param {HTMLElement} element 運 動 對 象
4. *@param{string} attr 需要改變的屬性。
5. * @param {number} iTarget 目 標 值
6. */
7. functionstartMove ( element , attr , iTarget ) {
8. clearInterval(element.timer);
9. element.timer = setInterval ( function ( ) {
10. //因為速度要動態改變,所以必須放在定時器中
11. variCurrent=0;
12. iCurrent=parseInt(getStyle(element,attr));//實際樣式大小
13. variSpeed=(iTarget‐iCurrent)/10;//(目標值‐當前值)/縮放系數=速度
14. iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);//速度取整
15. if(iCurrent===iTarget){//結束運動
16. clearInterval(element.timer);
17. } else {
18. element.style[attr]=iCurrent+iSpeed+"px";
19. };
20. },30);
21. };
試一試,這樣是不是就可以了呢?
還記得上面我們寫的透明度變化嗎? 再試試
果然還是不行, (廢話,你見過透明度有"px"單位的么? - -白眼 )
第三步:透明度兼容處理
思考:需要對那些屬性進行修改?
判斷attr是不是透明度屬性opacity 。
對于速度進行處理。
為透明度時,由于獲取到的透明度會是小數,所以需要 * 100
并且由于計算機儲存浮點數的問題,還需要將小數,進行四舍五入為整數。使用:
Math.round(parseFloat(getStyle(element, attr)) * 100)。
否則,繼續使用默認的速度。
對結果輸出部分進行更改。
判斷是透明度屬性,使用透明度方法
否則,使用使用默認的輸出格式。
1. /**
2. * 運動框架‐5‐兼容透明度
3. * @param {HTMLElement} element 運 動 對 象
4. *@param{string} attr 需要改變的屬性。
5. * @param {number} iTarget 目 標 值
6. */
7. function startMove ( element , attr , iTarget ) {
8. clearInterval ( element.timer );
9. element.timer = setInterval ( function ( ) {
10. //因為速度要動態改變,所以必須放在定時器中
11. var iCurrent=0;
12. if ( attr === "opacity" ){//為透明度時執行。
13. iCurrent = Math.round ( parseFloat ( getStyle ( element , attr ) ) * 100 );
14. } else { //默認情況
15. iCurrent=parseInt(getStyle(element,attr));//實際樣式大小 }
16. variSpeed=(iTarget‐iCurrent)/10;//(目標值‐當前值)/縮放系數=速度
17. iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);//速度取整
18. if(iCurrent===iTarget){//結束運動
19. clearInterval(element.timer);
20. } else {
21. if ( attr==="opacity"){//為透明度時,執行
22. element.style.filter="alpha(opacity:"+(iCurrent+iSpeed)+")";//IE
23. element.style.opacity=(iCurrent+iSpeed)/100;//標準
24. }else{//默認
25. element.style [ attr ] =iCurrent+iSpeed+"px";
26. }
27. }
28. },30);
29. }
到這里,這個運動框架就基本上完成了。但是,我們是追求完美的不是嗎?
繼續進化!
(五)鏈式動畫
鏈式動畫:顧名思義,就是在該次運動停止時,開始下一次運動。
如何實現呢?
使用回調函數:運動停止時,執行函數
添加func形參(回調函數)。
在當前屬性到達目的地時iCurrent === iTarget,判斷是否有回調函數存在,有則執行。
if (iCurrent === iTarget) {//結束運動
clearInterval(element.timer);
if (func) {
func();//回調函數
}
}
good,鏈式動畫完成!距離完美還差一步!
(六)同時運動
思考:如何實現同時運動?
使用JSON傳遞多個值
使用for in循環,遍歷屬性,與值。
定時器問題!(運動提前停止)
在循環外設置變量,假設所有的值都到達了目的值為true
在循環中檢測是否到達目標值,若沒有值未到則為false
在循環結束后,檢測是否全部達到目標值.是則清除定時器
實現:
刪除attr與iTarget兩個形參,改為json
在函數開始時,設置一個標記var flag = true; //假設所有運動到達終點.
在定時器內使用for in,遍歷屬性與目標,改寫原來的attr與iTarget,為json的屬性與值
修改運動終止條件,只有每一項的實際屬性值iCurrent,等于目標值json[attr]時,flag才為true。清除定時器,判斷是否回調。
否則,繼續執行代碼,直到所有屬性值等于目標值。
完美運動框架
1. /**
2. * 獲取實際樣式函數
3. *@param {HTMLElement} element 需要尋找的樣式的html節點
4. *@param {String]}attr在對象中尋找的樣式屬性
5. * @returns {String} 獲 取 到 的 屬 性
6. */
7. function getStyle(element,attr){
8. //IE寫法
9. if ( element.currentStyle ) {
10. return element.currentStyle[attr];
11. //標準
12. } else {
13. return getComputedStyle(element,false)[attr];
14. }
15. }
16. /**
17. * 完美運動框架
18. *@param{HTMLElement}element 運動對象
19. *@param{JSON} json 屬性:目標值
20. * @property{String}attr 屬性值
21. * @config {Number}target 目標值
22. *@param{function} func 可選,回調函數,鏈式動畫。
23. */
24. function startMove ( element , json , func ) {
25. var flag=true;//假設所有運動到達終點.
26. clearInterval(element.timer);
27. element.timer=setInterval(function(){
28. for(var attr in json){
29. //1.取當前的屬性值。
30. var iCurrent=0;
31. if(attr==="opacity"){//為透明度時執行。
32. iCurrent=Math.round(parseFloat(getStyle(element,attr))*100);
33. }else{//默認情況
34. iCurrent=parseInt(getStyle(element,attr));//實際樣式大小
35. }
36. //2.算運動速度,動畫緩沖效果
37. var iSpeed=(json[attr]‐iCurrent)/10;//(目標值‐當前值)/縮放系數=速度
38. iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);//速度取整
39. //3.未到達目標值時,執行代碼
40. if(iCurrent!=json[attr]){
41. flag=false;//終止條件
42. if(attr==="opacity"){//為透明度時,執行
43. element.style.filter="alpha(opacity:"+(iCurrent+iSpeed)+")";//IE
44. element.style.opacity=(iCurrent+iSpeed)/100;//標準
45. }else{//默認
46. element.style[attr]=iCurrent+iSpeed+"px";
47. }
48. }else{
49. flag=true;
50. }
51. //4. 運動終止,是否回調
52. if(flag){
53. clearInterval(element.timer);
54. if(func){
55. func();
56. }
57. }
58. }
59. },30);
60. }
運動框架總結
運動框架演變過程
框架 | 變化 |
---|---|
statrMove(element) | 運動 |
startMove(element,iTarget) | 勻速 -- > 緩沖 --> 多物體 |
startMove(element,attr,iTargrt) | 任意值 |
startMove(element,attr,iTargrt,func) | 鏈式運動 |
startMove(element,json,func) | 多值(同時) --> 完美運動框架 |