javascript
JavaScript 进阶知识 - 特效篇(一)
JS特效
前言
經過前面幾篇文章的講解,相信大家已經會操作DOM和BOM了。為什么前面要花那么多精力去講DOM呢?因為在后面的學習、工作中,會大量的使用DOM操作,一個表格需要增、刪、改、查,一個圖片需要改變大小..等,如果你想要動態的改變這些,必須要學會使用DOM。
為了鞏固前面的知識點,并且能夠熟練地使用它們,這里單獨寫了一篇《JavaScript 進階知識 - 特效篇》。本篇文章作為進階篇很重要,不單單是對前面知識點的運用,期間也會有大量的新知識點注入,所以希望小伙伴們繼續加油,認真閱讀。
在本篇文章中主要會講解一些案例,比如我們平時在頁面中碰到的一些特效,一些動畫效果。
注意: 所有的案例都在這里鏈接: 提取密碼密碼: 70ny,文章中的每個案例后面都有對應的序號。
1. offset 系列
offset系列用于用于獲取元素自身的大小和位置,在網頁特效中有廣泛應用。offset系列主要有:offsetHeight、offsetWidth、offsetParent、offsetLeft、offsetTop。1.1 offsetWidth 和 offsetHeight
offsetWidth 和 offsetHeight獲取的是元素的真實寬高- 獲取的是元素真實的高度和寬度
- 獲取到的是數值類型,方便計算
- offsetHeight與offsetWidth是只讀屬性,不能設置。
示例代碼:獲取一個盒子的真實寬高 [01-offset系列-offsetWidth&Height.html]
<!-- 樣式部分 --> <style>div {width: 200px;height: 100px;background-color: pink;padding: 10px;border: 10px solid salmon;margin: 10px;} </style><!-- html 部分 --> <div id="box"></div><!-- js 部分 --> <script>var box = document.getElementById('box');// offsetWidth是一個通過計算后得到的值, padding + border + widthconsole.log(box.offsetWidth); // 240console.log(box.offsetHeight); // 140 </script>offsetWidth是一個通過計算后得到的值, padding + border + width
思考: 之前我們不是也可以通過style來獲取樣式嗎?他們有什么不同
style.height與style.width只能獲取到行內樣式里的width和height- 獲取的是字符串類型,還需要轉換成數值類型
- 寫在css樣式里的寬高是獲取不到的,只能獲取行內樣式
總結:
- 設置寬度高度使用style.width與style.height
- 獲取寬度和高度offsetWidth與offsetHeight
- offset獲取的寬高包括padding、border
1.2 offsetParent
parentNode和offsetParent- parentNode始終是父元素
- offsetParent是離當前元素最近的定位元素(absolute、relative),如果沒有,那就找body
示例代碼: [02-offset系列-offsetParent.html]
<!-- 樣式部分 --> <style>#father {width: 500px;height: 500px;background-color: #FF9F68;}#son {width: 300px;height: 300px;background-color: #FEFF89;}#grandson {width: 100px;height: 100px;background-color: #AC005D;position: absolute;left: 100px;right: 100px;} </style><!-- html 部分 --> <div id="father"><div id="son"><div id="grandson"></div></div> </div><!-- js 部分 --> <script>var grandSon = document.getElementById("grandson");// 找父節點 親爹console.log(grandSon.parentNode); // 返回<div id="son"></div>// 找最近有定位的爹,如果找不到,會找bodyconsole.log(grandSon.offsetParent); // 返回<body></body> </script>1.3 offsetLeft與offsetTop
offsetLeft: 自身左側到offsetParent左側的距離:left + marginoffsetTop: 自身頂部到offsetParent頂部的距離 : top + margin
- 元素自身與offsetParent真實的距離
- 獲取到的是數值類型,方便計算
- 只讀屬性,只能獲取,不能設置
示例代碼:獲取一個盒子距父盒子的距離 [03-offset系列-offsetTop&Left.html]
<!-- 樣式部分 --> <style>#father {width: 400px;height: 400px;background-color: pink;position: relative;margin-left: 100px;}#son {width: 200px;height: 200px;background-color: skyblue;position: absolute;left: 100px;margin: 20px;} </style><!-- html 部分 --> <div id="father"><div id="son"></div> </div><!-- js 部分 --> <script>//offsetLeft與offsetTopvar son = document.getElementById("son");console.log(son.offsetLeft); // 120console.log(son.offsetTop); // 20 </script>思考: 之前我們不是也可以通過style來獲取樣式嗎?他們有什么不同
style.top與style.left只能獲取到行內樣式里的top和left- 獲取的是字符串類型,還需要轉換成數值類型
- 寫在css樣式里的寬高是獲取不到的,只能獲取行內樣式
總結:
- 設置定位left/top使用style.left與style.top
- 獲取定位left/top使用offsetLeft與offsetTop
- offset獲取的位置包括margin
- 如果父元素沒有定位,獲取的就是相對body的
一張圖看清offset系列
2. 勻速動畫框架
2.1 勻速動畫初體驗
如何讓一個物體動起來?動畫函數的實現原理其實就是利用間歇定時器每隔一段時間執行一次的原理實現的。1、讓一個物體動起來
點擊按鈕讓一個盒子勻速往右執行一段距離:[04-勻速動畫初體驗(一).html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;}#box {width: 100px;height: 100px;background-color: hotpink;position: absolute;} </style><!-- html 部分 --> <button id="btn">奔跑吧</button> <div id="box"></div><!-- js 部分 --> <script>var btn = document.getElementById('btn');var box = document.getElementById('box');btn.onclick = function() {setInterval(function() {// 定義一個距離 相當于每一次要跑的距離 stepvar step = 5;// 定義一個當前位置 leadervar leader = box.offsetLeft;// 每次執行的時候 讓leader都走step距離leader = leader + step;// 將距離賦值給boxbox.style.left = leader + "px";// 每15ms 就執行一次 人眼視覺停留 就有動畫效果了}, 15);} </script>效果圖:
BUG: 不知道細心的小伙伴有沒有發現兩個問題
- 現在執行的時候是不會停下來的,一直往右跑
- 點擊按鈕之后再去點擊,會發現,按鈕點擊次數越多,盒子速度越快
2、讓一個物體動起來,解決bug
我們讓盒子運動到500px的位置停下來 [05-勻速動畫初體驗(二).html]
var btn = document.getElementById('btn'); var box = document.getElementById('box'); var timer = null; /**為什么會越點越快?點擊一次就會調用一次定時器,點擊的次數越多,調用的就越多距離疊加的就會越來越大 視覺效果上看起來就跑的越來越快只要在每次點擊后,定時器執行前清除上一次定時器,就不會出現越點越快的效果了 */ btn.onclick = function() {// 一進來就清除定時器clearInterval(timer);timer = setInterval(function() {// 定義一個距離 相當于每一次要跑的距離 stepvar step = 5;// 定義一個當前位置 leadervar leader = box.offsetLeft;/**當移動的位置在500px內的時候,執行動畫函數否則就清除定時器,讓盒子停下來*/if (leader < 500) {// 每次執行的時候 讓leader都走step距離leader = leader + step;// 將距離賦值給boxbox.style.left = leader + "px";} else {clearInterval(timer);}}, 15); }效果圖:
總結:
- setInterval間歇定時器,如果不手動清除,它就會一直運行下去
- 點擊事件觸發定時器一定要注意,一進來就清除一次,否則會越點越快
2.2 勻速動畫函數封裝
函數需要獨立,就不能使用全局變量。timer之前是一個全局變量,如果不獨立,頁面只有一個定時器在運作。封裝的函數里將timer綁定給調用定時器的元素,這樣就獨立了。1、封裝一個動畫函數 [06-封裝一個勻速動畫函數.html]
<!-- html 部分 --> <button id="btn">奔跑吧,500</button> <button id="btn2">奔跑吧,1000</button> <div id="box"></div><!-- js 部分 --> <script>var btn = document.getElementById('btn');var btn2 = document.getElementById('btn2');var box = document.getElementById('box');/**既然是封裝的函數,有些不確定的,經常變的元素就要提出來比如: 1.每一次改變的距離 num2.調用動畫的對象 box ==> element 3.運動的目標距離 500 ==> target*/// 封裝一個動畫函數function animate(element, target,num) {// 一進來就清除定時器// 函數需要獨立,就不能使用全局變量 所以將timer綁定在element上clearInterval(element.timer);element.timer = setInterval(function() {// 定義一個距離 相當于每一次要跑的距離 stepvar step = num;// 定義一個當前位置 leadervar leader = element.offsetLeft;if (leader < target) {// 每次執行的時候 讓leader都走step距離leader = leader + step;// 將距離賦值給boxelement.style.left = leader + "px";} else {clearInterval(element.timer);}}, 15);}// 點擊按鈕1 移動到500px的位置btn.onclick = function() {animate(box, 500, 9);}// 點擊按鈕2 移動到1000px的位置btn2.onclick = function() {animate(box, 1000, 5);} </script>注意: 上面的案例我們只是簡單的實現了一個動畫的封裝效果,但是作為一個以后會經常用的函數,上面的代碼還有很多需要優化的地方
- 1、上面的函數只能往正方向跑,也就是說去到1000,想讓它回到500是不好實現的;
- 2、如果每次走的距離是5,目標距離是500,正好能整除。假如每次走的是9呢?每次走9,是不能被500整除的,所以最后停下里的距離會偏多一點。
2、封裝一個動畫函數完整版 [07-封裝一個勻速動畫函數完整版.html]
- 先說說第二個問題,距離的問題。如果走的距離不能被目標距離整除的話,最后會多出來一點距離,我們可以不用管這個距離,直接在清除定時器,停下里的時候讓它的距離等于目標距離。
- 現在說說第一個問題,盒子到1000的時候不能回到500。假設現在盒子在1000,我們點擊按鈕1的時候想要讓他回到500,這個時候我們可以發現時的leader = 1000,目標距離target為500,就是說當leader>target的時候,盒子是可以往回走的,這時候只要將步數設置為負數 ,盒子就是往回跑的。
- 此時就不能再根據 if (leader < target){}, else { clearInterval(element.timer); }去判斷,讓盒子運動了。這時的判斷條件應該是目標距離target 與盒子目前距離leader之間差的絕對值大于等于一步距離step絕對值的時候,讓他們執行leader = leader + step;否則的話清除清除定時器,并將最后的距離直接設置為target的距離。
完整代碼:
<!-- html 部分 --> <button id="btn">奔跑吧,500</button> <button id="btn2">奔跑吧,1000</button> <div id="box"></div><!-- js 部分 --> <script>var btn = document.getElementById('btn');var btn2 = document.getElementById('btn2');var box = document.getElementById('box');function animate(element, target, num) {clearInterval(element.timer);element.timer = setInterval(function() {var leader = element.offsetLeft;// 判斷此時每次走的距離,當目標距離大于當前位置 說明往正方向走 step的值就是正的var step = target > leader ? num : -num;// 獲得此時的距離 與目標距離的差的絕對值var distance = Math.abs(target - leader);// 通過判斷此時的差如果大于或者等于一步的距離step的時候,就應該執行動畫if (distance >= Math.abs(step)) {leader = leader + step;element.style.left = leader + "px";} else {// 否則清除動畫,并且將最后的距離設置為target的距離clearInterval(element.timer);element.style.left = target + "px";}}, 15);}btn.onclick = function() {animate(box, 500, 9);}btn2.onclick = function() {animate(box, 1000, 5);} </script>效果圖:
如上,這就是封裝的一個完美的動畫函數了,下次有需要用到動畫的地方,直接引用即可——[ js/animate.js ]
3. 輪播圖
基本上每個網站都會用到輪播圖,輪播圖的使用可以說是必不可少的。以后我們用的最多的可能是插件,原生的可能并不常用,但是輪播圖的原理我們必須知道,并且能夠寫出來。(之前一次面試就是讓我講出輪播圖的具體實現步驟)3.1 簡單輪播圖
現在我們先來學習下簡單的輪播圖實現原理。輪播圖樣式的特點:
- ul要足夠的寬,要求能夠一行放下所有的li
- 父盒子的寬高和圖片的寬高一樣
- 父盒子要有一個overflow:hidden ,僅顯示一張圖片,不多不少
要求ul很寬很寬,因為所有的li要左浮動,要保證所有的li在一行上顯示,定義一個盒子,盒子的寬高要和顯示的單張圖片寬高一樣,然后設置overflow:hidden這樣其他的li就會被隱藏在下面,通過改變ul的位置就能實現圖片的切換了
示例代碼: [08-實現簡單的輪播圖.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#slide {width: 560px;height: 315px;margin: 100px auto;position: relative;overflow: hidden;}#slide ul {width: 600%;position: absolute;}#slide ul li {float: left;}#slide ul img {display: block;}#slide ol {width: 100px;height: 14px;background-color: rgba(255, 255, 255, .6);/* background-color: pink; */position: absolute;bottom: 14px;left: 50%;margin-left: -50px;border-radius: 7px;}#slide ol li {width: 10px;height: 10px;float: left;background-color: #fff;border-radius: 50%;margin-top: 2px;margin-left: 8.5px;cursor: pointer;}#slide ol li.current {background-color: #DF654A;} </style><!-- html 部分 --> <div id="slide"><ul><li><a href="#"><img src="../image/1.jpg" alt=""></a></li><li><a href="#"><img src="../image/2.jpg" alt=""></a></li><li><a href="#"><img src="../image/3.jpg" alt=""></a></li><li><a href="#"><img src="../image/4.jpg" alt=""></a></li><li><a href="#"><img src="../image/5.jpg" alt=""></a></li></ul><ol><li class="current"></li><li></li><li></li><li></li><li></li></ol> </div><!-- js 部分 --> <script src="../js/animate.js"></script> <script>var slide = document.getElementById('slide');var ul = slide.children[0];var ol = slide.children[1];// ol 下的 li 小圓點var lis = ol.children;var imgWidth = slide.offsetWidth;// 給所有的小圓點注冊點擊事件for (var i = 0; i < lis.length; i++) {lis[i].index = i;lis[i].onclick = function() {// 小圓點高亮排他for (var i = 0; i < lis.length; i++) {lis[i].className = "";}this.className = "current";// 點擊小圓點,讓對應的圖片輪播 獲取ul要改變的距離// 負的表示ul 向左運動 此時小圓點對應的索引乘以盒子的寬度 就是ul要移動的寬度var target = -this.index * imgWidth;// ul.style.left = target + 'px';// 讓圖片像動畫一樣慢慢的移過去animate(ul, target, 50);}} </script>效果圖:
從上面效果圖中,我們可以看到,一個最簡單的輪播圖已經成型了,但是需要去用手點擊,而且如果跨點數去點擊,會發現圖片要一張張滑過去,這里后面我們會優化。
3.2 左右焦點輪播圖
左右焦點輪播圖,就是在顯示圖片的兩端添加兩個按鈕,一個向左,一個向右,點擊的時候圖片會根據點擊的方向滑動。并且當鼠標懸停在顯示區域的時候,兩個按鈕顯示。鼠標離開顯示區域,,兩個按鈕隱藏。示例代碼: [09-左右焦點輪播圖.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#slide {width: 560px;height: 315px;margin: 100px auto;position: relative;overflow: hidden;}#slide ul {width: 600%;position: absolute;}#slide ul li {float: left;}#slide ul img {display: block;}#slide #arrow {display: none;}#slide #arrow #leftArr,#slide #arrow #rightArr {width: 30px;height: 60px;background-color: rgba(255, 255, 2550, 0.3);position: absolute;top: 50%;margin-top: -30px;text-decoration: none;color: #fff;text-align: center;font: 700 24px/60px "宋體";}#slide #arrow #leftArr {left: 0;}#slide #arrow #rightArr {right: 0;} </style><!-- html 部分 --> <div id="slide"><ul><li><a href="#"><img src="../image/1.jpg" alt=""></a></li><li><a href="#"><img src="../image/2.jpg" alt=""></a></li><li><a href="#"><img src="../image/3.jpg" alt=""></a></li><li><a href="#"><img src="../image/4.jpg" alt=""></a></li><li><a href="#"><img src="../image/5.jpg" alt=""></a></li></ul><div id="arrow"><a href="javascript:void(0);" id="leftArr"><</a><a href="javascript:void(0);" id="rightArr">></a></div> </div><!-- js 部分 --> <script src="../js/animate.js"></script> <script>var slide = document.getElementById('slide');var ul = slide.children[0];var lis = ul.children;var arrow = document.getElementById('arrow');var leftArr = document.getElementById("leftArr");var rightArr = document.getElementById("rightArr");var imgWidth = slide.offsetWidth;// 給slide注冊鼠標經過事件,鼠標經過時 顯示arrowslide.onmouseover = function() {arrow.style.display = "block";};// 給slide注冊鼠標離開事件,鼠標離開時 隱藏arrowslide.onmouseout = function() {arrow.style.display = "none";};// 點擊右箭頭var count = 0; // 跑出去的張數rightArr.onclick = function() {// 當這個張數不等于最后一張的時候 執行動畫if (count < lis.length - 1) {count++;var target = -count * imgWidth;animate(ul, target, 40);}}leftArr.onclick = function() {// 當這個張數不等于最后一張的時候 執行動畫if (count > 0) {count--;var target = -count * imgWidth;animate(ul, target, 40);}} </script>效果圖:
3.3 無縫輪播圖
上圖可以看到,當滑到最左邊或者最右邊的時候,再點擊就沒有用了,正常的輪播圖肯定不是這樣的,點擊到最后一張后再點擊肯定是接著滑動的。下面我們接著看,如何實現一個無縫輪播圖
示例代碼:無縫輪播(可以一直點擊) [10-左右焦點輪播圖-無縫滾動.html]
何謂無縫滾動?無縫滾動就是圖片能夠循環切換,就算是最后一張,點擊之后也會跳到第一張
原理:
- 效果就像上面所說的一樣,主要實現原理就是,在最后面一張圖片,再加上一張圖片,這張圖片就是第一張圖片
- 當滑動到最后一張圖片的時候(看下圖),此時的視覺效果就是停在第一張圖片上
- 這時只需要在程序上判斷,當在最后一張的時候,直接跳到第一張圖片即可
示例代碼:無縫滾動的簡單原理 [10-無縫滾動原理.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#slide {position: relative;width: 560px;height: 315px;border: 6px dashed #CBF078;margin: 100px auto;overflow: hidden;}#slide ul {width: 3360px;position: absolute;left: 0;top: 0;}#slide ul li {float: left;}#slide ul li img {display: block;vertical-align: top;} </style><!-- html 部分 --> <div id="slide"><ul><li><img src="../image/1.jpg" alt=""></li><li><img src="../image/2.jpg" alt=""></li><li><img src="../image/3.jpg" alt=""></li><li><img src="../image/4.jpg" alt=""></li><li><img src="../image/5.jpg" alt=""></li><!-- 添加一張與第一張一模一樣的圖片 障眼法 --><li><img src="../image/1.jpg" alt=""></li></ul> </div><!-- js 部分 --> <script>var slide = document.getElementById('slide');var ul = slide.children[0];setInterval(function() {// 每次向左移動的距離var step = -3;// 獲取 ul的left的值 是個負值var leader = ul.offsetLeft;// 定義一個目標距離,這里的目標距離指的是最后一張圖片距離左邊的left值// 圖片寬度560 在最后一張距離左邊left的位置:-560*5 = -2800// 就是說當到達這張圖片的時候就應該讓 ul.style.left = "0px";var target = -2800;// 為什么不直接判斷 leader = -2800的時候讓ul.style.left = "0px";?// 因為每次走3步 3不能被2800整除,所以leader永遠不會等于-2800的// 這里直接判斷leader此時距左邊的距離減去目標距離當這個絕對值大于等于 一步距離的絕對值3的時候讓它執行往左運動if (Math.abs(leader - target) >= Math.abs(step)) {leader = leader + step;ul.style.left = leader + "px";// 當不足一步距離的時候說明就是最后一張了,就應該跳到第一張圖片了} else {ul.style.left = "0px";}}, 15); </script>效果圖:
左右焦點無縫輪播圖: [11-左右焦點無縫輪播圖.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#slide {width: 560px;height: 315px;margin: 100px auto;position: relative;overflow: hidden;}#slide ul {width: 600%;position: absolute;}#slide ul li {float: left;}#slide ul img {display: block;}#slide #arrow {display: none;}#slide #arrow #leftArr,#slide #arrow #rightArr {width: 30px;height: 60px;background-color: rgba(255, 255, 2550, 0.3);position: absolute;top: 50%;margin-top: -30px;text-decoration: none;color: #fff;text-align: center;font: 700 24px/60px "宋體";}#slide #arrow #leftArr {left: 0;}#slide #arrow #rightArr {right: 0;} </style><!-- html 部分--> <div id="slide"><ul><li><a href="#"><img src="../image/1.jpg" alt=""></a></li><li><a href="#"><img src="../image/2.jpg" alt=""></a></li><li><a href="#"><img src="../image/3.jpg" alt=""></a></li><li><a href="#"><img src="../image/4.jpg" alt=""></a></li><li><a href="#"><img src="../image/5.jpg" alt=""></a></li><!-- 添加一張圖片 障眼法 --><li><a href="#"><img src="../image/1.jpg" alt=""></a></li></ul><div id="arrow"><a href="javascript:void(0);" id="leftArr"><</a><a href="javascript:void(0);" id="rightArr">></a></div> </div><!-- js 部分 --> <script src="../js/animate.js"></script> <script>var slide = document.getElementById('slide');var ul = slide.children[0];var lis = ul.children;var arrow = document.getElementById('arrow');var leftArr = document.getElementById("leftArr");var rightArr = document.getElementById("rightArr");var imgWidth = slide.offsetWidth;// 給slide注冊鼠標經過事件,鼠標經過時 顯示arrowslide.onmouseover = function() {arrow.style.display = "block";};// 給slide注冊鼠標離開事件,鼠標離開時 隱藏arrowslide.onmouseout = function() {arrow.style.display = "none";};// 點擊右箭頭var count = 0; // 跑出去的張數rightArr.onclick = function() {// 當這個張數等于最后一張的時候,偷偷摸摸的把最后一張圖片換成第一張if (count == lis.length - 1) {count = 0;ul.style.left = 0;}count++;var target = -count * imgWidth;animate(ul, target, 40);}leftArr.onclick = function() {// 判斷是第一張的時候,偷偷摸摸的把第一張換成最后一張if (count == 0) {count = lis.length - 1;ul.style.left = -count * imgWidth + "px";}count--;var target = -count * imgWidth;animate(ul, target, 40);} </script>效果圖:
3.4 完整版輪播圖
前面我們已經可以通過點擊對應的小點、左右焦點和無縫滾動來實現輪播圖了,不過都是單獨分開來的,現在我們做個整合,實現一個完整的輪播圖。功能概述:
-
簡單輪播功能
- 給circle下的所有的li注冊點擊事件
- 排他
- 移動Ul
-
左右焦點功能
- 需要定義一個變量count來記錄移動的圖片的張數。
-
點擊右箭頭功能
- 如果當前圖片是最后一張(假圖片),需要瞬間變成真圖片
- 點擊一次,需要讓圖片往右移動一張
- 同步小圓點,干掉所有小圓點,復活對應count的小圓點。
- 最后一張假圖片對應的小圓點是第一個,需要做特殊處理
-
自動輪播的功能
- 開啟定時器,每隔兩秒點擊一次右箭頭
- 鼠標經過盒子,停止定時器(箭頭亂閃的問題解釋)觸發事件的一定要是外面的大盒子,不能是ul,如果給ul注冊事件,就會出現亂閃的問題
- 鼠標離開盒子,開啟定時器
-
同步功能
- 點擊小圓點時需要同步
- 淘寶bug解決方法(當一圈過后回到第一個小圓點的時候,再點擊它會發現他會再跑一圈)
- 淘寶bug圖:
完整代碼: [12-完整版輪播圖.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#slide {width: 560px;height: 315px;margin: 100px auto;position: relative;overflow: hidden;}#slide ul {width: 600%;position: absolute;}#slide ul li {float: left;}#slide ul img {display: block;}#slide #arrow {display: none;}#slide #arrow #leftArr,#slide #arrow #rightArr {width: 30px;height: 60px;background-color: rgba(0, 0, 0, 0.5);position: absolute;top: 50%;margin-top: -30px;text-decoration: none;color: #fff;text-align: center;font: 700 24px/60px "宋體";}#slide #arrow #leftArr {left: 0;}#slide #arrow #rightArr {right: 0;}#slide ol {width: 100px;height: 14px;background-color: rgba(255, 255, 255, .6);/* background-color: pink; */position: absolute;bottom: 14px;left: 50%;margin-left: -50px;border-radius: 7px;}#slide ol li {width: 10px;height: 10px;float: left;background-color: #fff;border-radius: 50%;margin-top: 2px;margin-left: 8.5px;cursor: pointer;}#slide ol li.current {background-color: #DF654A;} </style><!--html 部分--> <div id="slide"><ul><li><a href="#"><img src="../image/1.jpg" alt=""></a></li><li><a href="#"><img src="../image/2.jpg" alt=""></a></li><li><a href="#"><img src="../image/3.jpg" alt=""></a></li><li><a href="#"><img src="../image/4.jpg" alt=""></a></li><li><a href="#"><img src="../image/5.jpg" alt=""></a></li><li><a href="#"><img src="../image/1.jpg" alt=""></a></li></ul><!-- 左右箭頭 --><div id="arrow"><a href="javascript:void(0);" id="leftArr"><</a><a href="javascript:void(0);" id="rightArr">></a></div><!-- 小圓點 --><ol id="circleOl"><li class="current"></li><li></li><li></li><li></li><li></li></ol> </div><script src="../js/animate.js"></script> <script>// 自執行函數,防止頁面其他定時器會受影響(function() {var slide = document.getElementById('slide');var imgUl = slide.children[0];var imgLis = imgUl.children;var arrow = document.getElementById('arrow');var leftArr = document.getElementById("leftArr");var rightArr = document.getElementById("rightArr");var circleOl = document.getElementById('circleOl');var circleLis = circleOl.children;// 獲取圖片的寬度var imgWidth = slide.offsetWidth;var timer = null;// 點擊小圓點改變對應圖片for (var i = 0; i < circleLis.length; i++) {circleLis[i].index = i;circleLis[i].onclick = function() {// 小圓點點擊的時候高亮排他for (var i = 0; i < circleLis.length; i++) {circleLis[i].className = "";}this.className = "current";// 淘寶bug:這時還需要判斷一下 就是當圖片在最后一張假圖片的時候,// 再去點擊第一個小圓點的時候,會出現一個bug,就是圖片會輪播一圈再回到這張圖片上if (count == imgLis.length - 1) {count = 0;imgUl.style.left = 0;}// 點擊小圓點圖片要移動var target = -this.index * imgWidth;// 如果這里不記錄一下,當點擊小圓點跳到某張圖片的時候,再自動播放的時候,// 不會接著當前小圓點的位置往后播放,而是接著之前count不變的情況下 繼續播放的count = this.index;animate(imgUl, target, 40);}}// 左右焦點輪播圖var count = 0; // 跑出去的張數rightArr.onclick = function() {// 當這個張數等于最后一張(假圖片)的時候,偷偷摸摸的把最后一張圖片換成第一張if (count == imgLis.length - 1) {count = 0;imgUl.style.left = 0;}// 點擊一次圖片向右劃動一次count++;var target = -count * imgWidth;animate(imgUl, target, 40);//讓小圓點跟著動 只要將 count 與小圓點綁定即可for (var i = 0; i < circleLis.length; i++) {circleLis[i].className = "";}// 這里需要判斷一下 因為此時最后一張是假圖片 小圓點是不能正常跳轉到第一個的// 當count == 最后一張圖片的下標的時候,直接讓第一個小圓點亮if (count == imgLis.length - 1) {circleLis[0].className = "current";} else {// 否則其他的下標對應的小圓點高亮circleLis[count].className = "current";}}leftArr.onclick = function() {// 判斷是第一張的時候,偷偷摸摸的把第一張換成最后一張if (count == 0) {count = imgLis.length - 1;imgUl.style.left = -count * imgWidth + "px";}count--;var target = -count * imgWidth;animate(imgUl, target, 40);// 小圓點同步 往左的時候不會出現小圓點不同步的問題for (var i = 0; i < circleLis.length; i++) {circleLis[i].className = "";}circleLis[count].className = "current";}timer = setInterval(function() {rightArr.onclick();}, 2000);// 給slide注冊鼠標經過事件,鼠標經過時 顯示arrowslide.onmouseover = function() {arrow.style.display = "block";// 鼠標經過圖片的時候清除定時器,停止輪播clearInterval(timer);};// 給slide注冊鼠標離開事件,鼠標離開時 隱藏arrowslide.onmouseout = function() {arrow.style.display = "none";// 鼠標離開圖片的時候開啟定時器,自動輪播timer = setInterval(function() {rightArr.onclick();}, 2000);};})()輪播圖的一些功能可能有點繞,寫的有可能不太看得懂,有疑惑的小伙伴直接私信我,我會一步步解釋給你聽的,有什么bug也可以將代碼發給我。
想為后面打下扎實的基礎的話,這里一定要多敲幾遍,主要要搞明白的是實現的思路,以及一些小bug的解決。這對后面的學習是非常重要的。代碼備注可能讀起來有些拗口,不懂得小伙伴直接私信給我。
4. 緩動動畫框架
4.1 緩動動畫初體驗
緩動動畫,顧名思義,就是越來越慢的運動。我們先來回顧一下上面勻速動畫運動的原理:
/**step : 一步的距離leader :當前的距離我們可以看到 step在這里一直等于5 不曾改變 所以就是勻速運動 */ var step = 5; leader = leader + step;現在我們再來看下緩動動畫的原理:
/** target: 目標距離,盒子運動到什么地方step : 同樣的,還是指每次運動的距離,但是這里的步數是一個變化的量了,我們可以看到它會隨著leader的增加變得越來越小,這就是緩動動畫的原理leader: 當前的距離 */ var step = (target - leader)/10; leader = leader + step;示例代碼: [13-緩動動畫初體驗(一).html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;}#box {width: 100px;height: 100px;position: absolute;background: orange;} </style><!-- html 部分--> <input type="button" value="奔跑吧" id="btn"> <div id="box"></div><!-- js 部分 --> <script>var box = document.getElementById('box');var btn = document.getElementById('btn');var timer = null;btn.onclick = function() {clearInterval(timer);timer = setInterval(function() {// 定義一個目標距離var target = 600;// 獲得當前盒子的位置var leader = box.offsetLeft;// 每次運動的距離var step = (target - leader) / 10;// leader = leader + step 動起來leader += step;// 將距離給盒子box.style.left = leader + "px";// 當當前距離等于目標距離的時候清除定時器if (leader == target) {clearInterval(timer);}}, 15);} </script>效果圖:
完美了嗎?并沒有,這里有個小bug:
可能會有小伙伴不理解,有問題你上面直接講一下不就得了,還特地賣關子在下面重新寫一遍。我想跟大家說的一點就是,如果在上面我直接告訴你這里有個問題有個bug的話,你一眼看過,可能都不當回事,我在這里拿出來講一下,相信這個知識點你會記得更深。
小bug:明明設置的是600,怎么會是596.4px呢?
原因:
- offsetLeft獲取值的時候,只會獲取整數,會對小數部分會四舍五入處理,比如step = (target - leader)/10當step的值出現小數的時候,leader+= step之后,offsetLeft在獲取leader位置的時候就會把小數部分四舍五入,這樣就會造成最后距離的誤差。
解決方法:
- 對step向上取整處理(Math.ceil()),保證每一次都至少跑1px的距離,只要不出現小數offsetLeft就不會出現四舍五入。
完整代碼: [14-緩動動畫初體驗(二).html]
var box = document.getElementById('box'); var btn = document.getElementById('btn'); var timer = null;btn.onclick = function() {clearInterval(timer);timer = setInterval(function() {// 定義一個目標距離var target = 600;// 獲得當前盒子的位置var leader = box.offsetLeft;// 每次運動的距離var step = (target - leader) / 10;// 對step進行向上取整step = Math.ceil(step);// leader = leader + step 動起來leader += step;// 將距離給盒子box.style.left = leader + "px";// 當當前距離等于目標距離的時候清除定時器if (leader == target) {clearInterval(timer);}}, 15); }4.2 緩動動畫函數封裝
前面勻速動畫那里已經講過封裝一個函數的好處與重要性,現在我們將緩動動畫也封裝成一個函數。示例代碼: [15-緩動動畫函數封裝.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;}#box {width: 100px;height: 100px;background: orange;position: absolute;} </style><!-- html 部分 --> <input type="button" value="奔跑吧500" id="btn1"> <input type="button" value="奔跑吧1000" id="btn2"> <div id="box"></div><!-- js 部分 --> <script>var btn1 = document.getElementById('btn1');var btn2 = document.getElementById('btn2');var box = document.getElementById('box');// 緩動動畫函數/**element : 執行動畫元素target : 目標距離num : 用來控制動畫執行的速度 越大動畫執行越慢*/function slowAnimate(element, target, num) {// 一進來就要清除定時器,防止越點越快clearInterval(element.timer);element.timer = setInterval(function() {// 獲得元素當前位置var leader = element.offsetLeft;// 定義每次運動的距離var step = (target - leader) / num;// step可能是小數 所以要取整step = Math.ceil(step);leader += step;// 設置元素的位置element.style.left = leader + 'px';// 當元素的位置 等于 目標位置的時候 清除定時器if (leader == target) {clearInterval(element.timer);}}, 15);}// 調用緩動動畫函數btn1.onclick = function() {slowAnimate(box, 500, 10);}// 同樣是運動500的距離,我們可以發現從500到1000,明顯執行的比從0-500執行的慢btn2.onclick = function() {slowAnimate(box, 1000, 30);} </script>效果圖:
又到了找bug的時候了:
上面的代碼從0-500,從500-1000都沒有問題,經過向上取整后都能到達目標距離:500和1000。但是小伙伴可以看下,當從1000回到500的時候,是正好回到500的嗎?答案肯定不是的,為什么呢?
step為正數的時候,向上取整是完全沒有問題的,但是當從1000到500的時候,step就是負數了,負數向上取整后就會變得更大,比如原本是-33.3,向上取整后就是-33了,-0.3就會舍去,所有就不會到500的位置。
解決方法: 判斷step的正負,為正的時候,向上取整。為負的時候,向下取整。
緩動函數封裝完整版: [16-緩動動畫函數封裝完整版.html]
function slowAnimate(element, target, num) {// 一進來就要清除定時器,防止越點越快clearInterval(element.timer);element.timer = setInterval(function() {// 獲得元素當前位置var leader = element.offsetLeft;// 定義每次運動的距離var step = (target - leader) / num;//如果step是正數,對step向上取整,//如果step是負數,對step向下取整// 保證每一次最少都走1pxstep = step > 0 ? Math.ceil(step) : Math.floor(step);leader += step;// 設置元素的位置element.style.left = leader + 'px';// 當元素的位置 等于 目標位置的時候 清除定時器if (leader == target) {clearInterval(element.timer);}}, 15); };4.3 獲取元素計算后的樣式
獲取元素計算后的樣式指的是元素經過層疊后真正生效的樣式,不管樣式寫在哪,計算后的樣式指的就是最終的樣式。通過style只能獲取到寫在行內的樣式,那么想要獲取其他的樣式怎么辦呢?
- js提供了一個方法:window.getComputedStyle(element, null)[attr];,它返回的是一個對象CSSStyleDeclaration,[attr]就是這個對象里面就是計算后的所有的樣式的屬性名(關聯數組取對象的值)。element指的是當前參數,null
這里可以不用深究-官方解釋。這個方法需要window調用。
/**element :獲取樣式的當前元素null :這里可以傳一個偽元素,如果不是偽元素的話必須是nullattr :后面可以寫具體的屬性,比如boderRadius 就會獲取這個元素的border-radius樣式信息 */ window.getComputedStyle(element,null)[attr];
示例代碼: [17-獲取元素計算后的樣式.html]
<!-- 樣式部分 --> <style>div {width: 100px;height: 100px;background: pink;}#box {width: 200px;} </style><!-- html 部分 --> <div id="box" style="width:300px;"></div><!-- js 部分 --> <script>var box = document.getElementById('box');console.log(window.getComputedStyle(box, null)); // 打印獲得box的各種屬性的樣式// 其中行內樣式權重最高,所以最后獲得的寬應該是300pxconsole.log(window.getComputedStyle(box, null).width); // 300pxconsole.log(window.getComputedStyle(box, null).background);</script>效果圖:
兼容性處理:
- window.getComputedStyle(element, null)[attr];只適用于現代瀏覽器中
- IE678有自己的方法:element.currentStyle[attr];
[18-獲取元素計算后的樣式兼容性處理.html]
注意: 上面的封裝函數中,調用的時候,屬性名是一個字符串類型。
4.4 緩動動畫修改多個樣式
不管是上面的勻速動畫函數,還是這里的緩動動畫函數,都只能左右運動,但是一個真正完整的動畫函數,只改變左右位置肯定是不夠的,我們可能需要改變它的寬高等。在上面一節中,我們知道了如何獲取到元素計算后的樣式,而且只要是元素有的樣式都能獲取到,有了這個方法我們就可以讓動畫去執行更多的事情了。1、對獲取到的樣式返回值進行處理:
在上面的一節中,我們可以看到,獲取的返回值都是字符串格式,比如獲取寬度的時候,返回的是一個"300px"的字符串,因為緩動動畫函數里面是需要計算的,這里是個字符串肯定不行,所以我們需要對其進行parseInt取整處理。[19-緩動動畫修改多個樣式-處理返回值.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 返回的是樣式屬性的值 我們用一個變量將它儲存起來var leader = getStyle(element, attr);// 因為返回值是一個字符串,并且帶有字符px,所以我們對返回值進行取整轉換leader = parseInt(leader) || 0; // 這里或 0的目的就是,當parseInt取整失敗的話,給一個默認值0var step = (target - leader) / 10;step = step > 0 ? Math.ceil(step) : Math.floor(step);leader += step;// 設置指定樣式element.style[attr] = leader + "px";if (leader == target) {clearInterval(element.timer);}}, 15); }animate(box, "left", 800);上面的代碼我們對它的返回值進行了處理,而且還可以對它設置其他的樣式,只要單位是px的屬性都可以設置。但是這里每次還是只能設置一個樣式,下面我們來實現修改多個樣式。
注意: leader = parseInt(leader) || 0; "或"上0的目的就是:當有些屬性設置的值不是數字的時候,比如:auto,這時候parseInt轉換的結果是NaN。當"或"上0之后,轉換失敗后,leader,就會默認是0。
2、遍歷一個對象:
讓我們來復習一下,js基礎的時候,我們接觸到了對象,并且知道了可以用for..in的方法來遍歷對象。我們知道getComputedStyle方法,獲取計算后樣式的時候,返回的是一個名叫CSSStyleDeclaration的對象,這個對象里面是所有的樣式屬性,我們想要對這些屬性進行多個操作的時候,就可以通過遍歷的方法。 for(k in obj){// k :就是相當于對象的鍵// obj :就是需要遍歷的對象 }3、同時修改多個樣式:
同時修改多個樣式,就是將要修改的多個屬性以對象的形式作為參數傳進函數中。[20-緩動動畫修改多個樣式.html]
var box = document.getElementById('box'); var btn = document.getElementById('btn');// 封裝一個函數,element 表示執行動畫的元素 obj傳的是一個對象,里面可以設置多個屬性和值 function animate(element, obj) {clearInterval(element.timer);element.timer = setInterval(function() {// 遍歷外部傳進來的對象for (k in obj) {//attr : 要做動畫的樣式//target : 目標值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;// 給元素設置以樣式屬性名為attr的值 // 這個封裝的動畫函數只能改值是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];} } // 調用函數 設置了五個樣式屬性 btn.onclick = function() {animate(box, {width: 200,height: 200,left: 300,top: 300,// bprder-radius 應該轉為駝峰命名法 并且值只能是100px的格式 不能是百分比borderRadius: 100}); }效果圖:
通過上面封裝的函數我們可以改變多個樣式,但是效果圖中我們可以看到一個問題,就是當到達設定值后,點擊按鈕還會慢慢的抖動。原因是修改多個樣式的時候,所有的樣式并不能都到同時達終點。
4.5 緩動動畫修復定時器bug
出現這個bug的原因:在for循環中判斷是否到達目標值,到達后就清除定時器,但是我們同時修改了5個樣式,可能有的樣式到達目標值后就清楚定時器了,但是有的樣式還沒到達目標值,所以就出現了上面的bug。解決方法:假設成立法
- 假設成立
- 想辦法推翻假設
- 如果推翻不了,說明假設成立
示例代碼: [21-緩動動畫修改多個樣式-修復定時器bug.html]
function animate(element, obj) {clearInterval(element.timer);element.timer = setInterval(function() {// 1-假設都到達了終點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- 必須要等到所有的樣式都到達終點才清除定時器// 只要有一個樣式沒有到達設定值,說明假設失敗if (leader != target) {flag = false;}}// 所有的樣式都到達終點后 清除定時器if (flag) {clearInterval(element.timer);}}, 15); }4.6 緩動動畫兼容其它樣式屬性
經過前面幾小節的學習,我們已經可以實現同時修改多個樣式的緩動動畫了。但是細心的小伙伴不知道有沒有發現,目前只能設置跟px有關系的樣式,包括設置border-radiu也不算完善。這是因為我們緩動動畫封裝的時后,設置的element.style[attr] = leader + "px";,所以只能實現跟px有關的樣式。設置兼容其他屬性的時候,要注意兩點,第一獲取的時候要進行判斷,設置的時候也要進行判斷
1、兼容opacity屬性: [22-緩動動畫修改多個樣式-兼容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;// 獲得當前值if (attr === "opacity") {// 獲取的時候是個小數,將它乘以100 運算時不會出現精度丟失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") {// 因為開始的時候leader擴大了100倍 設置的時候 opacity只能是0-1element.style[attr] = leader / 100;// opacity 還需要單獨處理,因為IE678 不支持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];} }// 調用這個函數 btn.onclick = function() {animate(box, {width: 200,height: 200,left: 300,top: 300,// 這里是按照 0-100 設置不透明度的,因為小數計算的時候會出現精度丟失opacity: 50}); }2、兼容zIndex屬性: [23-緩動動畫修改多個樣式-兼容zIndex.html]
zIndex這個屬性不需要緩動的執行改變層級,直接獲得傳進來的值設置即可 // 賦值 if (attr === "opacity") {element.style[attr] = leader / 100;element.style.filter = "alpha(opacity=" + leader + ")"; // 判斷設置的時候是否是zIndex屬性 } else if (attr === "zIndex") {element.style.zIndex= leader; } else {element.style[attr] = leader + "px"; }示例代碼: [24-緩動動畫淡入淡出效果.html]
btn1.onclick = function() {animate(box, {opacity: 100}) } btn2.onclick = function() {animate(box, {opacity: 0}) }效果圖:
4.7 緩動動畫添加回調函數
程序執行完畢,再次執行的函數。示例代碼: [25-緩動動畫添加回調函數.html]
var box = document.getElementById('box'); var btn = document.getElementById('btn');function animate(element, obj, fn) {clearInterval(element.timer);element.timer = setInterval(function() {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";if (leader != target) {flag = false;}}if (flag) {clearInterval(element.timer);// 所有程序執行完畢了,現在可以執行回調函數了// 只有傳遞了回調函數,才能執行,所以這里要判斷一下if (fn) {fn();}/* fn&&fn(); */}}, 15); }// 處理兼容性 function getStyle(element, attr) {if (window.getComputedStyle) {return window.getComputedStyle(element, null)[attr];} else {return element.currentStyle[attr];} } // 調用函數 btn.onclick = function() {animate(box, {left: 600}, function() {animate(box, {top: 500,borderRadius: 50}, function() {animate(box, {width: 400,borderRadius: 50});});}); }效果圖:
5. 筋斗云案例
直接看效果圖:
效果如上圖,當我們鼠標經過某一項時,小方塊會緩動移過去,當離開列表欄時,小方塊會回到最初的位置。當點擊某一項時小方塊的初始位置就會停留在該項上。
示例代碼: [26-筋斗云案例.html]
<!-- 樣式部分 --> <style>body {padding: 0;margin: 0;background: #333;}#box {width: 800px;height: 34px;margin: 100px auto;background: orange;position: relative;}ul {padding: 0 50px;height: 34px;position: relative;}#box ul li {float: left;width: 100px;height: 34px;line-height: 34px;text-align: center;list-style: none;font-size: 18px;font-family: '方正';color: #fff;cursor: pointer;}#over {position: absolute;top: -3px;left: 51px;width: 100px;height: 38px;background: orangered;} </style><!-- html 部分 --> <div id='box'><span id='over'></span><ul id='nav'><li>首頁</li><li>社區服務</li><li>智慧超市</li><li>便民</li><li>圈子</li><li>活動</li><li>聚優惠</li></ul> </div><!-- js 部分 --> <script>var over = document.getElementById('over');var nav = document.getElementById('nav');var lis = nav.children;for (var i = 0; i < lis.length; i++) {lis[i].onmouseover = function() {// 鼠標經過時移動的距離就是它距離左邊的距離slowAnimate(over, this.offsetLeft);}// 設定默認位置,因為第一個選項距離左邊為51px距離所以,默認值設置為51var staticLeft = 51;lis[i].onmouseout = function() {// 鼠標離開的時候,要讓它回到默認位置slowAnimate(over, staticLeft);}lis[i].onclick = function() {// 當點擊某一選項的時候,將默認位置設置為此時的位置staticLeft = this.offsetLeft;}}// 緩動動畫function slowAnimate(element, target, num) {clearInterval(element.timer);element.timer = setInterval(function() {var leader = element.offsetLeft;// num 不傳的話,默認是10var step = (target - leader) / (num || 10);step = step > 0 ? Math.ceil(step) : Math.floor(step);leader += step;element.style.left = leader + 'px';if (leader == target) {clearInterval(element.timer);}}, 15);} </script>6. 右下角關閉廣告案例
在網頁中經常會出現廣告,我們舉個例子讓關閉廣告的時候有一個動畫效果。實現原理:
- 圖片其實被切成了兩個部分,看到的效果是一張圖片,其實是兩張。
- 點擊關閉按鈕的時候,調用緩動動畫函數,將下半部分的盒子高度等于0,所以會出現一個向下的效果
- 在剛剛的動畫函數的回調函數里面繼續調用緩動動畫,將整個大盒子的寬度等于0,所以出現一個向右的效果
示例代碼: [27-右下角關閉廣告案例.html]
<!-- 樣式部分 --> <style>#box {width: 213px;position: fixed;bottom: 0;right: 0;overflow: hidden;}#close {position: absolute;top: 0;right: 0;width: 30px;height: 30px;cursor: pointer;color: #FFFFFF;text-align: center;}.img {display: block;width: 212px;z-index: 99;} </style><!-- html 部分 --> <div id="box"><div id="hd"><span id="close"> x </span><img src="../image/關閉廣告/banna_up.png" class="img" alt="" /></div><div id="bt"><img src="../image/關閉廣告/banner_down.png" class="img" alt="" /></div> </div><!-- js 部分 --> <script src="../js/slow-animate-styles.js"></script> <script>var close = document.getElementById('close');var box = document.getElementById('box');var bt = document.getElementById('bt');close.onclick = function() {slowAnimateStyles(bt, {height: 0}, function() {slowAnimateStyles(box, {width: 0});});} </script>效果圖:
7. 手風琴案例
手風琴效果在網頁中用的也特別的多,下面我們會介紹兩種實現的方法,當然個人比較偏好第二種。1、浮動版手風琴
實現原理:
- 用ul,li進行布局,li左浮動,并且設置等分的寬度;
- 給每個li注冊鼠標經過事件,當鼠標經過的時候利用排他原理,將所有的li寬度設置成最小寬度,將當前經過的li寬度設置一個最大寬度;
- 然后再去設置鼠標離開事件,當鼠標離開時讓所有的li再恢復到等分的寬度。
示例代碼: [28-手風琴-浮動版.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#box {width: 900px;height: 441px;margin: 100px auto;overflow: hidden;border-radius: 30px;}ul {/* ul的寬要比外面的盒子大一點,否則在添加動畫效果的時候,最后一個li會出現閃動 */width: 120%;height: 100%;overflow: hidden;}li {width: 180px;height: 100%;float: left;} </style><!-- html 部分 --> <div id="box"><ul><li></li><li></li><li></li><li></li><li></li></ul> </div><!-- js 部分 --> <script src="../js/slow-animate-styles.js"></script> <script>var box = document.getElementById('box');var lis = box.getElementsByTagName("li");for (var i = 0; i < lis.length; i++) {// 動態創建img標簽var img = document.createElement("img");img.src = "../image/手風琴/" + (i + 1) + ".png";lis[i].appendChild(img);// 給所有li注冊鼠標經過事件,讓當前的li寬度變成 500,其余的li寬度變成100lis[i].onmouseover = function() {for (var i = 0; i < lis.length; i++) {// 先讓所有的li寬度變成100slowAnimateStyles(lis[i], {width: 100});// 鼠標當前經過的寬度為500slowAnimateStyles(this, {width: 500})}};// 當鼠標離開的時候,所以的li 寬度恢復到180pxlis[i].onmouseout = function() {for (var i = 0; i < lis.length; i++) {slowAnimateStyles(lis[i], {width: 180})}}} </script>效果圖:
2、定位版手風琴
實現原理:
- 給外部大盒子設置一個與圖片大小一致的寬高,并且設置相對定位
- 還是采用ul,li結構,li設置寬高,與圖片大小一致,設置絕對定
- 動態的給li添加背景圖片,因為li絕對定位的原因,此時所有的li都疊在一起
- 動態的給每個li設置left值(left*i),這時候li就會依次排開
- 大盒子還要設置一個overflow-hidden屬性,將多余的隱藏掉
- 給每個li注冊鼠標鼠標經過事件,然后根據下面推算出的規律(當前鼠標經過的索引index,他之前包括他自己的left值都是,設定的最小值乘以對應的索引。而他后面的會將設定的最小值乘以對應的索引后再加上450,這里的450不是一個固定值,根據規律找出來的)進行判斷,設置各自的left值;
- 鼠標離開的時候再讓所有的盒子恢復到一開始的位置,每個li顯示等分的寬度
大盒子沒有overflow-hidden的時候:
畫個圖,理解一下:
找規律:
結合上面的圖片,我們可以找到一個規律-
當鼠標在第1個li上的時候,li下標index為0:
- index:0 left:0
- index:1 left:500px
- index:2 left:550px
- index:3 left:600px
- index:4 left:650px
-
當鼠標在第2個li上的時候,li下標index為1:
- index:0 left:0
- index:1 left:50px
- index:2 left:550px
- index:3 left:600px
- index:4 left:650px
-
當鼠標在第3個li上的時候,li下標index為2:
- index:0 left:0
- index:1 left:50px
- index:2 left:100px
- index:3 left:600px
- index:4 left:650px
看出規律了嗎?
- 當對應li的下標<=鼠標懸停的的下標上的時候left值 是50*i
- 當對應li的下標>鼠標懸停的的下標上的時候left值 是50*i + ,450(450不是固定的值,是經過計算出來的)
示例代碼: 29-手風琴-定位版.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}#box {width: 700px;height: 440px;margin: 100px auto;position: relative;overflow: hidden;box-sizing: border-box;border-radius: 30px;}li {width: 700px;height: 440px;position: absolute;/* background: yellow; */} </style><!-- html 部分 --> <div id="box"><ul><li></li><li></li><li></li><li></li><li></li></ul> </div><!-- js 部分 --> <script src="../js/slow-animate-styles.js"></script> <script>var box = document.getElementById('box');var lis = box.getElementsByTagName('li');for (var i = 0; i < lis.length; i++) {lis[i].index = i;// 動態添加li的背景圖片 因為i下標從0開始,但是圖片序號是從1開始 所以jia1lis[i].style.backgroundImage = "url(../image/手風琴/" + (i + 1) + ".png)";// 現在都疊在一起,設置left 讓他們分開來 700/5 ==> 140pxlis[i].style.left = 140 * i + "px";// 注冊鼠標經過事件,讓當前的顯示寬度為500,其余的為50lis[i].onmouseover = function() {for (var i = 0; i < lis.length; i++) {// 判斷當i小于等于當前鼠標停留的下標的時候,給li的left設置 50*iif (i <= this.index) {slowAnimateStyles(lis[i], {left: 50 * i});// 當i大于當前鼠標停留的索引的時候,給后邊的li的left設置 50*i + 450 } else {slowAnimateStyles(lis[i], {left: 50 * i + 450});}}}// 注冊鼠標離開事件,讓所有的li都恢復到最初的樣式lis[i].onmouseout = function() {for (var i = 0; i < lis.length; i++) {slowAnimateStyles(lis[i], {left: 140 * i});}}} </script>效果圖:
8.旋轉木馬案例
旋轉木馬也叫旋轉輪播圖,在效果上它就是旋轉版的輪播圖,但是在實現原理上卻一點一不一樣旋轉木馬原理:
- 利用ul、li方式將圖片包裹在li里,并且對每個li的大小、層級、不透明度以及定位的位置設置好
- 樣式上可能比較繁瑣,我們將上面的每個參數再以對象的方式存到數組datas中
- 之前封裝過一個緩動動畫函數,可以改變層級和不透明度,這里正好用得到
- 其實拋開上面樣式上的細節,旋轉木馬最核心的就是運用到幾個數組常用的方法 pop、unshift、shift、push
- 點擊右按鈕的時候,將datas里的最后一項利用pop刪除掉,并且返回這個刪除的數據,再將這個數據unshift到數組的最前面。重新遍歷數組,執行一遍動畫
- 點擊左箭頭的時候,將datas里的最前面一項利用shift刪除掉,并且返回這個刪除的數據,再將這個數據push到數組的最后面。重新遍歷數組,執行一遍動畫
- 再給按鈕添加一個節流閥,沒加之前不停地點擊按鈕,圖片就會不停切換,加上之后,點一次執行完才可以再次點擊。
示例代碼: [30-旋轉木馬輪播圖案例.html]
<!-- 樣式部分 --> <style>* {margin: 0;padding: 0;list-style: none;}body {background: #666;}.wrap {width: 1200px;margin: 200px auto;}.slide {height: 340px;position: relative;}.slide li {position: absolute;left: 300px;top: 0;}img {width: 100%;}.arrow {opacity: 0;position: relative;z-index: 99;top: 50%;}.arrow #left,.arrow #right {width: 40px;height: 90px;position: absolute;top: 50%;margin-top: -45px;background: url(../image/旋轉木馬/left.png);background-size: cover;z-index: 99;}.arrow #right {right: 0;background: url(../image/旋轉木馬/right.png);background-size: cover;} </style><!-- html 部分 --> <div class="wrap" id="wrap"><div class="slide" id="slide"><ul><li><img src="../image/1.jpg" alt=""></li><li><img src="../image/2.jpg" alt=""></li><li><img src="../image/3.jpg" alt=""></li><li><img src="../image/4.jpg" alt=""></li><li><img src="../image/5.jpg" alt=""></li></ul><div class="arrow" id="arrow"><a href="javascript:;"><span id="left"></span></a><a href="javascript:;"><span id="right"></span></a></div></div> </div><!-- js 部分 --> <script src="../js/slow-animate-styles.js"> </script> <script>// 將其余四張位置與透明度等信息,存放在一個數組中var datas = [{"width": 300,"top": -20,"left": 150,"opacity": 20,"zIndex": 2}, //0{"width": 500,"top": 30,"left": 50,"opacity": 80,"zIndex": 3}, //1{"width": 600,"top": 100,"left": 300,"opacity": 100,"zIndex": 4}, //2{"width": 500,"top": 30,"left": 650,"opacity": 80,"zIndex": 3}, //3{"width": 300,"top": -20,"left": 750,"opacity": 20,"zIndex": 2} //4];var slide = document.getElementById('slide');var lis = slide.getElementsByTagName('li');var arrow = document.getElementById('arrow');var left = document.getElementById('left');var right = document.getElementById('right');// 定義一個節流閥var flag = true;// 一開始頁面刷新的時候,將datas里的數據 動態添加進去for (var i = 0; i < lis.length; i++) {slowAnimateStyles(lis[i], datas[i]);};// 鼠標經過的時候 箭頭顯示slide.onmouseover = function() {slowAnimateStyles(arrow, {opacity: 100})};// 鼠標離開的時候 箭頭隱藏slide.onmouseout = function() {slowAnimateStyles(arrow, {opacity: 0})};// 點擊右箭頭的時候// 利用數組的pop 和 unshift方法對數組datas進行操作// pop 會刪除數組的最后一項,并且返回這一項。 unshift 會在數組的最前添加right.onclick = function() {// 只有節流閥為true的時候 點擊才會執行里面的代碼if (flag) {// 電擊后一進來就將節流閥關上,再次點擊的時候就不會進來flag = false;datas.unshift(datas.pop());for (var i = 0; i < lis.length; i++) {// 點擊一次就要動畫渲染一次,datas[i] 其實是一個對象/*{"width": 300,"top": -20,"left": 150,"opacity": 20,"zIndex": 2}*/slowAnimateStyles(lis[i], datas[i], function() {// 當動畫執行完,也就是回調函數觸發的時候,再將節流閥打開,這樣就可以繼續點擊了flag = true;});}}}// 點擊左箭頭// 利用數組的 shift 和 push方法對數組datas進行操作// shift 會刪除數組的第一項,并且返回這一項。 push 會在數組的最后添加left.onclick = function() {if (flag) {flag = false;datas.push(datas.shift());for (var i = 0; i < lis.length; i++) {slowAnimateStyles(lis[i], datas[i], function() {flag = true;});}}} </script>效果圖:
上一篇:JavaScript 基礎知識 - BOM篇
下一篇:JavaScript 進階知識 - 特效篇(二)
總結
以上是生活随笔為你收集整理的JavaScript 进阶知识 - 特效篇(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS3-3D转换
- 下一篇: ER图连接线上加个圆圈什么意思_你们要的