offset相关属性和匀速动画
offset家族的组成
offset 的中文是偏移,补偿,位移。
offset相关的属性包括:
- offsetWidth
- offsetHeight
- offsetLeft
- offsetTop
- offsetParent
offsetWidth和offsetHeight
offsetWidth
和 offsetHeight
的值为:元素的宽/高 + padding + border的宽度。
<style>
.box {
box-sizing: content-box;
width: 100px;
height: 100px;
margin: 10px;
border: 10px solid #000;
padding: 10px;
}
</style>
<div class="box"></div>
<script>
const box = document.querySelector('.box');
console.log(box.offsetWidth); // 140
console.log(typeof box.offsetWidth); //number
</script>
offsetParent
offsetParent
获取的是元素的定位父元素。
如果当前元素的父元素,有CSS定位(position为absolute、relative、fixed),那么
offsetParent
获取的是最近的那个父元素。如果当前元素的父元素,没有CSS定位(position为absolute、relative、fixed),那么
offsetParent
获取的是body。
<div class="box1" style="position: absolute;">
<div class="box2" style="position: relative;">
<div class="box3"></div>
</div>
</div>
<script>
const box3 = document.querySelector('.box3');
console.log(box3.offsetParent);
</script>
输出为:
offsetLeft和offsetTop
offsetLeft
和 offsetTop
的值为:相对于元素的offsetParent
(定位父元素)的水平/垂直偏移量
备注:从父元素的 padding
开始算起,父元素的 border
不算在内。
<style>
.box1 {
position: relative;
width: 200px;
height: 200px;
border: 100px solid #000;
margin: 100px;
padding: 100px;
}
.box2 {
width: 100px;
height: 100px;
}
</style>
<div class="box1">
<div class="box2" style="left: 20px;"></div>
</div>
<script>
const box2 = document.querySelector('.box2');
console.log(box2.offsetLeft); //100
console.log(box2.style.left); //20px
</script>
如果当前元素的父元素position为relative,自己的position为absolute时,offset == style.left(去掉px)
offsetLeft 和 style.Left 的区别
- 最大区别:
offsetLeft 可以返回无定位父元素的偏移量。如果父元素中都没有定位,则body为准。
style.left 只能获取行内样式(写在标签内),如果定义在style内或在外部样式表内,则返回空字符串。
- offsetTop 返回的是数字,而 style.top 返回的是字符串,而且还带有单位:px。
- offsetLeft 和 offsetTop 只读,而 style.left 和 style.top 可读写(只读是获取值,可写是修改值)
总结:我们一般的做法是:用offsetLeft 和 offsetTop 获取值,用style.left 和 style.top 赋值(比较方便)。理由如下:
- style.left:只能获取行内式,获取的值可能为空,容易出现NaN。
- offsetLeft:获取值特别方便,而且是现成的number,方便计算。它是只读的,不能赋值。
动画的种类
- 闪现(基本不用)
- 匀速
- 缓动
简单举例如下:(每隔500ms,盒子享有移动50px)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
position: absolute;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<button>动画</button>
<div></div>
<script>
const button = document.querySelector('button');
const div = document.querySelector('div');
button.onclick = function() {
setInterval(function() {
div.style.left = div.offsetLeft + 50 + 'px';
}, 500);
};
</script>
</body>
</html>
匀速动画:每隔50ms,移动盒子10px
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.container {
position: relative;
}
.box1 {
position: absolute;
top: 30px;
left: 195px;
width: 100px;
height: 100px;
background-color: pink;
}
.box2 {
position: absolute;
top: 140px;
width: 100px;
height: 100px;
background-color: aquamarine;
}
</style>
</head>
<body>
<div class="container">
<button>移动到 left:200px</button>
<button>移动到 left:400px</button>
<div class="box1"></div>
<div class="box2"></div>
</div>
<script>
const button1 = document.getElementsByTagName('button')[0];
const button2 = document.getElementsByTagName('button')[1];
const box1 = document.getElementsByClassName('box1')[0];
const box2 = document.getElementsByClassName('box2')[0];
button1.onclick = function() {
animate(box1, 200);
animate(box2, 200);
};
button2.onclick = function() {
animate(box1, 400);
animate(box2, 400);
}
// 每隔50ms,移动10px
function animate(ele, target) {
// clearInterval(ele.timer);
// speed为步长,如果目标值大于当前值,向右移动
// 如果目标值小于当前值,向左移动
let speed = target > ele.offsetLeft ? 10 : -10;
ele.timer = setInterval(function() {
// 计算目标值和当前值的距离
let value = target - ele.offsetLeft;
// 如果计算值小于步长,直接到达目的地,并清除计时器,否则每次移动10px
if(Math.abs(value) < Math.abs(speed)) {
ele.style.left = target + 'px';
clearInterval(ele.timer);
} else {
ele.style.left = ele.offsetLeft + speed + 'px';
}
}, 50);
}
</script>
</body>
</html>
轮播图的实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
list-style: none;
}
.all {
position: relative;
width: 500px;
height: 200px;
padding: 7px;
margin: 100px auto;
border: 1px solid #ccc;
}
.screen {
position: relative;
width: 500px;
height: 200px;
overflow: hidden;
}
.screen ul {
position: absolute;
left: 0;
top: 0;
width: 3000px;
}
.screen li {
float: left;
overflow: hidden;
width: 500px;
height: 200px;
}
.screen ol {
position: absolute;
right: 10px;
bottom: 10px;
line-height: 20px;
}
.screen ol li {
float: left;
width: 20px;
height: 20px;
border: 1px solid #ccc;
margin-left: 10px;
text-align: center;
background-color: #fff;
cursor: pointer;
}
.screen ol li.current {
background-color: yellow;
}
#arr {
display: none;
}
#arr span {
position: absolute;
width: 40px;
height: 40px;
left: 5px;
top: 50%;
margin-top: -20px;
background-color: #000;
opacity: 0.3;
border: 1px solid #fff;
text-align: center;
color: #fff;
font-size: 30px;
cursor: pointer;
}
#arr #right {
right: 5px;
left: auto;
}
</style>
</head>
<body>
<div id="all" class="all">
<div class="screen">
<ul>
<li><img src="image/1.jpg" alt="pic1" width="500" height="200"></li>
<li><img src="image/2.jpg" alt="pic1" width="500" height="200"></li>
<li><img src="image/3.jpeg" alt="pic1" width="500" height="200"></li>
<li><img src="image/4.jpeg" alt="pic1" width="500" height="200"></li>
</ul>
<ol></ol>
<div id="arr">
<span id="left"><</span>
<span id="right">></span>
</div>
</div>
</div>
<script>
window.onload = function() {
// 获取需要DOM操作的节点
const all = document.getElementById('all');
const screen = all.firstElementChild || all.firstChild;
const imgWidth = screen.offsetWidth;
const ul = screen.firstElementChild || screen.firstChild;
const ol = screen.children[1];
const spanArr = screen.lastElementChild || screen.lastChild;
// 复制ul中的第一个li节点,添加到ul的最后面
const cloneLi = ul.children[0].cloneNode(true);
ul.appendChild(cloneLi);
// 有多少张图片就在ol中添加多少个li
// 注意要减掉1,因为第一张和最后一张是相同的
// 最后,点亮第一li
for(let i = 0; i < ul.children.length - 1; i++) {
let ele = document.createElement('li');
ele.innerText = i + 1;
ol.appendChild(ele);
}
const olChildren = ol.children;
olChildren[0].className = 'current';
// 鼠标放到ol的li上切换图片
for(let i = 0; i < olChildren.length; i++) {
olChildren[i].index = i;
olChildren[i].onmouseover = function() {
// 去掉所有li上的类名
for(let i = 0; i < olChildren.length; i++) {
olChildren[i].className = '';
}
// 点亮当前li
this.className = 'current';
key = square = this.index;
animate(ul, -this.index * imgWidth);
}
}
// 两个计数器,第一个记录图片,第二个记录小方块
let key = 0;
let square = 0;
// 添加一个定时器
let timer = setInterval(autoPlay, 1000);
// 鼠标放上去清除定时器,移开后开启定时器
all.onmouseover = function() {
spanArr.style.display = 'block';
clearInterval(timer);
};
all.onmouseout = function() {
spanArr.style.display = 'none';
timer = setInterval(autoPlay, 1000);
}
spanArr.children[0].onclick = function() {
key--;
// 如果是第一张图片,先跳转到最后一张图片(第一张和最后一张是相同的图片),
// 把key置为length - 1,也就是倒数第二张图片的key
// 然后animate滑动到倒数第二张
if(key < 0) {
ul.style.left = -imgWidth * (olChildren.length) + 'px';
key = olChildren.length - 1;
}
animate(ul, -key * imgWidth);
square--;
// 如果是第一张图片,square--后变为-1,重新置为length - 1
if(square < 0) {
square = olChildren.length - 1;
}
for(let i = 0; i < olChildren.length; i++) {
olChildren[i].className = '';
}
olChildren[square].className = 'current';
};
// 点击向右的箭头相当于执行一次autoPlay
spanArr.children[1].onclick = function() {
autoPlay();
};
function autoPlay() {
key++;
// 到了最后一张图片,跳转到第一张,把key置为1
// 然后下面的animate滑动到第二张
if(key > olChildren.length) {
ul.style.left = '0px';
key = 1;
}
animate(ul, -key * imgWidth);
square++;
// 等于5的时候变为0
if(square > olChildren.length - 1) {
square = 0;
}
for(let i = 0; i < olChildren.length; i++) {
olChildren[i].className = '';
}
olChildren[square].className = 'current';
}
function animate(ele, target) {
clearInterval(ele.timer);
let speed = target > ele.offsetLeft ? 10 : -10;
ele.timer = setInterval(function() {
let value = target - ele.offsetLeft;
if(Math.abs(value) < Math.abs(speed)) {
ele.style.left = target + 'px';
clearInterval(ele.timer);
} else {
ele.style.left = ele.offsetLeft + speed + 'px';
}
}, 10);
}
}
</script>
</body>
</html>
实现效果: