效果:效果是当鼠标移入的时候小图出现一小块是以鼠标为中心的遮罩层,鼠标在小图移动时大图放大显示遮罩层所在的局部,且遮罩层不能超出小图位置。如下图所示:

html部分
从代码可知大图和小图是分别两个img标签图片其实也是一样的,只不过尺寸不一样。
<body><div class="box"><!-- 小图 --><div class="left"><div class="shadow"></div><img src="./01.jpg" alt="" srcset=""></div><!-- 大图 --><div class="right"><img class="rightImg" src="./03.jpg" alt="" srcset=""></div></div>
</body>
css部分
pointer-events: none:性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件的 target。none就是表示就是让鼠标事件失效(链接、点击等事件)。
大图部分的是一个div套着img,img的大小要大于div盒子,然后用超出隐藏,这样的结构就可以跟据鼠标在小图位置然后通过设置offsetleft和offsettop来移动大图的位置看到局部放大的图片了
<style>.box {margin-top: 200px;width: 800px;height: 600px;margin-left: 200px;display: flex;}.left {position: relative;width: 150px;height: 150px;border: 1px solid red;cursor: crosshair;}.left img {width: 100%;height: 100%;}.right {height: 300px;width: 300px;overflow: hidden;display: none;}.shadow {width: 60px;height: 60px;background-color: pink;opacity: 0.5;display: none;left: 0;top: 0;position: absolute;z-index: 999;pointer-events: none;}* {margin: 0;}</style>
js部分
通过计算鼠标在图片的位置,就需要先了解offsetWidth、offsetHeight、clientHeight 、clientWidth、offsetX 、offsetY 。
offsetHeight是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。
offsetWidth是一个只读属性,返回一个元素的布局宽度。一个典型的(译者注:各浏览器的offsetWidth可能有所不同)offsetWidth是测量包含元素的边框(border)、水平线上的内边距(padding)、竖直方向滚动条(scrollbar)(如果存在的话)、以及CSS设置的宽度(width)的值。
clientWidth属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。
clientHeight这个属性是只读属性,对于没有定义CSS或者内联布局盒子的元素为0,否则,它是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距。
offsetX:MouseEvent 接口的只读属性 offsetX 规定了事件对象与目标节点的内填充边(padding edge)在 X 轴方向上的偏移量。
<script>window.onload = function() {// 阴影盒子const shadow = document.getElementsByClassName('shadow')[0];const left = document.getElementsByClassName('left')[0];const right = document.getElementsByClassName('right')[0];const rightImg = document.getElementsByClassName('rightImg')[0];// 阴影盒子宽度let shadowWidth = 0let shadowHeight = 0let leftWidth = 0let leftHeight = 0let nowX = 0;let nowY = 0;// 移入时触发,在这里实时计算鼠标的位置left.addEventListener('mousemove', function(e) {// 阴影部分位置偏移量// 减去自身的一半是为了让鼠标在阴影部分正中间,不减去一半的话就是鼠标在阴影的左上角了// nowX = e.offsetX - (shadowWidth/2);// nowY = e.offsetY - (shadowHeight/2);nowX = e.clientX - left.offsetLeft - (shadowWidth/2);nowY = e.clientY - left.offsetTop - (shadowHeight/2);// 超出监听移动事件的左边if (nowX < 0) {nowX = 0// 超出监听移动事件的右边// 鼠标相对于事件对象的x坐标 > 事件对象的宽度 - 阴影盒子宽度的一半} else if (e.offsetX > (leftWidth - (shadowWidth/2))) {// 最大偏移量 = 事件对象的宽度 - 阴影盒子的宽度nowX = leftWidth - shadowWidth}// 超出监听移动事件的上边if (nowY < 0) {nowY = 0// 超出监听移动事件的下边// 鼠标相对于事件对象的y坐标 > 事件对象的高度 - 阴影盒子高度的一半} else if (e.offsetY > (leftHeight - (shadowHeight/2))) {// 最大偏移量 = 事件对象的高度 - 阴影盒子的高度nowY = leftHeight - shadowHeight}shadow.style.left = nowX + 'px'shadow.style.top = nowY + 'px'console.log('now:',nowX,nowY);// scrollLeft 读取或设置元素滚动条到元素左边的距离。// clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。// 在左侧小图移动鼠标后右边大图按比例跟随滚动距离 = 当前左边小图的left / 最大偏移量 * (右侧大图图片的内容宽度 - 右侧大图图片的父元素的内容宽度)// right.scrollLeft = nowX / (leftWidth - shadowWidth) * (rightImg.clientWidth - right.clientWidth)// right.scrollTop = nowY / (leftHeight - shadowHeight) * (rightImg.clientHeight - right.clientHeight)// 在左侧小图移动鼠标后右边大图按比例跟随滚动距离 = 当前小图的left * (右边大图的元素的内容宽度 / 左边小图的元素的内容宽度)right.scrollLeft = nowX * (rightImg.clientWidth / leftWidth)right.scrollTop = nowY * (rightImg.clientHeight / leftHeight)})// 从外部移入left时触发时,只会触发一次,所以可以获取一些不会改变的值。默认隐藏移入后显示left.addEventListener('mouseenter', function(e) {shadow.style.display = 'block';right.style.display = 'block';shadowWidth = shadow.offsetWidthshadowHeight = shadow.offsetHeightleftWidth = left.offsetWidthleftHeight = left.offsetHeight})// 离开时触发,离开后隐藏left.addEventListener('mouseleave', function() {shadow.style.display = 'none'right.style.display = 'none'})}
</script>
完整代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>图片放大镜</title><style>.box {margin-top: 200px;width: 800px;height: 600px;margin-left: 200px;display: flex;}.left {position: relative;width: 150px;height: 150px;border: 1px solid red;cursor: crosshair;}.left img {width: 100%;height: 100%;}.right {height: 300px;width: 300px;/* 关键 */overflow: hidden;display: none;}.shadow {width: 60px;height: 60px;background-color: pink;opacity: 0.5;display: none;left: 0;top: 0;position: absolute;z-index: 999;/* 性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件的 target。none就是表示就是让鼠标事件失效(链接、点击等事件)。 *//* pointer-events: none; */}* {margin: 0;}</style>
</head>
<body><div class="box"><div class="left"><div class="shadow"></div><img src="./01.jpg" alt="" srcset=""></div><div class="right"><img class="rightImg" src="./03.jpg" alt="" srcset=""></div></div>
</body>
</html>
<script>window.onload = function() {const shadow = document.getElementsByClassName('shadow')[0];const left = document.getElementsByClassName('left')[0];const right = document.getElementsByClassName('right')[0];const rightImg = document.getElementsByClassName('rightImg')[0];let shadowWidth = 0let shadowHeight = 0let leftWidth = 0let leftHeight = 0let nowX = 0;let nowY = 0;left.addEventListener('mousemove', function(e) {// 使用offsetX的计算需要配合css属性pointer-events: none;服用// nowX = e.offsetX - (shadowWidth/2);// nowY = e.offsetY - (shadowHeight/2);// 或者使用这种方法nowX = e.clientX - left.offsetLeft - (shadowWidth/2);nowY = e.clientY - left.offsetTop - (shadowHeight/2);// 超出监听移动事件的左边if (nowX < 0) {nowX = 0// 超出监听移动事件的右边// 鼠标相对于事件对象的x坐标 > 事件对象的宽度 - 阴影盒子宽度的一半} else if (e.offsetX > (leftWidth - (shadowWidth/2))) {// 最大偏移量 = 事件对象的宽度 - 阴影盒子的宽度nowX = leftWidth - shadowWidth}// 超出监听移动事件的上边if (nowY < 0) {nowY = 0// 超出监听移动事件的下边// 鼠标相对于事件对象的y坐标 > 事件对象的高度 - 阴影盒子高度的一半} else if (e.offsetY > (leftHeight - (shadowHeight/2))) {// 最大偏移量 = 事件对象的高度 - 阴影盒子的高度nowY = leftHeight - shadowHeight}shadow.style.left = nowX + 'px'shadow.style.top = nowY + 'px'console.log('now:',nowX,nowY);// scrollLeft 读取或设置元素滚动条到元素左边的距离。// clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。// 在左侧小图移动鼠标后右边大图按比例跟随滚动距离 = 当前左边小图的left / 最大偏移量 * (右侧大图图片的内容宽度 - 右侧大图图片的父元素的内容宽度)// right.scrollLeft = nowX / (leftWidth - shadowWidth) * (rightImg.clientWidth - right.clientWidth)// right.scrollTop = nowY / (leftHeight - shadowHeight) * (rightImg.clientHeight - right.clientHeight)// 在左侧小图移动鼠标后右边大图按比例跟随滚动距离 = 当前小图的left * (右边大图的元素的内容宽度 / 左边小图的元素的内容宽度)right.scrollLeft = nowX * (rightImg.clientWidth / leftWidth)right.scrollTop = nowY * (rightImg.clientHeight / leftHeight)})left.addEventListener('mouseenter', function(e) {shadow.style.display = 'block';right.style.display = 'block';shadowWidth = shadow.offsetWidthshadowHeight = shadow.offsetHeightleftWidth = left.offsetWidthleftHeight = left.offsetHeight})left.addEventListener('mouseleave', function() {shadow.style.display = 'none'right.style.display = 'none'})}
</script>
在使用offsetX、offsetY时遇到一个问题,如下图:

鼠标移动时会一直闪,卡了好久终于知道了原因。罪魁祸首就是就是offsetX、offsetY了。我在mousemove事件(第一二行被注释的代码)中是使用offsetX、offsetY计算偏移量的。上面说到offsetX是事件对象与目标节点的内填充边(padding edge)在 X 轴方向上的偏移量。因为我们的事件对象不断地在切换(刚开始的时候事件对象在left的元素,阴影部分在鼠标上时,事件对象有变成了阴影的这个元素)而offsetX是事件对象到鼠标的位置。所以当事件对象是阴影盒子时offsetX的值时比较小的,所以计算后会跑到左上角,但是此时事件对象又在left元素上了,然后此时计算后又回到了鼠标位置,循环反复。刚开始我想这我明明是给left元素绑定的事件怎么还作用的阴影盒子上了呢,所以我一通操作,我以为是冒泡捕获机制导致的所以添加了下面一段阻止冒泡捕获,随便也阻止了默认事件,但是并没有效果。后来发现使用pointer-events: none就完美解决了。或者使用clientX进行计算也可以
















