摘要:運動框架動起來進行運動的節(jié)點定時器你沒看錯,就是那么簡單。直接在定時器內(nèi)部,判斷到達目標值,清除定時器就行拉運動框架運動終止進行運動的節(jié)點運動終止條件。
運動框架的實現(xiàn)思路轉(zhuǎn)自個人博客三省吾身丶丶
原來是JS動畫效果,但是我會過頭再看的時候,發(fā)現(xiàn)太粗略了,于是重新寫了一篇。
喜歡別只收藏啊,點個推薦,大兄弟^ ^!
運動,其實就是在一段時間內(nèi)改變left、right、width、height、opactiy的值,到達目的地之后停止。
現(xiàn)在按照以下步驟來進行我們的運動框架的封裝:
勻速運動。
緩沖運動。
多物體運動。
任意值變化。
鏈式運動。
同時運動。
(一)勻速運動 速度動畫 運動基礎(chǔ)思考:如何讓div動起來?
如下:
設(shè)置元素為絕對定位,只有絕對定位后,left,top等值才生效。
定時器的使用(動態(tài)改變值),這里使用setInterval()每隔指定的時間執(zhí)行代碼。
計時器setInterval(函數(shù),交互時間(毫秒)):在執(zhí)行時,從載入頁面后每隔指定的時間執(zhí)行代碼。
取消計時器clearInterval(函數(shù)) 方法可取消由 setInterval() 設(shè)置的交互時間。
獲取當前的位置,大小等等。offsetLeft(當前元素相對父元素位置)。
速度--物體運動的快慢
定時器間隔時間
改變值的大小
根據(jù)上面的信息我們就可以開始封裝運動框架創(chuàng)建一個變化的div了。
/** * 運動框架-1-動起來 * @param {HTMLElement} element 進行運動的節(jié)點 */ var timer = null; function startMove(element) { timer = setInterval(function () {//定時器 element.style.left = element.offsetLeft + 5 + "px"; }, 30); }
你沒看錯,就是那么簡單。但是等等, what? 怎么不會停?WTF?
那是因為我們沒有運動終止條件。好再還是比較簡單。直接在定時器內(nèi)部,判斷到達目標值,清除定時器就行拉!
/** * 運動框架-2-運動終止 * @param {HTMLElement} element 進行運動的節(jié)點 * @param {number} iTarget 運動終止條件。 */ var timer = null; function startMove(element, iTarget) { timer = setInterval(function () { element.style.left = element.offsetLeft + 5 + "px"; if (element.offsetLeft === iTarget) {//停止條件 clearInterval(timer); } }, 30); }
就這樣是不是就完成了呢?已經(jīng)ok了呢?
no。還有一些Bug需要處理。
速度取到某些值會無法停止
到達位置后再點擊還會運動
重復(fù)點擊速度加快
速度無法更改
解決BUG速度取到某些值會無法停止(這個Bug稍后解決,在進化過程中自然解決)
把運動和停止隔開(if/else)
在開始運動時,關(guān)閉已有定時器
把速度用變量保存
/** * 運動框架-3-解決Bug */ var timer = null; function startMove(element, iTarget) { clearInterval(timer);//在開始運動時,關(guān)閉已有定時器 timer = setInterval(function () { var iSpeed = 5;//把速度用變量保存 //把運動和停止隔開(if/else) if (element.offsetLeft === iTarget) {//結(jié)束運動 clearInterval(timer); } else { element.style.left = element.offsetLeft + iSpeed + "px"; } }, 30); }
這樣一個簡單的運動框架就完成了。但是,再等等。只能向右走?別急,我們不是定義了把速度變成為了變量嗎?只需要對它進行一些處理就行啦!
var iSpeed = 5;-->
//判斷距離目標位置,達到自動變化速度正負 var iSpeed = 0; if (element.offsetLeft < iTarget) { iSpeed = 5; } else { iSpeed = -5; }透明度動畫
用變量alpha儲存當前透明度。
把上面的element.offsetLeft改成變量alpha。
運動和停止條件部分進行更改。如下:
//透明度瀏覽器兼容實現(xiàn) if (alpha === iTarget) { clearInterval(time); } else { alpha += speed; element.style.filter = "alpha(opacity:" + alpha + ")"; //兼容IE element.style.opacity = alpha / 100;//標準 }(二)緩沖動畫
思考:怎么樣才是緩沖動畫?
應(yīng)該有以下幾點:
逐漸變慢,最后停止
距離越遠速度越大
速度由距離決定
速度=(目標值-當前值)/縮放系數(shù)
Bug :速度取整(使用Math方法),不然會閃
向上取整。Math.ceil(iSpeed)
向下取整。Math.floor(iSpeed)
還是對速度作文章:
/** * 運動框架-4-緩沖動畫 */ function startMove(element, iTarget) { clearInterval(timer); timer = setInterval(function () { //因為速度要動態(tài)改變,所以必須放在定時器中 var iSpeed = (iTarget - element.offsetLeft) / 10; //(目標值-當前值)/縮放系數(shù)=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 if (element.offsetLeft === iTarget) {//結(jié)束運動 clearInterval(timer); } else { element.style.left = element.offsetLeft + iSpeed + "px"; } }, 30); }
做到這里,(速度取到某些值會無法停止)這個Bug就自動解決啦!
例子:緩沖菜單
跟隨頁面滾動的緩沖側(cè)邊欄
在線演示:codepen
(三)多物體運動潛在問題目標值不是整數(shù)時
思考:如何實現(xiàn)多物體運動?
單定時器,存在問題。每個div一個定時器
定時器作為對象的屬性
直接使用element.timer把定時器變成對象上的一個屬性。
參數(shù)的傳遞:物體/目標值
比較簡單把上面框架的進行如下更改:timer-->element.timer
就這樣就行啦!
(四)任意值變化咳咳。我們來給div加個1px的邊框。boder :1px solid #000
然后來試試下面的代碼
setInterval(function () { oDiv.style.width = oDiv.offsetWidth - 1 + "px"; }, 30)
嗯,神奇的事情發(fā)生了!what?我設(shè)置的不是寬度在減嗎?怎么尼瑪增加了! 不對啊,大兄弟。
究竟哪里出了問題呢?
一起找找資料,看看文檔,原來offset這一系列的屬性都會存在,被其他屬性干擾的問題。
好吧,既然不能用,那么我們就順便把任意值變化給做了吧。
第一步:獲取實際樣式使用offsetLeft..等獲取樣式時, 若設(shè)置了邊框, padding, 等可以改變元素寬度高度的屬性時會出現(xiàn)BUG..
通過查找發(fā)現(xiàn)element.currentStyle(attr)可以獲取計算過之后的屬性。
但是因為兼容性的問題,需封裝getStyle函數(shù)。(萬惡的IE)
當然配合CSS的box-sizing屬性設(shè)為border-box可以達到一樣的效果 ? (自認為,未驗證)。
/** * 獲取實際樣式函數(shù) * @param {HTMLElement} element 需要尋找的樣式的html節(jié)點 * @param {String]} attr 在對象中尋找的樣式屬性 * @returns {String} 獲取到的屬性 */ function getStyle(element, attr) { //IE寫法 if (element.currentStyle) { return element.currentStyle[attr]; //標準 } else { return getComputedStyle(element, false)[attr]; } }第二步:改造原函數(shù)
添加參數(shù),attr表示需要改變的屬性值。
更改element.offsetLeft為getStyle(element, attr)。
需要注意的是:getStyle(element, attr)不能直接使用,因為它獲取到的字符串,例:10px。
變量iCurrent使用parseInt(),將樣式轉(zhuǎn)成數(shù)字。
element.style.left為element.style[attr]。
/** * 運動框架-4-任意值變化 * @param {HTMLElement} element 運動對象 * @param {string} attr 需要改變的屬性。 * @param {number} iTarget 目標值 */ function startMove(element, attr, iTarget) { clearInterval(element.timer); element.timer = setInterval(function () { //因為速度要動態(tài)改變,所以必須放在定時器中 var iCurrent=0; iCurrent = parseInt(getStyle(element, attr));//實際樣式大小 var iSpeed = (iTarget - iCurrent) / 10; //(目標值-當前值)/縮放系數(shù)=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 if (iCurrent === iTarget) {//結(jié)束運動 clearInterval(element.timer); } else { element.style[attr] = iCurrent + iSpeed + "px"; } }, 30); }
試一試,這樣是不是就可以了呢?
還記得上面我們寫的透明度變化嗎? 再試試
果然還是不行, (廢話,你見過透明度有"px"單位的么? - -白眼 )
第三步:透明度兼容處理思考:需要對那些屬性進行修改?
判斷attr是不是透明度屬性opacity 。
對于速度進行處理。
為透明度時,由于獲取到的透明度會是小數(shù),所以需要 * 100
并且由于計算機儲存浮點數(shù)的問題,還需要將小數(shù),進行四舍五入為整數(shù)。使用: Math.round(parseFloat(getStyle(element, attr)) * 100)。
否則,繼續(xù)使用默認的速度。
對結(jié)果輸出部分進行更改。
判斷是透明度屬性,使用透明度方法
否則,使用使用默認的輸出格式。
/** * 運動框架-5-兼容透明度 * @param {HTMLElement} element 運動對象 * @param {string} attr 需要改變的屬性。 * @param {number} iTarget 目標值 */ function startMove(element, attr, iTarget) { clearInterval(element.timer); element.timer = setInterval(function () { //因為速度要動態(tài)改變,所以必須放在定時器中 var iCurrent = 0; if (attr === "opacity") { //為透明度時執(zhí)行。 iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100); } else { //默認情況 iCurrent = parseInt(getStyle(element, attr)); //實際樣式大小 } var iSpeed = (iTarget - iCurrent) / 10; //(目標值-當前值)/縮放系數(shù)=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 if (iCurrent === iTarget) {//結(jié)束運動 clearInterval(element.timer); } else { if (attr === "opacity") { //為透明度時,執(zhí)行 element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE element.style.opacity = (iCurrent + iSpeed) / 100; //標準 } else { //默認 element.style[attr] = iCurrent + iSpeed + "px"; } } }, 30); }
到這里,這個運動框架就基本上完成了。但是,我們是追求完美的不是嗎?
繼續(xù)進化!
(五)鏈式動畫鏈式動畫:顧名思義,就是在該次運動停止時,開始下一次運動。
如何實現(xiàn)呢?
使用回調(diào)函數(shù):運動停止時,執(zhí)行函數(shù)
添加func形參(回調(diào)函數(shù))。
在當前屬性到達目的地時iCurrent === iTarget,判斷是否有回調(diào)函數(shù)存在,有則執(zhí)行。
if (iCurrent === iTarget) {//結(jié)束運動 clearInterval(element.timer); if (func) { func();//回調(diào)函數(shù) } }
good,鏈式動畫完成!距離完美還差一步!
(六)同時運動思考:如何實現(xiàn)同時運動?
使用JSON傳遞多個值
使用for in循環(huán),遍歷屬性,與值。
定時器問題!(運動提前停止)
在循環(huán)外設(shè)置變量,假設(shè)所有的值都到達了目的值為true
在循環(huán)中檢測是否到達目標值,若沒有值未到則為false
在循環(huán)結(jié)束后,檢測是否全部達到目標值.是則清除定時器
實現(xiàn):
刪除attr與iTarget兩個形參,改為json
在函數(shù)開始時,設(shè)置一個標記var flag = true; //假設(shè)所有運動到達終點.
在定時器內(nèi)使用for in,遍歷屬性與目標,改寫原來的attr與iTarget,為json的屬性與值
修改運動終止條件,只有每一項的實際屬性值iCurrent,等于目標值json[attr]時,flag才為true。清除定時器,判斷是否回調(diào)。
否則,繼續(xù)執(zhí)行代碼,直到所有屬性值等于目標值。
完美運動框架/** * 獲取實際樣式函數(shù) * @param {HTMLElement} element 需要尋找的樣式的html節(jié)點 * @param {String]} attr 在對象中尋找的樣式屬性 * @returns {String} 獲取到的屬性 */ function getStyle(element, attr) { //IE寫法 if (element.currentStyle) { return element.currentStyle[attr]; //標準 } else { return getComputedStyle(element, false)[attr]; } } /** * 完美運動框架 * @param {HTMLElement} element 運動對象 * @param {JSON} json 屬性:目標值 * @property {String} attr 屬性值 * @config {Number} target 目標值 * @param {function} func 可選,回調(diào)函數(shù),鏈式動畫。 */ function startMove(element, json, func) { var flag = true; //假設(shè)所有運動到達終點. clearInterval(element.timer); element.timer = setInterval(function () { for (var attr in json) { //1.取當前的屬性值。 var iCurrent = 0; if (attr === "opacity") { //為透明度時執(zhí)行。 iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100); } else { //默認情況 iCurrent = parseInt(getStyle(element, attr)); //實際樣式大小 } //2.算運動速度,動畫緩沖效果 var iSpeed = (json[attr] - iCurrent) / 10; //(目標值-當前值)/縮放系數(shù)=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 //3.未到達目標值時,執(zhí)行代碼 if (iCurrent != json[attr]) { flag = false; //終止條件 if (attr === "opacity") { //為透明度時,執(zhí)行 element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE element.style.opacity = (iCurrent + iSpeed) / 100; //標準 } else { //默認 element.style[attr] = iCurrent + iSpeed + "px"; } } else { flag = true; } //4. 運動終止,是否回調(diào) if (flag) { clearInterval(element.timer); if (func) { func(); } } } }, 30); }運動框架總結(jié)
運動框架演變過程
框架 | 變化 |
---|---|
startMove(element) | 運動 |
startMove(element,iTarget) | 勻速-->緩沖-->多物體 |
startMove(element,attr,iTargrt) | 任意值 |
startMove(element,attr,iTargrt,func) | 鏈式運動 |
startMove(element,json,func) | 多值(同時)-->完美運動框架 |
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86158.html
摘要:在這個完美運動框架中,我們可以只讓一個物體的一個屬性運動,可以鏈式調(diào)用,也可以幾個屬性同時運動。能解決我們項目中遇到的大部分運動。運動框架演變過程運動實現(xiàn)留言板的例子完美運動運動,高度展開發(fā)布 前面的運動,每次只能改一個值,你改div的width的時候,就不能改div的高度,改高度的時候就不能改寬度,如果我的運動想同時改寬度和高度,怎么實現(xiàn)?在這里我們把屬性和目標值用json實現(xiàn)。在這...
摘要:學習之道簡體中文版通往實戰(zhàn)大師之旅掌握最簡單,且最實用的教程。前言學習之道這本書使用路線圖中的精華部分用于傳授,并將其融入一個獨具吸引力的真實世界的具體代碼實現(xiàn)。完美展現(xiàn)了的優(yōu)雅。膜拜的學習之道是必讀的一本書。 《React 學習之道》The Road to learn React (簡體中文版) 通往 React 實戰(zhàn)大師之旅:掌握 React 最簡單,且最實用的教程。 showIm...
摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅(qū)動,事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進階、CSS進階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對...
摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅(qū)動,事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進階、CSS進階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對...
閱讀 3717·2021-10-11 10:59
閱讀 1301·2019-08-30 15:44
閱讀 3479·2019-08-29 16:39
閱讀 2888·2019-08-29 16:29
閱讀 1800·2019-08-29 15:24
閱讀 808·2019-08-29 15:05
閱讀 1264·2019-08-29 12:34
閱讀 2302·2019-08-29 12:19