类似淘宝商品放大镜功能,以及offsetX、offsetY造成的鼠标移动时阴影部分会一闪一闪的不断回到左上角问题

article/2025/11/8 22:30:15

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

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进行计算也可以


http://chatgpt.dhexx.cn/article/YaMt1YPe.shtml

相关文章

event.offsetX event.pageX event.clientX 和 obj.offsetLeft学习笔记

一、问题描述 工作中&#xff0c;需要在航拍图中 添加摄像头在航拍图中的位置&#xff0c;因此&#xff0c;需要开发一个功能&#xff1a;鼠标点击航拍图&#xff08;背景&#xff09;&#xff0c;显示鼠标点击位置在页面中的位置&#xff08;pageX和pageY&#xff09;,然后将…

html什么代码确定x坐标,HTML MouseEvent offsetX用法及代码示例

MouseEvent offsetX属性是一个只读属性&#xff0c;用于返回鼠标指针相对于目标元素的x坐标。 用法: event.offsetX 返回值&#xff1a;它返回一个数字&#xff0c;该数字表示鼠标指针的水平坐标&#xff0c;以像素为单位。 以下示例程序旨在说明MouseEvent offsetX属性&#x…

写轮播图时,关于offsetX和pageX的选择以及一些坑

一、不要使用offsetX 前几天在公司实习&#xff0c;由于使用了swiper来做c端的滑动效果&#xff0c;在自定义的过程中&#xff0c;出现了一系列的坑&#xff0c;我看了源码&#xff0c;看了文档&#xff0c;也还是没有很好的理解&#xff0c;这个swiper的标准操作流程&#xff…

js e.offsetX 和 e.offsetY

本人自己在写代码的过程中&#xff0c;一直对e.offsetX 与 e.offsetY 分不清&#xff0c;今天好好探究一下e.offsetX 与 e.offsetY e.offsetX 与 e.offsetY 下相对与事件源的距离&#xff0c;也就是距离e.target的距离&#xff0c; 大家看下面例子,给出几张截图。 <!DOCTY…

html5 offsetx,原生HTML5关于Div对象的.clientLeft、.offsetLeft、.clientX、.offsetX区分

本篇主要介绍clientLeft、offsetLeft、clientX、offsetX这四种元素属性的区别&#xff0c;首先我们要理解清楚它们的概念&#xff1a; clientLeft&#xff1a;该元素对象的左边框宽度。 clientWidth&#xff1a;该元素对象的左内边框至右内边框的距离。 offsetLeft&#xff1a;…

html5 offsetx,event对象中offsetX,clientX,pageX,screenX的区别

1、offsetX offset意为偏移量,是事件对象距左上角为参考原点的距离。以元素盒子模型的内容区域的左上角为参考点。不包括border。 2、clientX 事件对象相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条 3、pageX 事件对象相对于整个文档的坐标以像素…

彻底搞懂 offsetX、scrollX、clientX 的区别

无论在 iOS 还是前端开发中&#xff0c;关于如何定位一个元素是必须要掌握的知识&#xff0c;而在前端中&#xff0c;元素定位比较难理解&#xff0c;我们今天一起学习下。 在 DOM 设计中&#xff0c;主要通过这些 API 来确定某个元素的具体位置。 offsetTop, offsetLeft, offs…

Java数组赋值时内存中的变化

java中的方法区存放的是编译后的文件 xxx.class文件当创建数组对象时&#xff0c;数组对象会存放在堆里面&#xff0c;数据也存在于堆。当给数组赋值时&#xff0c;方法会进栈&#xff0c;然后拿着数组的地址去堆里面寻找数据并赋值

Java数组:用fill()方法给数组赋值

Arrays类可以在指定位置进行数值填充&#xff0c;但是只能使用同一个数值进行填充&#xff1a; Arrays.fill(Object[] a,Object value);a表示数组&#xff0c;value表示填充的值 例1 public static void main(String[] args) {int[] a new int[6];//声明创建一个数组System.o…

Java数组练习--数组随机赋值

随机数生成&#xff1a;使用Math.random()方法&#xff0c;即&#xff1a; [ 0 , 1 ) → [ 0 , 1 ) 30 → [0 ,30) [0 ,30) 1 → [1,31) 取整后范围是[1,30] 此题目要点是&#xff0c;赋值之后的数组元素&#xff0c;要与之前赋值元素的值比较&#xff…

java 数组的创建 与 赋值

1.2.2 数组类型 基本类型的数组有3种赋值形式&#xff0c;如下所示&#xff1a; 第1种和第2种都是预先知道数组的内容&#xff0c;而第3种是先分配长度&#xff0c; 然后再给每个元素赋值。第3种形式中&#xff0c;即使没有给每个元素赋值&#xff0c;每个元素也都有一个默认值…

java 数组批量赋值_「数组赋值」java编程-定义数组并赋值 - seo实验室

数组赋值 package day03; public class TestArray { public static void main(String [] args) { int [] b; b new int[] {88,99,66}; //分步定义数组&#xff0c;先定义数组名&#xff0c;然后再为数组赋值 int [] d {88,99,100}; //直接定义数组&#xff0c;同时赋值 Syste…

java数组循环动态赋值_Java数组

Java语言数组遍历教程 Java语言数组遍历详解 语法 for(int i = 0;i< 数组名称.length;i++){数组名称[i]; } 说明 我们在程序中,定义了一个变量 i,用 i 的值和数组的长度值比较,因为数组的索引是从 0 开始的,所以我们遍历的数值只能够小于 数组名称.length。 数组存储的结…

Java数组的赋值机制

数组的两种赋值的方式 引用传递&#xff0c;赋的是地址 数组的值是放在JVM的堆里&#xff0c;当定义一个数组时会在JVM里的栈中&#xff0c;放置堆的地址&#xff0c;来指向JVM对应的 堆 。在对数组进行赋值的时候&#xff0c;默认情况下是引用传递&#xff0c;即把堆的地址给…

Java二维数组赋值

Java二维数组是指由多个一维数组组成的数组结构。它可以看作是一个表格&#xff0c;其中行表示数组中的第一个维度&#xff0c;列表示第二个维度。 可以使用两个方括号&#xff08;[ ] [ ]&#xff09;来声明和访问Java二维数组中的元素。 例如&#xff0c;以下代码声明了一个包…

java数组赋值_java中给数组赋值的方法

1、数组操作中&#xff0c;可以使用等于()赋值 注意&#xff1a;此时新数组只是指向原数组的存储空间&#xff0c;并没有重新申请新的空间。 实例&#xff1a;public class ArrayTest{ public static void main(String args[]){ // 1 int[] anew int[4]; a[0]1; a[1]2; a[2]3; …

Java数组变量赋值

一、问题&#xff1a;Java数组变量赋值是值传递还是址传递&#xff1f; 1.1 结论&#xff1a;java变量赋值是址传递 1.2 验证过程&#xff1a; ​ public class Test {public static void main(String[] args) {int[] arryA {1, 2, 3};int[] arryB {4, 5, 6, 7};arryA ar…

java 数组赋值_java中为数组赋值的方法

java中为数组赋值的方法 发布时间&#xff1a;2020-06-25 14:31:36 来源&#xff1a;亿速云 阅读&#xff1a;184 作者&#xff1a;Leah 这期内容当中小编将会给大家带来有关java中为数组赋值的方法&#xff0c;文章内容丰富且以专业的角度为大家分析和叙述&#xff0c;阅读完这…

【自学Java】Java语言数组赋值

Java语言数组赋值 Java数组赋值教程 在 Java 语言 中&#xff0c;给 数组 赋值&#xff0c;就相当于给每个位置上的对应的位置填充数据。 Java语言数组赋值详解 Java 中的数据赋值分为动态化赋值和静态化赋值两种赋值方式。动态化赋值指的是先定义数组&#xff0c;然后指定…

Java数组赋值数组复制(拷贝)

Java数组赋值&数组复制(拷贝) 数组赋值数组复制 1⃣️数组赋值 值传递(基本数据类型)与引用传递(数组)区别 值传递&#xff1a;基本数据类型赋值&#xff0c;赋给变量的值就是具体的数据&#xff0c;而且相互不受影响&#xff1b; int a 10; int b a; // b的变化不会影…