javascript
JavaScript动画:offset和匀速动画详解(含轮播图的实现)
本文最初發表于博客園,并在GitHub上持續更新前端的系列文章。歡迎在GitHub上關注我,一起入門和進階前端。
以下是正文。
offset簡介
我們知道,三大家族包括:offset/scroll/client。今天來講一下offset,以及與其相關的勻速動畫。
offset的中文是:偏移,補償,位移。
js中有一套方便的獲取元素尺寸的辦法就是offset家族。offset家族包括:
offsetWidth
offsetHight
offsetLeft
offsetTop
offsetParent
下面分別介紹。
1、offsetWidth 和 offsetHight
用于檢測盒子自身的寬高+padding+border,不包括margin。如下:
offsetWidth = width + padding + border;
offsetHeight = Height + padding + border;
這兩個屬性,他們綁定在了所有的節點元素上。獲取之后,只要調用這兩個屬性,我們就能夠獲取元素節點的寬和高。
舉例如下:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;padding: 10px;border: 10px solid #000;margin: 100px;background-color: pink;}</style> </head> <body><div class="box"></div> <script>var div1 = document.getElementsByTagName("div")[0];console.log(div1.offsetHeight); //打印結果:140(100+20+20)console.log(typeof div1.offsetHeight); //打印結果:number</script> </body> </html>2、offsetLeft 和 offsetTop
返回距離上級盒子(帶有定位)左邊的位置;如果父級都沒有定位,則以body為準。
offsetLeft: 從父親的 padding 開始算,父親的 border 不算。
舉例:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>.box1 {width: 300px;height: 300px;padding: 100px;margin: 100px;position: relative;border: 100px solid #000;background-color: pink;}.box2 {width: 100px;height: 100px;background-color: red;/*position: absolute;*//*left: 10px;*//*top: 10px;*/}</style> </head> <body> <div class="box1"><div class="box2" style="left: 10px"></div> </div><script>var box2 = document.getElementsByClassName("box2")[0];//offsetTop和offsetLeftconsole.log(box2.offsetLeft); //100console.log(box2.style.left); //10px</script></body> </html>在父盒子有定位的情況下,offsetLeft == style.left(去掉px之后)。注意,后者只識別行內樣式。但區別不僅僅于此,后面會講。
3、offsetParent
檢測父系盒子中帶有定位的父盒子節點。返回結果是該對象的父級(帶有定位)。
如果當前元素的父級元素,沒有CSS定位(position為absolute、relative、fixed),那么offsetParent的返回結果為body。
如果當前元素的父級元素,有CSS定位(position為absolute、relative、fixed),那么offsetParent的返回結果為最近的那個父級元素。
舉例:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title> </head> <body> <div class="box1" style="position: absolute;"><div class="box2" style="position: fixed;"><div class="box3"></div></div> </div> <script>//offsetParent:復習盒子中帶有定位的盒子//復習盒子中都沒有定位,返回body//如果有,誰有返回最近哪個var box3 = document.getElementsByClassName("box3")[0];console.log(box3.offsetParent); </script> </body> </html>打印結果:
offsetLeft和style.left區別
(1)最大區別在于:
offsetLeft 可以返回沒有定位盒子的距離左側的位置。如果父系盒子中都沒有定位,以body為準。
style.left 只能獲取行內式,如果沒有,則返回""(意思是,返回空);
(2)offsetTop 返回的是數字,而 style.top 返回的是字符串,而且還帶有單位:px。
比如:
div.offsetLeft = 100; div.style.left = "100px";(3)offsetLeft 和 offsetTop 只讀,而 style.left 和 style.top 可讀寫(只讀是獲取值,可寫是賦值)
(4)如果沒有給 HTML 元素指定過 top 樣式,則style.top 返回的是空字符串。
總結:我們一般的做法是:用offsetLeft 和 offsetTop 獲取值,用style.left 和 style.top 賦值(比較方便)。理由如下:
style.left:只能獲取行內式,獲取的值可能為空,容易出現NaN。
offsetLeft:獲取值特別方便,而且是現成的number,方便計算。它是只讀的,不能賦值。
動畫的種類
閃現(基本不用)
勻速(本文重點)
緩動(后續重點)
簡單舉例如下:(每間隔500ms,向右移動盒子100px)
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;}</style> </head> <body> <button>動畫</button> <div class="box" style="left: 0px"></div><script>var btn = document.getElementsByTagName("button")[0];var div = document.getElementsByTagName("div")[0];//1、閃動// btn.onclick = function () {// div.style.left = "500px";// }//2、勻速運動btn.onclick = function () {//定時器,每隔一定的時間向右走一些setInterval(function () {console.log(parseInt(div.style.left));//動畫原理: 盒子未來的位置 = 盒子現在的位置 + 步長;//用style.left賦值,用offsetLeft獲取值。div.style.left = div.offsetLeft + 100 + "px";//div.style.left = parseInt(div.style.left)+10+"px"; //NaN不能用}, 500);} </script> </body> </html>效果如下:
勻速動畫的封裝:每間隔30ms,移動盒子10px【重要】
代碼如下:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>.box1 {margin: 0;padding: 5px;height: 300px;background-color: #ddd;position: relative;}button {margin: 5px;}.box2 {width: 100px;height: 100px;background-color: red;position: absolute;left: 195px;top: 40px;}.box3 {width: 100px;height: 100px;background-color: yellow;position: absolute;left: 0;top: 150px;}</style> </head> <body> <div class="box1"><button>運動到 left = 200px</button><button>運動到 left = 400px</button><div class="box2"></div><div class="box3"></div> </div><script>var btnArr = document.getElementsByTagName("button");var box2 = document.getElementsByClassName("box2")[0];var box3 = document.getElementsByClassName("box3")[0];//綁定事件btnArr[0].onclick = function () {//如果有一天我們要傳遞另外一個盒子,那么我們的方法就不好用了//所以我們要增加第二個參數,被移動的盒子本身。animate(box2, 200);animate(box3, 200);}btnArr[1].onclick = function () {animate(box2, 400);animate(box3, 400);}//【重要】方法的封裝:每間隔30ms,將盒子向右移動10pxfunction animate(ele, target) {//要用定時器,先清除定時器//一個盒子只能有一個定時器,這樣的話,不會和其他盒子出現定時器沖突//我們可以把定時器本身,當成為盒子的一個屬性clearInterval(ele.timer);//我們要求盒子既能向前又能向后,那么我們的步長就得有正有負//目標值如果大于當前值取正,目標值如果小于當前值取負var speed = target > ele.offsetLeft ? 10 : -10; //speed指的是步長ele.timer = setInterval(function () {//在執行之前就獲取當前值和目標值之差var val = target - ele.offsetLeft;ele.style.left = ele.offsetLeft + speed + "px";//移動的過程中,如果目標值和當前值之差如果小于步長,那么就不能在前進了//因為步長有正有負,所有轉換成絕對值來比較if (Math.abs(val) < Math.abs(speed)) {ele.style.left = target + "px";clearInterval(ele.timer);}}, 30)} </script> </body> </html>實現的效果:
上方代碼中的方法封裝,可以作為一個模板步驟,要記住。其實,這個封裝的方法,寫成下面這樣,會更嚴謹,更容易理解:(將if語句進行了改進)
//【重要】方法的封裝:每間隔30ms,將盒子向右移動10pxfunction animate(ele, target) {//要用定時器,先清除定時器//一個盒子只能有一個定時器,這樣的話,不會和其他盒子出現定時器沖突//我們可以把定時器本身,當成為盒子的一個屬性clearInterval(ele.timer);//我們要求盒子既能向前又能向后,那么我們的步長就得有正有負//目標值如果大于當前值取正,目標值如果小于當前值取負var speed = target > ele.offsetLeft ? 10 : -10; //speed指的是步長ele.timer = setInterval(function () {//在執行之前就獲取當前值和目標值之差var val = target - ele.offsetLeft;//移動的過程中,如果目標值和當前值之差如果小于步長,那么就不能在前進了//因為步長有正有負,所有轉換成絕對值來比較if (Math.abs(val) < Math.abs(speed)) { //如果val小于步長,則直接到達目的地;否則,每次移動一個步長ele.style.left = target + "px";clearInterval(ele.timer);} else {ele.style.left = ele.offsetLeft + speed + "px";}}, 30)}代碼舉例:輪播圖的實現
完整版代碼如下:(注釋已經比較詳細)
<!doctype html> <html lang="en"> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>無標題文檔</title><style type="text/css">* {padding: 0;margin: 0;list-style: none;border: 0;}.all {width: 500px;height: 200px;padding: 7px;border: 1px solid #ccc;margin: 100px auto;position: relative;}.screen {width: 500px;height: 200px;overflow: hidden;position: relative;}.screen li {width: 500px;height: 200px;overflow: hidden;float: left;}.screen ul {position: absolute;left: 0;top: 0px;width: 3000px;}.all ol {position: absolute;right: 10px;bottom: 10px;line-height: 20px;text-align: center;}.all ol li {float: left;width: 20px;height: 20px;background: #fff;border: 1px solid #ccc;margin-left: 10px;cursor: pointer;}.all ol li.current {background: yellow;}#arr {display: none;}#arr span {width: 40px;height: 40px;position: absolute;left: 5px;top: 50%;margin-top: -20px;background: #000;cursor: pointer;line-height: 40px;text-align: center;font-weight: bold;font-family: '黑體';font-size: 30px;color: #fff;opacity: 0.3;border: 1px solid #fff;}#arr #right {right: 5px;left: auto;}</style><script>window.onload = function () {//需求:無縫滾動。//思路:賦值第一張圖片放到ul的最后,然后當圖片切換到第五張的時候// 直接切換第六章,再次從第一張切換到第二張的時候先瞬間切換到// 第一張圖片,然后滑動到第二張//步驟://1.獲取事件源及相關元素。(老三步)//2.復制第一張圖片所在的li,添加到ul的最后面。//3.給ol中添加li,ul中的個數-1個,并點亮第一個按鈕。//4.鼠標放到ol的li上切換圖片//5.添加定時器//6.左右切換圖片(鼠標放上去隱藏,移開顯示)//1.獲取事件源及相關元素。(老三步)var all = document.getElementById("all");var screen = all.firstElementChild || all.firstChild;var imgWidth = screen.offsetWidth;var ul = screen.firstElementChild || screen.firstChild;var ol = screen.children[1];var div = screen.lastElementChild || screen.lastChild;var spanArr = div.children;//2.復制第一張圖片所在的li,添加到ul的最后面。var ulNewLi = ul.children[0].cloneNode(true);ul.appendChild(ulNewLi);//3.給ol中添加li,ul中的個數-1個,并點亮第一個按鈕。for (var i = 0; i < ul.children.length - 1; i++) {var olNewLi = document.createElement("li");olNewLi.innerHTML = i + 1;ol.appendChild(olNewLi)}var olLiArr = ol.children;olLiArr[0].className = "current";//4.鼠標放到ol的li上切換圖片for (var i = 0; i < olLiArr.length; i++) {//自定義屬性,把索引值綁定到元素的index屬性上olLiArr[i].index = i;olLiArr[i].onmouseover = function () {//排他思想for (var j = 0; j < olLiArr.length; j++) {olLiArr[j].className = "";}this.className = "current";//鼠標放到小的方塊上的時候索引值和key以及square同步 // key = this.index; // square = this.index;key = square = this.index;//移動盒子animate(ul, -this.index * imgWidth);}}//5.添加定時器var timer = setInterval(autoPlay, 1000);//固定向右切換圖片//兩個定時器(一個記錄圖片,一個記錄小方塊)var key = 0;var square = 0;function autoPlay() {//通過控制key的自增來模擬圖片的索引值,然后移動ulkey++;if (key > olLiArr.length) {//圖片已經滑動到最后一張,接下來,跳轉到第一張,然后在滑動到第二張ul.style.left = 0;key = 1;}animate(ul, -key * imgWidth);//通過控制square的自增來模擬小方塊的索引值,然后點亮盒子//排他思想做小方塊square++;if (square > olLiArr.length - 1) {//索引值不能大于等于5,如果等于5,立刻變為0;square = 0;}for (var i = 0; i < olLiArr.length; i++) {olLiArr[i].className = "";}olLiArr[square].className = "current";}//鼠標放上去清除定時器,移開后在開啟定時器all.onmouseover = function () {div.style.display = "block";clearInterval(timer);}all.onmouseout = function () {div.style.display = "none";timer = setInterval(autoPlay, 1000);}//6.左右切換圖片(鼠標放上去顯示,移開隱藏)spanArr[0].onclick = function () {//通過控制key的自增來模擬圖片的索引值,然后移動ulkey--;if (key < 0) {//先移動到最后一張,然后key的值取之前一張的索引值,然后在向前移動ul.style.left = -imgWidth * (olLiArr.length) + "px";key = olLiArr.length - 1;}animate(ul, -key * imgWidth);//通過控制square的自增來模擬小方塊的索引值,然后點亮盒子//排他思想做小方塊square--;if (square < 0) {//索引值不能大于等于5,如果等于5,立刻變為0;square = olLiArr.length - 1;}for (var i = 0; i < olLiArr.length; i++) {olLiArr[i].className = "";}olLiArr[square].className = "current";}spanArr[1].onclick = function () {//右側的和定時器一模一樣autoPlay();}function animate(ele, target) {clearInterval(ele.timer);var speed = target > ele.offsetLeft ? 10 : -10;ele.timer = setInterval(function () {var val = target - ele.offsetLeft;ele.style.left = ele.offsetLeft + speed + "px";if (Math.abs(val) < Math.abs(speed)) {ele.style.left = target + "px";clearInterval(ele.timer);}}, 10)}}</script> </head><body> <div class="all" id='all'><div class="screen" id="screen"><ul id="ul"><li><img src="images/1.jpg" width="500" height="200"/></li><li><img src="images/2.jpg" width="500" height="200"/></li><li><img src="images/3.jpg" width="500" height="200"/></li><li><img src="images/4.jpg" width="500" height="200"/></li><li><img src="images/5.jpg" width="500" height="200"/></li></ul><ol></ol><div id="arr"><span id="left"><</span><span id="right">></span></div></div> </div> </body> </html>實現效果:
溫馨提示:動圖太大,可以把http://img.smyhvae.com/20180202_2020.gif單獨在瀏覽器中打開。
工程文件:
- 2018-02-02-JS動畫實現輪播圖.rar
我的公眾號
想學習代碼之外的軟技能?不妨關注我的微信公眾號:生命團隊(id:vitateam)。
掃一掃,你將發現另一個全新的世界,而這將是一場美麗的意外:
轉載于:https://www.cnblogs.com/qianguyihao/p/8407109.html
總結
以上是生活随笔為你收集整理的JavaScript动画:offset和匀速动画详解(含轮播图的实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL 选择数据库
- 下一篇: IOI1999 花店橱窗布置