这里的放大镜效果是指当鼠标(手指)和页面(屏幕)中一个方框内的图片交互时,图片呈现放大效果,图片在框内显示的放大区域随鼠标(页面)位置的改变而改变
演示链接:https://codesandbox.io/s/fangdajingxiaoguo-jxdfo
以下我尝试了几种不同的实现方式
移动图片框内的图片
这里是通过改变图片框内的一张图片的大小和绝对定位偏移量实现最终效果
HTML的基本结构如下:
<div id="image">
<!-- 该图片设置为绝对定位 -->
<img src="./img/doge.webp" alt="doge">
</div>
这里JS中涉及到的事件有mouseenter
,mouseleave
,mousemove
,这些事件都在图片框绑定(#image
)
在mouseenter
事件处理函数中,只需要将框中的图片尺寸放大即可:
let imageBox = document.getElementById('image');
imageBox.addEventListener('mouseenter', () => {
// 这里将图片(图片和框一样为正方形)放大为 450x450 ,相当于图片框的3倍(150x150)
img.style.width = '450px';
})
在mouseleave
事件处理函数中则只需要将定位的偏移样式和图片的放大尺寸样式清除即可:
// 清除所有样式 >> 恢复原样
imageBox.addEventListener('mouseleave', () => {
img.style.width = '';
img.style.top = '';
img.style.left = '';
})
图片的放大效果的实现重点是mousemove
的事件处理函数中关于图片定位偏移量的计算,这里的思路如下:
先找出当前鼠标位置相对于图片框左上角的坐标位置x, y
,接着计算出这个坐标位置在图片框中的相对百分比位置(比如该点在框的正中间的话就是0.5, 0.5
或者50%, 50%
,这个百分比是相对于框体的左上角而言的),用这个百分比位置在放大的图片中找到相对于放大的图片左上角相同百分比偏移的位置targetX, targetY
,然后将x, y
和targetX, targetY
这两个点通过定位偏移重叠即可。为了将这两个点重叠,要求出这两个点的水平距离和垂直距离distanceX, distanceY
,由于需要将图片相对于图片框的进行绝对定位偏移,那么就需要进行反向偏移,则需要对求出的distanceX, distanceY
取负值,也就是最终的定位偏移为left: - distanceX + 'px'; top: - distanceY + 'px'
以下为具体计算步骤:
-
计算当前鼠标位置相对于图片框左上角的坐标
x, y
:鼠标在页面中的具体位置可以通过
e.pageX, e.pageY
获得(e
表示鼠标事件对象),这个具体位置是相对于页面的左上角计算的,那么相对于框体的左上角位置,可以通过减去框体的顶边和左边距离页面的顶边和左边的距离(也就是imageBox.offsetTop, imageBox.offsetLeft
)获得:x = e.pageX - imageBox.offsetLeft; y = e.pageY - imageBox.offsetTop;
-
将坐标转化成在图片框中的相对百分比位置:
相对于整个框体的相对位置的计算需要使用到框体的高度和宽度(
imageBox.offsetWidth, imageBox.offsetHeight
)x / imageBox.offsetWidth; y / imageBox.offsetHeight;
-
计算放大的图片中相同相对位置下的目标点
targetX, targetY
:这需要利用到上述计算得到的相对位置,再去乘以放大的图片尺寸(
img.offsetWidth, img.offsetHeight
)获得targetX = img.offsetWidth * (x / imageBox.offsetWidth); targetY = img.offsetHeight * (y / imageBox.offsetHeight);
-
计算需要进行偏移的距离
distanceX, distanceY
:偏移的水平距离和垂直距离就是上述两个相对位置
x, y
和targetX, targetY
的水平距离和垂直距离的差值:distanceX = targetX - x; distanceY = targetY - y;
-
将图片相对于图片框进行绝对定位偏移,需要将图片反向移动,所以需要对偏移距离的值取负(设置样式要加上单位“px”):
img.style.left = - distanceX + 'px'; img.style.top = - distanceY + 'px';
至此对于图片定位偏移量的计算就结束了(具体计算可以参见下图解析)
所以最终的mousemove
的事件处理函数为:
imageBox.addEventListener('mousemove', (e) => {
// 当前鼠标位置相对于图片框左上角的坐标x, y
let x = e.pageX - imageBox.offsetLeft;
let y = e.pageY - imageBox.offsetTop;
// 放大的图片中相同相对位置下的目标点targetX, targetY
let targetX = img.offsetWidth * x / imageBox.offsetWidth;
let targetY = img.offsetHeight * y / imageBox.offsetHeight;
// 需要进行偏移的距离distanceX, distanceY
let distanceX = targetX - x;
let distanceY = targetY - y;
// 将图片相对于图片框进行绝对定位偏移,需要将图片反向移动,所以需要对偏移距离的值取负
img.style.left = - distanceX + 'px';
img.style.top = - distanceY + 'px';
})
移动图片框的背景图片
这里介绍第二种方式:将上述的图片框内的图片移除,直接将要放大的图片作为图片框的背景图片,通过改变背景图片大小和背景图片位置来实现最终效果
HTML的基本结构如下:
<div id="image"></div>
这里涉及到的三个JS事件还是mouseenter
,mouseleave
,mousemove
,这些事件都和图片框绑定(imageBox
)
在mouseenter
事件处理函数中,只需要将框的背景图片尺寸放大即可:
image.addEventListener('mouseenter', (e) => {
// 为了提高事件处理函数的通用性,最好就不要引用外部变量 image
// image.style.backgroundSize = '450px';
e.target.style.backgroundSize = '450px';
});
在mouseleave
事件处理函数中则是只需要将背景图片的尺寸和位置样式清除即可:
image.addEventListener('mouseleave', (e) => {
// image.style.backgroundSize = '';
// image.style.backgroundPosition = '';
e.target.style.backgroundSize = '';
e.target.style.backgroundPosition = '';
});
这里的实现重点依然是对图片偏移量的计算,不过不一样的是,这里的背景图片尺寸信息比较难找到相关属性值来动态获取(如elem.offsetWidth
),不能像上述采用绝对定位的做法那样求出具体的偏移距离。而如果设为定值,那么当背景图片的放大尺寸变了,程序要改的地方就比较多了
这里利用背景图片位置的百分比取值来实现将图片移动到目标位置的目的(这样甚至都不用计算具体偏移距离了!)
这里简单讲一下背景图片位置background-position
取值为百分数时的表现:
百分比取值执行的是下述公式,最终的结果是使容器和背景图片相同的相对位置重叠