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