鼠标跟随、DOM二级事件
一、鼠标跟随
- 实现一个跟随鼠标移动的效果
- 思路:
- 页面中的盒子并不会自己跟着鼠标移动,所谓的鼠标跟随就是在鼠标移动的时候,不断的计算鼠标所处的位置,然后把盒子的位置设置到鼠标所处的位置上即可;
- 很显然是鼠标移动的时候,所以需要监听鼠标移动事件,因为盒子在整个页面中所以需要监听document的鼠标移动事件;
- 在事件触发时计算鼠标的位置,把元素设置到鼠标的位置
let box = document.querySelector('#box');
document.onmousemove = function (e) {
// 1. 获取鼠标的位置
let {
clientX,
clientY
} = e;
console.log('move', clientX, clientY);
// 2. 计算盒子的位置,并设置给元素
box.style.left = clientX - box.offsetWidth / 2 + 'px';
box.style.top = clientY - box.offsetHeight / 2 + 'px';
};
二、限制边界的鼠标跟随
- 前面已经实现了鼠标跟随,但是鼠标可以画出屏幕,现在要求限制盒子在屏幕的可是区域内;
- 思路:
- 盒子的最大边界即盒子的left和top的最大值,left的最大值 = 浏览器可视窗口的宽度 - 盒子的宽度;top的最大值 = 浏览器的可视窗口的高度 - 盒子的高度;
- 盒子边最小边界即left=0,top=0的位置
function win(attr) {
return document.documentElement[attr] || document.body[attr];
}
let box = document.querySelector('#box');
let minL = 0;
let minT = 0;
document.onmousemove = function (e) {
// 1. 获取鼠标的位置
let {
clientX,
clientY
} = e;
// 2. 获取盒子的offsetWidth/offsetHeight
let {
offsetWidth,
offsetHeight
} = box;
console.log('move', clientX, clientY);
// 3. 求盒子的最大边界值
let maxL = win('clientWidth') - box.offsetWidth;
let maxT = win('clientHeight') - box.offsetHeight;
// 4. 根据鼠标位置求得盒子应该所处的位置
let l = clientX - offsetWidth / 2;
let t = clientY - offsetHeight / 2;
// 5. 在设置之前对求出的l和t进行修正
// left的边界判断
if (l < minL) {
l = minL;
}
if (l > maxL) {
l = maxL;
}
// top的边界判断
if (t < minT) {
t = minT;
}
if (t > maxT) {
t = maxT;
}
// 6. 把修正之后的值设置给元素对象
box.style.left = l + 'px';
box.style.top = t + 'px';
};
三、电商放大镜
- 页面中有两个等大的盒子,box1中放着原图和一个可以跟随鼠标移动的小盒子mask,mask相对于box1相对定位;
- 另一个box2放着大图,大图相对于盒子绝对定位;且大盒子overflow:hidden; 大图片的尺寸等于box1的尺寸乘以想放大的倍数,如本例中放大三倍,box1宽高300,所以大图宽高900;
- mask 的尺寸 box1的尺寸 = box2的尺寸 大图的尺寸
- 起初box2和mask都是隐藏的,当鼠标划入box1时,设置mask和box2的display: block;
- 接着在box1中移动鼠标,实现带边界的鼠标跟随;
- 因为mask占box1的比例和box2占大图的比例是相同的。但是大图的尺寸是box2的3倍,为了保证看到的是相同的地方,box2中图片的left和top的移动距离,应该是mask在box1中移动的3倍(放大三倍)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
.box1, .box2 {
position: relative;
float: left;
width: 300px;
height: 300px;
border: 1px solid red;
margin: 50px;
overflow: hidden;
}
.box1 img {
width: 100%;
height: 100%;
}
.box2 img {
position: absolute;
left: 0;
top: 0;
width: 900px;
height: 900px;
}
.mask {
display: none;
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 100px;
background: rgba(0, 0, 0, .5);
cursor: move;
}
.box2 {
display: none;
}
</style>
</head>
<body>
<div id="box1" class="box1">
<img src="./iphone.jpg" alt="">
<div class="mask" id="mask" style="width: 100px;height: 100px"></div>
</div>
<div class="box2" id="box2">
<img src="./iphone.jpg" alt="" id="bigImg">
</div>
<script src="js/5-放大镜.js"></script>
</body>
</html>
let $ = sltcr => document.querySelector(sltcr);
// 1. 获取元素对象
let box1 = $('#box1');
let box2 = $('#box2');
let mask = $('#mask');
let bigImg = $('#bigImg');
// 2. 当鼠标划入时把mask和box2显示出来
box1.onmouseenter = function () {
box2.style.display = mask.style.display = 'block';
};
box1.onmouseleave = function () {
box2.style.display = mask.style.display = 'none';
};
// 计算mask能够在box1中移动的边界
let {
offsetLeft,
offsetTop,
clientWidth,
clientHeight
} = box1;
let {
width,
height
} = mask.style;
let maxL = clientHeight - parseFloat(width);
let maxT = clientHeight - parseFloat(height);
// 2. 监听box1的鼠标移动事件
box1.onmousemove = function (e) {
// 2.1 获取现在鼠标的位置并计算盒子应该出现的位置
let l = e.clientX - offsetLeft - parseFloat(width) / 2;
let t = e.clientY - offsetTop - parseFloat(height) / 2;
// 2.2 根据边界修正l和t
if (t < 0) {
t = 0;
}
if (t > maxT) {
t = maxT;
}
if (l < 0) {
l = 0;
}
if (l > maxL) {
l = maxL;
}
// 2.3 把l和t设置给mask实现mask鼠标跟随跟随
mask.style.left = l + 'px';
mask.style.top = t + 'px';
// 2.4 设置大图left和top的值为mask移动距离的3倍
bigImg.style.left = -l * 3 + 'px';
bigImg.style.top = -t * 3 + 'px';
};
四、DOM2级事件
- JS中的事件分为DOM0级事件和DOM2级事件;
DOM0级事件
- DOM0级事件绑定
box.onclick = function () {};
box.onclick = function () {
console.log('1')
};
box.onclick = function () {
console.log('2');
};
- DOM0级事件移除
box.onclick = null;
- 用DOM0级事件的方式绑定,只能绑定一个事件,因为DOM0级事件是DOM元素对象的一个属性,多次赋值,这个属性只能保存的是最后一次被赋的值;
- DOM0级事件都是绑定在冒泡阶段的;
DOM2级事件:
- 可以为同一事件绑定多个事件函数;
- 可以指定绑定的在捕获阶段还是绑定再冒泡阶段;
- DOM2级事件绑定:
元素.addEventListener('事件名称', 事件函数, false冒泡 | true捕获)
box.addEventListener('click', function () {
console.log(1)
}, true);
box.addEventListener('click', function () {
console.log(2)
}, false);
box.addEventListener('click', function () {
console.log(3);
}, false);
- 移除DOM2级事件
元素.removeEventListener('事件名称', 事件函数, false|true);
DOM0级事件和DOM2级事件绑定区别:
- DOM0级事件只有冒泡阶段,DOM2级事件的第三个参数false表示冒泡,true表示捕获
- DOM0级事件只能为一个事件绑定一个事件函数,而DOM2级事件可以绑定多个事件函数;
DOM2事件为啥可以绑定多个事件呢?
DOM2级事件给在绑定的时候是给每个元素的每个事件准备了一个事件池,类似一个数组,每次addEventListener()就是把事件函数放到事件池中,然后等事件触发的时候,再一个一个的把事件函数从事件池中拿出来执行一次;
- IE低版本DOM2级事件:
- 绑定事件:元素.attachEvent(‘onclick’, callback);
- 移除事件:元素.detachEvent(‘onclick’, callback);
- 而且IE的DOM2级事件只能绑定在冒泡阶段,如果绑定多个,事件触发时,事件函数的执行顺序和绑定顺序无关;