前言
JS动画的主要内容如下:
1、三大家族和一个事件对象:
2、动画(闪现/匀速/缓动)
3、冒泡/兼容/封装
4、正则
offset 家族的组成
我们知道,三大家族包括:offset/scroll/client。今天来讲一下offset,以及与其相关的匀速动画。
offset的中文是:偏移,补偿,位移。
js中有一套方便的获取元素尺寸的办法就是offset家族。offset家族包括:
-
offsetWidth
-
offsetHight
-
offsetLeft
-
offsetTop
-
offsetParent
下面分别介绍。
1、offsetWidth 和 offsetHight
用于检测盒子自身的宽高+padding+border,不包括margin。如下:
这两个属性,他们绑定在了所有的节点元素上。获取之后,只要调用这两个属性,我们就能够获取元素节点的宽和高。
举例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <!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); console.log(typeof div1.offsetHeight);
</script> </body> </html>
|
2、offsetLeft 和 offsetTop
返回距离上级盒子(带有定位)左边的位置;如果父级都没有定位,则以body为准。
offsetLeft: 从父亲的 padding 开始算,父亲的 border 不算。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <!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; } </style> </head> <body> <div class="box1"> <div class="box2" style="left: 10px"></div> </div>
<script>
var box2 = document.getElementsByClassName("box2")[0];
console.log(box2.offsetLeft); console.log(box2.style.left);
</script>
</body> </html>
|
在父盒子有定位的情况下,offsetLeft == style.left(去掉px之后)。注意,后者只识别行内样式。但区别不仅仅于此,后面会讲。
3、offsetParent
检测父系盒子中带有定位的父盒子节点。返回结果是该对象的父级(带有定位)。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <!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>
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。
比如:
1 2 3 4
| 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 赋值(比较方便)。理由如下:
动画的种类
-
闪现(基本不用)
-
匀速(本文重点)
-
缓动(后续重点)
简单举例如下:(每间隔500ms,向右移动盒子100px)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!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];
btn.onclick = function () { setInterval(function () { console.log(parseInt(div.style.left)); div.style.left = div.offsetLeft + 100 + "px";
}, 500); } </script> </body> </html>
|
效果如下:
匀速动画的封装:每间隔30ms,移动盒子10px【重要】
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| <!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); }
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); } }, 30) } </script> </body> </html>
|
实现的效果:
上方代码中的方法封装,可以作为一个模板步骤,要记住。其实,这个封装的方法,写成下面这样,会更严谨,更容易理解:(将if语句进行了改进)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function animate(ele, target) { clearInterval(ele.timer); var speed = target > ele.offsetLeft ? 10 : -10; ele.timer = setInterval(function () { var val = target - ele.offsetLeft;
if (Math.abs(val) < Math.abs(speed)) { ele.style.left = target + "px"; clearInterval(ele.timer); } else { ele.style.left = ele.offsetLeft + speed + "px"; } }, 30) }
|
代码举例:轮播图的实现
完整版代码如下:(注释已经比较详细)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
| <!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 () {
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;
var ulNewLi = ul.children[0].cloneNode(true); ul.appendChild(ulNewLi); 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";
for (var i = 0; i < olLiArr.length; i++) { olLiArr[i].index = i; olLiArr[i].onmouseover = function () { for (var j = 0; j < olLiArr.length; j++) { olLiArr[j].className = ""; } this.className = "current";
key = square = this.index; animate(ul, -this.index * imgWidth); } }
var timer = setInterval(autoPlay, 1000);
var key = 0; var square = 0;
function autoPlay() { key++; if (key > olLiArr.length) { ul.style.left = 0; key = 1; } animate(ul, -key * imgWidth); square++; if (square > olLiArr.length - 1) { 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); }
spanArr[0].onclick = function () { key--; if (key < 0) { ul.style.left = -imgWidth * (olLiArr.length) + "px"; key = olLiArr.length - 1; } animate(ul, -key * imgWidth); square--; if (square < 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单独在浏览器中打开。
工程文件: