摘要:之前是一個(gè)全局變量,如果不獨(dú)立,頁(yè)面只有一個(gè)定時(shí)器在運(yùn)作。這時(shí)的判斷條件應(yīng)該是目標(biāo)距離與盒子目前距離之間差的絕對(duì)值大于等于一步距離絕對(duì)值的時(shí)候,讓他們執(zhí)行否則的話清除清除定時(shí)器,并將最后的距離直接設(shè)置為的距離。
JS特效 前言
經(jīng)過(guò)前面幾篇文章的講解,相信大家已經(jīng)會(huì)操作DOM和BOM了。為什么前面要花那么多精力去講DOM呢?因?yàn)樵诤竺娴膶W(xué)習(xí)、工作中,會(huì)大量的使用DOM操作,一個(gè)表格需要增、刪、改、查,一個(gè)圖片需要改變大小..等,如果你想要?jiǎng)討B(tài)的改變這些,必須要學(xué)會(huì)使用DOM。
為了鞏固前面的知識(shí)點(diǎn),并且能夠熟練地使用它們,這里多帶帶寫(xiě)了一篇《JavaScript 進(jìn)階知識(shí) - 特效篇》。本篇文章作為進(jìn)階篇很重要,不單單是對(duì)前面知識(shí)點(diǎn)的運(yùn)用,期間也會(huì)有大量的新知識(shí)點(diǎn)注入,所以希望小伙伴們繼續(xù)加油,認(rèn)真閱讀。
在本篇文章中主要會(huì)講解一些案例,比如我們平時(shí)在頁(yè)面中碰到的一些特效,一些動(dòng)畫(huà)效果。
注意: 所有的案例都在這里鏈接: 提取密碼密碼: 70ny,文章中的每個(gè)案例后面都有對(duì)應(yīng)的序號(hào)。
1. offset 系列offset系列用于用于獲取元素自身的大小和位置,在網(wǎng)頁(yè)特效中有廣泛應(yīng)用。offset系列主要有:offsetHeight、offsetWidth、offsetParent、offsetLeft、offsetTop。1.1 offsetWidth 和 offsetHeight
offsetWidth 和 offsetHeight獲取的是元素的真實(shí)寬高
獲取的是元素真實(shí)的高度和寬度
獲取到的是數(shù)值類型,方便計(jì)算
offsetHeight與offsetWidth是只讀屬性,不能設(shè)置。
示例代碼:獲取一個(gè)盒子的真實(shí)寬高 [01-offset系列-offsetWidth&Height.html]
offsetWidth是一個(gè)通過(guò)計(jì)算后得到的值, padding + border + width
思考: 之前我們不是也可以通過(guò)style來(lái)獲取樣式嗎?他們有什么不同
style.height與style.width只能獲取到行內(nèi)樣式里的width和height
獲取的是字符串類型,還需要轉(zhuǎn)換成數(shù)值類型
寫(xiě)在css樣式里的寬高是獲取不到的,只能獲取行內(nèi)樣式
總結(jié):
設(shè)置寬度高度使用style.width與style.height
獲取寬度和高度offsetWidth與offsetHeight
offset獲取的寬高包括padding、border
1.2 offsetParentparentNode和offsetParent
parentNode始終是父元素
offsetParent是離當(dāng)前元素最近的定位元素(absolute、relative),如果沒(méi)有,那就找body
示例代碼: [02-offset系列-offsetParent.html]
1.3 offsetLeft與offsetTop
offsetLeft: 自身左側(cè)到offsetParent左側(cè)的距離:left + marginoffsetTop: 自身頂部到offsetParent頂部的距離 : top + margin
元素自身與offsetParent真實(shí)的距離
獲取到的是數(shù)值類型,方便計(jì)算
只讀屬性,只能獲取,不能設(shè)置
示例代碼:獲取一個(gè)盒子距父盒子的距離 [03-offset系列-offsetTop&Left.html]
思考: 之前我們不是也可以通過(guò)style來(lái)獲取樣式嗎?他們有什么不同
style.top與style.left只能獲取到行內(nèi)樣式里的top和left
獲取的是字符串類型,還需要轉(zhuǎn)換成數(shù)值類型
寫(xiě)在css樣式里的寬高是獲取不到的,只能獲取行內(nèi)樣式
總結(jié):
設(shè)置定位left/top使用style.left與style.top
獲取定位left/top使用offsetLeft與offsetTop
offset獲取的位置包括margin
如果父元素沒(méi)有定位,獲取的就是相對(duì)body的
一張圖看清offset系列
2. 勻速動(dòng)畫(huà)框架 2.1 勻速動(dòng)畫(huà)初體驗(yàn)如何讓一個(gè)物體動(dòng)起來(lái)?動(dòng)畫(huà)函數(shù)的實(shí)現(xiàn)原理其實(shí)就是利用間歇定時(shí)器每隔一段時(shí)間執(zhí)行一次的原理實(shí)現(xiàn)的。
1、讓一個(gè)物體動(dòng)起來(lái)
點(diǎn)擊按鈕讓一個(gè)盒子勻速往右執(zhí)行一段距離:[04-勻速動(dòng)畫(huà)初體驗(yàn)(一).html]
效果圖:
BUG: 不知道細(xì)心的小伙伴有沒(méi)有發(fā)現(xiàn)兩個(gè)問(wèn)題
現(xiàn)在執(zhí)行的時(shí)候是不會(huì)停下來(lái)的,一直往右跑
點(diǎn)擊按鈕之后再去點(diǎn)擊,會(huì)發(fā)現(xiàn),按鈕點(diǎn)擊次數(shù)越多,盒子速度越快
2、讓一個(gè)物體動(dòng)起來(lái),解決bug
我們讓盒子運(yùn)動(dòng)到500px的位置停下來(lái) [05-勻速動(dòng)畫(huà)初體驗(yàn)(二).html]
var btn = document.getElementById("btn"); var box = document.getElementById("box"); var timer = null; /** 為什么會(huì)越點(diǎn)越快? 點(diǎn)擊一次就會(huì)調(diào)用一次定時(shí)器,點(diǎn)擊的次數(shù)越多,調(diào)用的就越多 距離疊加的就會(huì)越來(lái)越大 視覺(jué)效果上看起來(lái)就跑的越來(lái)越快 只要在每次點(diǎn)擊后,定時(shí)器執(zhí)行前清除上一次定時(shí)器,就不會(huì)出現(xiàn)越點(diǎn)越快的效果了 */ btn.onclick = function() { // 一進(jìn)來(lái)就清除定時(shí)器 clearInterval(timer); timer = setInterval(function() { // 定義一個(gè)距離 相當(dāng)于每一次要跑的距離 step var step = 5; // 定義一個(gè)當(dāng)前位置 leader var leader = box.offsetLeft; /** 當(dāng)移動(dòng)的位置在500px內(nèi)的時(shí)候,執(zhí)行動(dòng)畫(huà)函數(shù) 否則就清除定時(shí)器,讓盒子停下來(lái) */ if (leader < 500) { // 每次執(zhí)行的時(shí)候 讓leader都走step距離 leader = leader + step; // 將距離賦值給box box.style.left = leader + "px"; } else { clearInterval(timer); } }, 15); }
效果圖:
總結(jié):
setInterval間歇定時(shí)器,如果不手動(dòng)清除,它就會(huì)一直運(yùn)行下去
點(diǎn)擊事件觸發(fā)定時(shí)器一定要注意,一進(jìn)來(lái)就清除一次,否則會(huì)越點(diǎn)越快
2.2 勻速動(dòng)畫(huà)函數(shù)封裝函數(shù)需要獨(dú)立,就不能使用全局變量。timer之前是一個(gè)全局變量,如果不獨(dú)立,頁(yè)面只有一個(gè)定時(shí)器在運(yùn)作。封裝的函數(shù)里將timer綁定給調(diào)用定時(shí)器的元素,這樣就獨(dú)立了。
1、封裝一個(gè)動(dòng)畫(huà)函數(shù) [06-封裝一個(gè)勻速動(dòng)畫(huà)函數(shù).html]
注意: 上面的案例我們只是簡(jiǎn)單的實(shí)現(xiàn)了一個(gè)動(dòng)畫(huà)的封裝效果,但是作為一個(gè)以后會(huì)經(jīng)常用的函數(shù),上面的代碼還有很多需要優(yōu)化的地方
1、上面的函數(shù)只能往正方向跑,也就是說(shuō)去到1000,想讓它回到500是不好實(shí)現(xiàn)的;
2、如果每次走的距離是5,目標(biāo)距離是500,正好能整除。假如每次走的是9呢?每次走9,是不能被500整除的,所以最后停下里的距離會(huì)偏多一點(diǎn)。
2、封裝一個(gè)動(dòng)畫(huà)函數(shù)完整版 [07-封裝一個(gè)勻速動(dòng)畫(huà)函數(shù)完整版.html]
先說(shuō)說(shuō)第二個(gè)問(wèn)題,距離的問(wèn)題。如果走的距離不能被目標(biāo)距離整除的話,最后會(huì)多出來(lái)一點(diǎn)距離,我們可以不用管這個(gè)距離,直接在清除定時(shí)器,停下里的時(shí)候讓它的距離等于目標(biāo)距離。
clearInterval(element.timer); // 清除前位置在504,直接在下面設(shè)置讓它位置等于500 element.style.left = target + "px"; // 500
現(xiàn)在說(shuō)說(shuō)第一個(gè)問(wèn)題,盒子到1000的時(shí)候不能回到500。假設(shè)現(xiàn)在盒子在1000,我們點(diǎn)擊按鈕1的時(shí)候想要讓他回到500,這個(gè)時(shí)候我們可以發(fā)現(xiàn)時(shí)的leader = 1000,目標(biāo)距離target為500,就是說(shuō)當(dāng)leader>target的時(shí)候,盒子是可以往回走的,這時(shí)候只要將步數(shù)設(shè)置為負(fù)數(shù) ,盒子就是往回跑的。
var leader = element.offsetLeft; // 當(dāng)目標(biāo)距離大于當(dāng)前位置 說(shuō)明往正方向走 step的值就是正的 var step = target > leader? 9 : -9;
此時(shí)就不能再根據(jù) if (leader < target){}, else { clearInterval(element.timer); }去判斷,讓盒子運(yùn)動(dòng)了。這時(shí)的判斷條件應(yīng)該是目標(biāo)距離target 與盒子目前距離leader之間差的絕對(duì)值大于等于一步距離step絕對(duì)值的時(shí)候,讓他們執(zhí)行leader = leader + step;否則的話清除清除定時(shí)器,并將最后的距離直接設(shè)置為target的距離。
var distance = Math.abs(target - leader); // 通過(guò)判斷此時(shí)的差如果大于或者等于一步的距離step的時(shí)候,就應(yīng)該執(zhí)行動(dòng)畫(huà) if (distance >= Math.abs(step)) { leader = leader + step; element.style.left = leader + "px"; }
完整代碼:
效果圖:
如上,這就是封裝的一個(gè)完美的動(dòng)畫(huà)函數(shù)了,下次有需要用到動(dòng)畫(huà)的地方,直接引用即可——[ js/animate.js ]
3. 輪播圖基本上每個(gè)網(wǎng)站都會(huì)用到輪播圖,輪播圖的使用可以說(shuō)是必不可少的。以后我們用的最多的可能是插件,原生的可能并不常用,但是輪播圖的原理我們必須知道,并且能夠?qū)懗鰜?lái)。(之前一次面試就是讓我講出輪播圖的具體實(shí)現(xiàn)步驟)3.1 簡(jiǎn)單輪播圖
現(xiàn)在我們先來(lái)學(xué)習(xí)下簡(jiǎn)單的輪播圖實(shí)現(xiàn)原理。
輪播圖樣式的特點(diǎn):
ul要足夠的寬,要求能夠一行放下所有的li
父盒子的寬高和圖片的寬高一樣
父盒子要有一個(gè)overflow:hidden ,僅顯示一張圖片,不多不少
要求ul很寬很寬,因?yàn)樗械?b>li要左浮動(dòng),要保證所有的li在一行上顯示,定義一個(gè)盒子,盒子的寬高要和顯示的單張圖片寬高一樣,然后設(shè)置overflow:hidden這樣其他的li就會(huì)被隱藏在下面,通過(guò)改變ul的位置就能實(shí)現(xiàn)圖片的切換了
示例代碼: [08-實(shí)現(xiàn)簡(jiǎn)單的輪播圖.html]
效果圖:
從上面效果圖中,我們可以看到,一個(gè)最簡(jiǎn)單的輪播圖已經(jīng)成型了,但是需要去用手點(diǎn)擊,而且如果跨點(diǎn)數(shù)去點(diǎn)擊,會(huì)發(fā)現(xiàn)圖片要一張張滑過(guò)去,這里后面我們會(huì)優(yōu)化。
3.2 左右焦點(diǎn)輪播圖左右焦點(diǎn)輪播圖,就是在顯示圖片的兩端添加兩個(gè)按鈕,一個(gè)向左,一個(gè)向右,點(diǎn)擊的時(shí)候圖片會(huì)根據(jù)點(diǎn)擊的方向滑動(dòng)。并且當(dāng)鼠標(biāo)懸停在顯示區(qū)域的時(shí)候,兩個(gè)按鈕顯示。鼠標(biāo)離開(kāi)顯示區(qū)域,,兩個(gè)按鈕隱藏。
示例代碼: [09-左右焦點(diǎn)輪播圖.html]
效果圖:
3.3 無(wú)縫輪播圖上圖可以看到,當(dāng)滑到最左邊或者最右邊的時(shí)候,再點(diǎn)擊就沒(méi)有用了,正常的輪播圖肯定不是這樣的,點(diǎn)擊到最后一張后再點(diǎn)擊肯定是接著滑動(dòng)的。下面我們接著看,如何實(shí)現(xiàn)一個(gè)無(wú)縫輪播圖
示例代碼:無(wú)縫輪播(可以一直點(diǎn)擊) [10-左右焦點(diǎn)輪播圖-無(wú)縫滾動(dòng).html]
何謂無(wú)縫滾動(dòng)?
無(wú)縫滾動(dòng)就是圖片能夠循環(huán)切換,就算是最后一張,點(diǎn)擊之后也會(huì)跳到第一張
原理:
效果就像上面所說(shuō)的一樣,主要實(shí)現(xiàn)原理就是,在最后面一張圖片,再加上一張圖片,這張圖片就是第一張圖片
當(dāng)滑動(dòng)到最后一張圖片的時(shí)候(看下圖),此時(shí)的視覺(jué)效果就是停在第一張圖片上
這時(shí)只需要在程序上判斷,當(dāng)在最后一張的時(shí)候,直接跳到第一張圖片即可
示例代碼:無(wú)縫滾動(dòng)的簡(jiǎn)單原理 [10-無(wú)縫滾動(dòng)原理.html]
效果圖:
左右焦點(diǎn)無(wú)縫輪播圖: [11-左右焦點(diǎn)無(wú)縫輪播圖.html]
效果圖:
3.4 完整版輪播圖前面我們已經(jīng)可以通過(guò)點(diǎn)擊對(duì)應(yīng)的小點(diǎn)、左右焦點(diǎn)和無(wú)縫滾動(dòng)來(lái)實(shí)現(xiàn)輪播圖了,不過(guò)都是多帶帶分開(kāi)來(lái)的,現(xiàn)在我們做個(gè)整合,實(shí)現(xiàn)一個(gè)完整的輪播圖。
功能概述:
簡(jiǎn)單輪播功能
給circle下的所有的li注冊(cè)點(diǎn)擊事件
排他
移動(dòng)Ul
左右焦點(diǎn)功能
需要定義一個(gè)變量count來(lái)記錄移動(dòng)的圖片的張數(shù)。
點(diǎn)擊右箭頭功能
如果當(dāng)前圖片是最后一張(假圖片),需要瞬間變成真圖片
點(diǎn)擊一次,需要讓圖片往右移動(dòng)一張
同步小圓點(diǎn),干掉所有小圓點(diǎn),復(fù)活對(duì)應(yīng)count的小圓點(diǎn)。
最后一張假圖片對(duì)應(yīng)的小圓點(diǎn)是第一個(gè),需要做特殊處理
點(diǎn)擊左箭頭的功能和右箭頭基本一致。
自動(dòng)輪播的功能
開(kāi)啟定時(shí)器,每隔兩秒點(diǎn)擊一次右箭頭
鼠標(biāo)經(jīng)過(guò)盒子,停止定時(shí)器(箭頭亂閃的問(wèn)題解釋)觸發(fā)事件的一定要是外面的大盒子,不能是ul,如果給ul注冊(cè)事件,就會(huì)出現(xiàn)亂閃的問(wèn)題
鼠標(biāo)離開(kāi)盒子,開(kāi)啟定時(shí)器
同步功能
點(diǎn)擊小圓點(diǎn)時(shí)需要同步
淘寶bug解決方法(當(dāng)一圈過(guò)后回到第一個(gè)小圓點(diǎn)的時(shí)候,再點(diǎn)擊它會(huì)發(fā)現(xiàn)他會(huì)再跑一圈)
淘寶bug圖:
完整代碼: [12-完整版輪播圖.html]
效果圖:
完美了嗎?并沒(méi)有,這里有個(gè)小bug:
可能會(huì)有小伙伴不理解,有問(wèn)題你上面直接講一下不就得了,還特地賣關(guān)子在下面重新寫(xiě)一遍。我想跟大家說(shuō)的一點(diǎn)就是,如果在上面我直接告訴你這里有個(gè)問(wèn)題有個(gè)bug的話,你一眼看過(guò),可能都不當(dāng)回事,我在這里拿出來(lái)講一下,相信這個(gè)知識(shí)點(diǎn)你會(huì)記得更深。
小bug:明明設(shè)置的是600,怎么會(huì)是596.4px呢?
原因:
offsetLeft獲取值的時(shí)候,只會(huì)獲取整數(shù),會(huì)對(duì)小數(shù)部分會(huì)四舍五入處理,比如step = (target - leader)/10當(dāng)step的值出現(xiàn)小數(shù)的時(shí)候,leader+= step之后,offsetLeft在獲取leader位置的時(shí)候就會(huì)把小數(shù)部分四舍五入,這樣就會(huì)造成最后距離的誤差。
解決方法:
對(duì)step向上取整處理(Math.ceil()),保證每一次都至少跑1px的距離,只要不出現(xiàn)小數(shù)offsetLeft就不會(huì)出現(xiàn)四舍五入。
完整代碼: [14-緩動(dòng)動(dòng)畫(huà)初體驗(yàn)(二).html]
var box = document.getElementById("box"); var btn = document.getElementById("btn"); var timer = null; btn.onclick = function() { clearInterval(timer); timer = setInterval(function() { // 定義一個(gè)目標(biāo)距離 var target = 600; // 獲得當(dāng)前盒子的位置 var leader = box.offsetLeft; // 每次運(yùn)動(dòng)的距離 var step = (target - leader) / 10; // 對(duì)step進(jìn)行向上取整 step = Math.ceil(step); // leader = leader + step 動(dòng)起來(lái) leader += step; // 將距離給盒子 box.style.left = leader + "px"; // 當(dāng)當(dāng)前距離等于目標(biāo)距離的時(shí)候清除定時(shí)器 if (leader == target) { clearInterval(timer); } }, 15); }4.2 緩動(dòng)動(dòng)畫(huà)函數(shù)封裝
前面勻速動(dòng)畫(huà)那里已經(jīng)講過(guò)封裝一個(gè)函數(shù)的好處與重要性,現(xiàn)在我們將緩動(dòng)動(dòng)畫(huà)也封裝成一個(gè)函數(shù)。
示例代碼: [15-緩動(dòng)動(dòng)畫(huà)函數(shù)封裝.html]
效果圖:
又到了找bug的時(shí)候了:
上面的代碼從0-500,從500-1000都沒(méi)有問(wèn)題,經(jīng)過(guò)向上取整后都能到達(dá)目標(biāo)距離:500和1000。但是小伙伴可以看下,當(dāng)從1000回到500的時(shí)候,是正好回到500的嗎?答案肯定不是的,為什么呢?
step為正數(shù)的時(shí)候,向上取整是完全沒(méi)有問(wèn)題的,但是當(dāng)從1000到500的時(shí)候,step就是負(fù)數(shù)了,負(fù)數(shù)向上取整后就會(huì)變得更大,比如原本是-33.3,向上取整后就是-33了,-0.3就會(huì)舍去,所有就不會(huì)到500的位置。
解決方法: 判斷step的正負(fù),為正的時(shí)候,向上取整。為負(fù)的時(shí)候,向下取整。
緩動(dòng)函數(shù)封裝完整版: [16-緩動(dòng)動(dòng)畫(huà)函數(shù)封裝完整版.html]
function slowAnimate(element, target, num) { // 一進(jìn)來(lái)就要清除定時(shí)器,防止越點(diǎn)越快 clearInterval(element.timer); element.timer = setInterval(function() { // 獲得元素當(dāng)前位置 var leader = element.offsetLeft; // 定義每次運(yùn)動(dòng)的距離 var step = (target - leader) / num; //如果step是正數(shù),對(duì)step向上取整, //如果step是負(fù)數(shù),對(duì)step向下取整 // 保證每一次最少都走1px step = step > 0 ? Math.ceil(step) : Math.floor(step); leader += step; // 設(shè)置元素的位置 element.style.left = leader + "px"; // 當(dāng)元素的位置 等于 目標(biāo)位置的時(shí)候 清除定時(shí)器 if (leader == target) { clearInterval(element.timer); } }, 15); };4.3 獲取元素計(jì)算后的樣式
獲取元素計(jì)算后的樣式指的是元素經(jīng)過(guò)層疊后真正生效的樣式,不管樣式寫(xiě)在哪,計(jì)算后的樣式指的就是最終的樣式。
通過(guò)style只能獲取到寫(xiě)在行內(nèi)的樣式,那么想要獲取其他的樣式怎么辦呢?
js提供了一個(gè)方法:window.getComputedStyle(element, null)[attr];,它返回的是一個(gè)對(duì)象CSSStyleDeclaration,[attr]就是這個(gè)對(duì)象里面就是計(jì)算后的所有的樣式的屬性名(關(guān)聯(lián)數(shù)組取對(duì)象的值)。element指的是當(dāng)前參數(shù),null
這里可以不用深究-官方解釋。這個(gè)方法需要window調(diào)用。
/** element :獲取樣式的當(dāng)前元素 null :這里可以傳一個(gè)偽元素,如果不是偽元素的話必須是null attr :后面可以寫(xiě)具體的屬性,比如boderRadius 就會(huì)獲取這個(gè)元素的border-radius樣式信息 */ window.getComputedStyle(element,null)[attr];
示例代碼: [17-獲取元素計(jì)算后的樣式.html]
效果圖:
兼容性處理:
window.getComputedStyle(element, null)[attr];只適用于現(xiàn)代瀏覽器中
IE678有自己的方法:element.currentStyle[attr];
// 獲取元素計(jì)算后的樣式 function getStyle(element,attr){ if(window.getComputedStyle){ return window.getComputedStyle(element, null)[attr]; }else{ return element.currentStyle[attr]; } } // 注意:調(diào)用函數(shù)的時(shí)候 獲取的屬性名是一個(gè)字符串 alert(getStyle(box, "width"));
[18-獲取元素計(jì)算后的樣式兼容性處理.html]
注意: 上面的封裝函數(shù)中,調(diào)用的時(shí)候,屬性名是一個(gè)字符串類型。
4.4 緩動(dòng)動(dòng)畫(huà)修改多個(gè)樣式不管是上面的勻速動(dòng)畫(huà)函數(shù),還是這里的緩動(dòng)動(dòng)畫(huà)函數(shù),都只能左右運(yùn)動(dòng),但是一個(gè)真正完整的動(dòng)畫(huà)函數(shù),只改變左右位置肯定是不夠的,我們可能需要改變它的寬高等。在上面一節(jié)中,我們知道了如何獲取到元素計(jì)算后的樣式,而且只要是元素有的樣式都能獲取到,有了這個(gè)方法我們就可以讓動(dòng)畫(huà)去執(zhí)行更多的事情了。
1、對(duì)獲取到的樣式返回值進(jìn)行處理:
在上面的一節(jié)中,我們可以看到,獲取的返回值都是字符串格式,比如獲取寬度的時(shí)候,返回的是一個(gè)"300px"的字符串,因?yàn)榫弰?dòng)動(dòng)畫(huà)函數(shù)里面是需要計(jì)算的,這里是個(gè)字符串肯定不行,所以我們需要對(duì)其進(jìn)行parseInt取整處理。
[19-緩動(dòng)動(dòng)畫(huà)修改多個(gè)樣式-處理返回值.html]:
function getStyle(element, attr) { if (window.getComputedStyle) { return window.getComputedStyle(element, null)[attr]; } else { return element.currentStyle[attr]; } } function animate(element, attr, target) { clearInterval(element.timer); element.timer = setInterval(function() { // getStyle 返回的是樣式屬性的值 我們用一個(gè)變量將它儲(chǔ)存起來(lái) var leader = getStyle(element, attr); // 因?yàn)榉祷刂凳且粋€(gè)字符串,并且?guī)в凶址鹥x,所以我們對(duì)返回值進(jìn)行取整轉(zhuǎn)換 leader = parseInt(leader) || 0; // 這里或 0的目的就是,當(dāng)parseInt取整失敗的話,給一個(gè)默認(rèn)值0 var step = (target - leader) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); leader += step; // 設(shè)置指定樣式 element.style[attr] = leader + "px"; if (leader == target) { clearInterval(element.timer); } }, 15); } animate(box, "left", 800);
上面的代碼我們對(duì)它的返回值進(jìn)行了處理,而且還可以對(duì)它設(shè)置其他的樣式,只要單位是px的屬性都可以設(shè)置。但是這里每次還是只能設(shè)置一個(gè)樣式,下面我們來(lái)實(shí)現(xiàn)修改多個(gè)樣式。
注意: leader = parseInt(leader) || 0; "或"上0的目的就是:當(dāng)有些屬性設(shè)置的值不是數(shù)字的時(shí)候,比如:auto,這時(shí)候parseInt轉(zhuǎn)換的結(jié)果是NaN。當(dāng)"或"上0之后,轉(zhuǎn)換失敗后,leader,就會(huì)默認(rèn)是0。
2、遍歷一個(gè)對(duì)象:
讓我們來(lái)復(fù)習(xí)一下,js基礎(chǔ)的時(shí)候,我們接觸到了對(duì)象,并且知道了可以用for..in的方法來(lái)遍歷對(duì)象。我們知道getComputedStyle方法,獲取計(jì)算后樣式的時(shí)候,返回的是一個(gè)名叫CSSStyleDeclaration的對(duì)象,這個(gè)對(duì)象里面是所有的樣式屬性,我們想要對(duì)這些屬性進(jìn)行多個(gè)操作的時(shí)候,就可以通過(guò)遍歷的方法。
for(k in obj){ // k :就是相當(dāng)于對(duì)象的鍵 // obj :就是需要遍歷的對(duì)象 }
3、同時(shí)修改多個(gè)樣式:
同時(shí)修改多個(gè)樣式,就是將要修改的多個(gè)屬性以對(duì)象的形式作為參數(shù)傳進(jìn)函數(shù)中。
[20-緩動(dòng)動(dòng)畫(huà)修改多個(gè)樣式.html]
var box = document.getElementById("box"); var btn = document.getElementById("btn"); // 封裝一個(gè)函數(shù),element 表示執(zhí)行動(dòng)畫(huà)的元素 obj傳的是一個(gè)對(duì)象,里面可以設(shè)置多個(gè)屬性和值 function animate(element, obj) { clearInterval(element.timer); element.timer = setInterval(function() { // 遍歷外部傳進(jìn)來(lái)的對(duì)象 for (k in obj) { //attr : 要做動(dòng)畫(huà)的樣式 //target : 目標(biāo)值 var attr = k; var target = obj[k]; // 獲取元素開(kāi)始時(shí)計(jì)算后的樣式 var leader = getStyle(element, attr); leader = parseInt(leader) || 0; // 緩動(dòng)動(dòng)畫(huà)函數(shù)原理 var step = (target - leader) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); leader += step; // 給元素設(shè)置以樣式屬性名為attr的值 // 這個(gè)封裝的動(dòng)畫(huà)函數(shù)只能改值是px單位的樣式 element.style[attr] = leader + "px"; if (leader == target) { clearInterval(element.timer); } } }, 15); } // 處理兼容性 function getStyle(element, attr) { if (window.getComputedStyle) { return window.getComputedStyle(element, null)[attr]; } else { return element.currentStyle[attr]; } } // 調(diào)用函數(shù) 設(shè)置了五個(gè)樣式屬性 btn.onclick = function() { animate(box, { width: 200, height: 200, left: 300, top: 300, // bprder-radius 應(yīng)該轉(zhuǎn)為駝峰命名法 并且值只能是100px的格式 不能是百分比 borderRadius: 100 }); }
效果圖:
通過(guò)上面封裝的函數(shù)我們可以改變多個(gè)樣式,但是效果圖中我們可以看到一個(gè)問(wèn)題,就是當(dāng)?shù)竭_(dá)設(shè)定值后,點(diǎn)擊按鈕還會(huì)慢慢的抖動(dòng)。原因是修改多個(gè)樣式的時(shí)候,所有的樣式并不能都到同時(shí)達(dá)終點(diǎn)。
4.5 緩動(dòng)動(dòng)畫(huà)修復(fù)定時(shí)器bug出現(xiàn)這個(gè)bug的原因:在for循環(huán)中判斷是否到達(dá)目標(biāo)值,到達(dá)后就清除定時(shí)器,但是我們同時(shí)修改了5個(gè)樣式,可能有的樣式到達(dá)目標(biāo)值后就清楚定時(shí)器了,但是有的樣式還沒(méi)到達(dá)目標(biāo)值,所以就出現(xiàn)了上面的bug。
解決方法:假設(shè)成立法
假設(shè)成立
想辦法推翻假設(shè)
如果推翻不了,說(shuō)明假設(shè)成立
示例代碼: [21-緩動(dòng)動(dòng)畫(huà)修改多個(gè)樣式-修復(fù)定時(shí)器bug.html]
function animate(element, obj) { clearInterval(element.timer); element.timer = setInterval(function() { // 1-假設(shè)都到達(dá)了終點(diǎn) var flag = true; for (k in obj) { var attr = k; var target = obj[k]; var leader = getStyle(element, attr); leader = parseInt(leader) || 0; var step = (target - leader) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); leader += step; element.style[attr] = leader + "px"; // 2- 必須要等到所有的樣式都到達(dá)終點(diǎn)才清除定時(shí)器 // 只要有一個(gè)樣式?jīng)]有到達(dá)設(shè)定值,說(shuō)明假設(shè)失敗 if (leader != target) { flag = false; } } // 所有的樣式都到達(dá)終點(diǎn)后 清除定時(shí)器 if (flag) { clearInterval(element.timer); } }, 15); }4.6 緩動(dòng)動(dòng)畫(huà)兼容其它樣式屬性
經(jīng)過(guò)前面幾小節(jié)的學(xué)習(xí),我們已經(jīng)可以實(shí)現(xiàn)同時(shí)修改多個(gè)樣式的緩動(dòng)動(dòng)畫(huà)了。但是細(xì)心的小伙伴不知道有沒(méi)有發(fā)現(xiàn),目前只能設(shè)置跟px有關(guān)系的樣式,包括設(shè)置border-radiu也不算完善。這是因?yàn)槲覀兙弰?dòng)動(dòng)畫(huà)封裝的時(shí)后,設(shè)置的element.style[attr] = leader + "px";,所以只能實(shí)現(xiàn)跟px有關(guān)的樣式。
設(shè)置兼容其他屬性的時(shí)候,要注意兩點(diǎn),第一獲取的時(shí)候要進(jìn)行判斷,設(shè)置的時(shí)候也要進(jìn)行判斷
1、兼容opacity屬性: [22-緩動(dòng)動(dòng)畫(huà)修改多個(gè)樣式-兼容opacity.html]
function animate(element, obj) { clearInterval(element.timer); element.timer = setInterval(function() { var flag = true; for (k in obj) { var attr = k; var target = obj[k]; // 判斷獲得的屬性是不是“opacity”,是的話多帶帶處理 var leader; // 獲得當(dāng)前值 if (attr === "opacity") { // 獲取的時(shí)候是個(gè)小數(shù),將它乘以100 運(yùn)算時(shí)不會(huì)出現(xiàn)精度丟失 leader = getStyle(element, attr) * 100 || 1; } else { leader = getStyle(element, attr); leader = parseInt(leader) || 0; } var step = (target - leader) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); leader += step; // 賦值 // 判斷是不是opacity屬性 是的話 多帶帶賦值 if (attr === "opacity") { // 因?yàn)殚_(kāi)始的時(shí)候leader擴(kuò)大了100倍 設(shè)置的時(shí)候 opacity只能是0-1 element.style[attr] = leader / 100; // opacity 還需要多帶帶處理,因?yàn)镮E678 不支持opacity element.style.filter = "alpha(opacity=" + leader + ")"; } else { element.style[attr] = leader + "px"; } if (leader != target) { flag = false; } } if (flag) { clearInterval(element.timer); } }, 15); } // 處理獲取樣式兼容性 function getStyle(element, attr) { if (window.getComputedStyle) { return window.getComputedStyle(element, null)[attr]; } else { return element.currentStyle[attr]; } } // 調(diào)用這個(gè)函數(shù) btn.onclick = function() { animate(box, { width: 200, height: 200, left: 300, top: 300, // 這里是按照 0-100 設(shè)置不透明度的,因?yàn)樾?shù)計(jì)算的時(shí)候會(huì)出現(xiàn)精度丟失 opacity: 50 }); }
2、兼容zIndex屬性: [23-緩動(dòng)動(dòng)畫(huà)修改多個(gè)樣式-兼容zIndex.html]
zIndex這個(gè)屬性不需要緩動(dòng)的執(zhí)行改變層級(jí),直接獲得傳進(jìn)來(lái)的值設(shè)置即可
// 賦值 if (attr === "opacity") { element.style[attr] = leader / 100; element.style.filter = "alpha(opacity=" + leader + ")"; // 判斷設(shè)置的時(shí)候是否是zIndex屬性 } else if (attr === "zIndex") { element.style.zIndex= leader; } else { element.style[attr] = leader + "px"; }
示例代碼: [24-緩動(dòng)動(dòng)畫(huà)淡入淡出效果.html]
btn1.onclick = function() { animate(box, { opacity: 100 }) } btn2.onclick = function() { animate(box, { opacity: 0 }) }
效果圖:
4.7 緩動(dòng)動(dòng)畫(huà)添加回調(diào)函數(shù)程序執(zhí)行完畢,再次執(zhí)行的函數(shù)。
示例代碼: [25-緩動(dòng)動(dòng)畫(huà)添加回調(diào)函數(shù).html]
var box = document.getElementById("box"); var btn = document.getElementById("btn"); fun
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90482.html
摘要:個(gè)人前端文章整理從最開(kāi)始萌生寫(xiě)文章的想法,到著手開(kāi)始寫(xiě),再到現(xiàn)在已經(jīng)一年的時(shí)間了,由于工作比較忙,更新緩慢,后面還是會(huì)繼更新,現(xiàn)將已經(jīng)寫(xiě)好的文章整理一個(gè)目錄,方便更多的小伙伴去學(xué)習(xí)。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個(gè)人前端文章整理 從最開(kāi)始萌生寫(xiě)文章的想法,到著手...
摘要:面向?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整站開(kāi)發(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整站開(kāi)發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...
閱讀 722·2021-10-14 09:42
閱讀 1975·2021-09-22 15:04
閱讀 1585·2019-08-30 12:44
閱讀 2146·2019-08-29 13:29
閱讀 2737·2019-08-29 12:51
閱讀 556·2019-08-26 18:18
閱讀 707·2019-08-26 13:43
閱讀 2817·2019-08-26 13:38