一 . 介绍
如今购物网站商品详情页面,都会有放大镜功能,例如京东和淘宝,如下图1-1所示。让用户可以通过悬浮小图从而可以查看大图,方便用户可以更清晰得查看商品细节。
接下来为商品放大镜功能详细的实现过程和思路。下图1-2为最终实现效果。
二 . 实现步骤
1.放大效果
首先将大致框架写出来,分为左右两个div。并添加样式。css样式代码如下图2-1-1所示。
其中right,也就是放大图块,设置根据页面浮动,代码为position:absolute;。right上左边距再根据左边left块灵活决定,从而使right元素位置变活。当然,右边位置也可以通过样式直接定死,但这样每次调动左边left元素位置时,right也要跟着再次调动,就比较麻烦。具体实现代码如下图2-1-2所示。
其中先获取左右元素,用querySelector()方法,根据选择器规则,获取对应的第一个元素。再设置right的上左边距,其中right的上边距也就是left的上外边距,所有设置right.style.top = left.offsetTop。right左边距 = left左外边距 + left可视间距 + 左右间距,其中left的可视间距为offsetWidth(元素里的可用间距+两侧边框大小),这边的左右间距我设置了10px,再都加上单位 ' px '。具体分析图如下图2-1-3所示。
页面效果图如下图2-1-4所示。
接着在div中插入图片,一共大小两张图。其实实现放大图的效果,并不是真的把原本的图片放大,而是通过插入另一张更大的图,从而实现放大的效果。
我们可以去购物网站的源代码中找到那些大小商品图,小图比较容易能找到,大图一般就藏在小图图片标签里,例如下图2-1-5中京东网站的大图链接就在属性jqimg中,可以直接复制图片链接,也可以输入链接将图片下载到本地中。
其中大图是在样式中以背景图片的方式插入的,是为了后面图片的移动效果。效果如下图2-1-6。
鼠标在小图上移动时,大图也跟着相对移动,这种效果的原理是通过backgroundPosition样式实现,通过设置背景图片的位置百分比,而这个百分比通过鼠标在小图上相对于整个小图的位置百分比而决定的,这样就可以实现大小图的相对移动了。具体代码如下图2-1-7。
为left元素创建鼠标移动事件,其中offsetX,offsetY变量代表鼠标相对于自身left元素的左边距和上边距,分别通过鼠标相对于页面的左边距-元素left的左边距和鼠标相对于页面的上边距-元素left的上边距得出。再将得出的offsetX和offsetY分别除以left元素的可用宽度和可用高度,再除以100加上百分号,就可以得出鼠标相对于left元素的横比percent_x,纵比percent_y,并设置right大图的图片位置样式。这样就可以通过移动左边小图元素的鼠标,右边大图跟着相对移动的效果了。分析图如下图2-1-8所示。
接下来实现左边小图每次鼠标拖动时出现的透明块。我们在left中添加一个class为shadow的div,并为它设置样式,其中背景要设置一定的透明度,设置相对于left元素定位,设置鼠标悬浮样式为move。
创建shadowLeft和shadowTop变量,分别为shadow元素相对于left的左、上间距。shadowLeft通过鼠标相对于left的左间距offsetX - shadow的可视宽度offsetWidth/2得出。shadowTop通过鼠标相对于left的上间距offsetY - shadow的可视高度offsetWidth/2得出。除以2是为了让鼠标指针可以在shadow元素块正中间。
shadow不能移出left。所有设置最小间距,也就是0,当左边距shadowLeft小于0,左边距为0,上边距相同。设置最大边距为left的可用间距 - shadow的可视间距,当左边距shadowLeft大于最大左边距,左边距为最大左边距,最大上边距相同。
将左、上边距(相对于left)的值设置给shadow的样式。具体代码如下图2-1-9。
先设置right和shadow元素默认样式不显示(display:none;)。设置当鼠标进入left时,right大图显示,shadow也显示。反之鼠标移开left时,right大图消失,shadow也消失。代码如下图2-1-10。
2.图片切换效果
创建一个imgs对象数组存储所有的大中小图,小图为底下用来图片切换的图,中图是左边left中的图,大图当然就是右边放大的图。imgs数组里一个四个对象,每个对象都有small,middle,large三个属性,所有一共有12张图。页面不直接插入图片,除了中大图设置默认的第一张图片外,其他都通过调取imgs对象数组获取。
每当鼠标进入到一个底下小图时,就会显示对应的中图,进入中图,就会显示对应的大图,原理是循坏遍历,然后通过下标一一对应就可以实现图片对应的效果。为小图悬浮设置高亮样式。具体代码如下图2-2-1。
三.知识点整理
offsetWidth返回元素的可见宽度
offsetHeight返回元素的可见高度
clientWidth返回元素的可用宽度
clientHeight返回元素的可用高度
offsetLeft返回元素到左外边距
offsetTop返回元素到上外边距
pageX,pageY 是点击处距离视口(0,0)坐标的距离
offsetX,offsetY 是点击处距离当前容器的距离
四.完整代码
完整代码如下:
<!DOCTYPE html>
<html>
<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>放大镜</title>
<style>
*{
margin: 0;
padding: 0;
list-style: none;
}
#left{
width: 300px;
height: 300px;
border: 1px solid #eee;
position: relative;
}
#left img{
width: 100%;
height: 100%;
}
#right{
width: 500px;
height: 500px;
position: absolute;
border: 1px solid #eee;
background: url(https://img11.360buyimg.com//n0/jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg);
display: none;
}
.shadow{
width: 140px;
height: 140px;
background: rgba(235, 235, 111, 0.4);
position: absolute;
box-sizing: border-box;
cursor: move;
display: none;
}
.side{
margin: 50px;
width: 300px;
}
.indexs{
display: flex;
justify-content: space-between;
padding-top: 10px;
}
.indexs li{
width: 70px;
height: 70px;
}
.indexs li img{
width: 100%;
}
.indexs li.active{
box-shadow: 0 0 2px red;
}
</style>
</head>
<body>
<div class="side">
<div id="left">
<div class="shadow"></div>
<img src="https://img11.360buyimg.com/n1/s450x450_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg">
</div>
<ul class="indexs">
<li class="active">
<img>
</li>
<li>
<img>
</li>
<li>
<img>
</li>
<li>
<img>
</li>
</ul>
</div>
<div id="right"></div>
<script>
let imgs = [
{
small:'https://img11.360buyimg.com/n5/s54x54_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg',
middle:'https://img11.360buyimg.com/n1/s450x450_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg',
large:'https://img11.360buyimg.com//n0/jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg'
},
{
small:'https://img11.360buyimg.com/n5/s54x54_jfs/t1/177253/12/17346/177769/6107c50eE38095356/8f9bfe0993d35868.jpg',
middle:'https://img11.360buyimg.com/n1/s450x450_jfs/t1/177253/12/17346/177769/6107c50eE38095356/8f9bfe0993d35868.jpg',
large:'https://img11.360buyimg.com//n0/jfs/t1/177253/12/17346/177769/6107c50eE38095356/8f9bfe0993d35868.jpg'
},
{
small:'https://img11.360buyimg.com/n5/s54x54_jfs/t1/198884/15/1182/224588/6107c50eE3f7afab5/a440d75129f918ef.jpg',
middle:'https://img11.360buyimg.com/n1/s450x450_jfs/t1/198884/15/1182/224588/6107c50eE3f7afab5/a440d75129f918ef.jpg',
large:'https://img11.360buyimg.com//n0/jfs/t1/198884/15/1182/224588/6107c50eE3f7afab5/a440d75129f918ef.jpg'
},
{
small:'https://img11.360buyimg.com/n5/s54x54_jfs/t1/184642/22/17151/130181/6107c50dE086535b3/35e5ca0ddad69a52.jpg',
middle:'https://img11.360buyimg.com/n1/s450x450_jfs/t1/184642/22/17151/130181/6107c50dE086535b3/35e5ca0ddad69a52.jpg',
large:'https://img11.360buyimg.com//n0/jfs/t1/184642/22/17151/130181/6107c50dE086535b3/35e5ca0ddad69a52.jpg'
}
]
// 获取所有的小图的li
let indexs = document.querySelectorAll('.indexs li')
// 获取中图
let mimg = document.querySelector('#left img')
// 获取所有的小图
let simgs = document.querySelectorAll('.indexs img')
indexs.forEach((r,i)=>{
r.onmouseenter = function(){
// 除去之前高亮样式
document.querySelector('.indexs li.active').classList.remove('active')
// 添加当前(鼠标进入的li)高亮样式
r.classList.add('active')
// 显示对应的中图
mimg.src = imgs[i].middle
// 显示对应的大图
right.style.background = `url(${imgs[i].large})`
}
})
// 获取显示所有的小图
simgs.forEach((g,i)=>{
g.src = imgs[i].small
})
// 获取left
let left = document.querySelector('#left')
// 获取right
let right = document.querySelector('#right')
// right上边距 = left上外边距
right.style.top = left.offsetTop + 'px'
// right左边距 = left左外边距 + left可视间距 + 左右间距
right.style.left = left.offsetLeft + left.offsetWidth + 10 + 'px'
// 获取shadow
let shadow = document.querySelector('.shadow')
left.onmousemove = function(e){
let offsetX = e.pageX - left.offsetLeft
let offsetY = e.pageY - left.offsetTop
let percent_x = (offsetX/left.clientWidth) * 100 + '%';
let percent_y = (offsetY/left.clientHeight) * 100 + '%';
right.style.backgroundPosition = `${percent_x} ${percent_y}`
// shadow左边距(相对于left) = 鼠标相对于left的间距 - shadow的可视间距/2
let shadowLeft = offsetX - shadow.offsetWidth/2
let shadowTop = offsetY - shadow.offsetHeight/2
// 最小间距
if(shadowLeft < 0) shadowLeft = 0
if(shadowTop < 0) shadowTop = 0
// shadow最大左边距(相对于left) = left的可用间距 - shadow的可视间距
let maxLeft = left.clientWidth - shadow.offsetWidth
let maxTop = left.clientHeight - shadow.offsetHeight
// 最大间距
if(shadowLeft>maxLeft) shadowLeft=maxLeft
if(shadowTop>maxTop) shadowTop=maxTop
shadow.style.left = shadowLeft + 'px'
shadow.style.top = shadowTop + 'px'
}
left.onmouseenter = function(){
right.style.display = 'block'
shadow.style.display = 'block'
}
left.onmouseleave = function(){
right.style.display = 'none'
shadow.style.display = 'none'
}
</script>
</body>
</html>