web前端高级实战:实现商品详细放大镜效果
在我们访问淘宝天猫或京东等购物网站时,点击某个商品进入详情页面会看到一个商品的缩略图,当鼠标浮到缩略图上时,在右侧就会出现一个大图并且可以挪动,如下图:
- 思路分析
- HTML+CSS布局
- 首先有个小盒子用来存放缩略图
- 在小盒子中应该有张图片(缩略图)和一个半透明的遮罩层
- 遮罩层默认隐藏,当鼠标悬浮到小盒子上时,遮罩层显示
- 紧挨着小盒子有个大盒子,用来存放商品大图,默认也是隐藏,商品大图的大小要大于大盒子的大小
- 当鼠标悬浮到小盒子上时,大盒子显示
- JavaScript代码
- 获取页面上的元素(小盒子,遮罩层,小图片,大盒子和大图片)
- 获取各个元素的width、height、left和top值
- 根据遮罩层宽高占小盒子宽高的比例与大盒子宽高占大图片宽高的比例相等这一原理,计算出大图片的宽高,这样不管小盒子,遮罩层和大盒子的宽高怎么变化,都能保证放大部分就是遮罩层遮住的部分
- 当遮罩层移动时,大图片也随着移动但方向是相反的
- 遮罩层和大图片移动计算原理:遮罩层水平移动的距离/小盒子的宽 = 大图片水平移动的距离/大图片的宽(垂直距离同理)
- 一些细节
- 遮罩层在移动时是有边界的,不能移出小盒子
- 当鼠标进入到小盒子时,鼠标指针自动停在遮罩层中间
- 因为大图片要比大盒子大,所以要设置大盒子的overflow属性,让超出的部分隐藏
- 源码展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>放大镜效果</title>
<style>
.container {
width: 100%;
height: 100%;
}
.container .abbr {
position: relative;
box-sizing: border-box;
width: 300px;
height: 200px;
left: 200px;
top: 100px;
}
.abbr img {
width: 100%;
height: 100%;
}
.mark {
display: none;
position: absolute;
box-sizing: border-box;
width: 150px;
height: 100px;
border: 1px solid red;
left: 0px;
top: 0;
background: rgba(255, 0, 0, .3);
cursor: move;
}
.details {
position: relative;
left: 500px;
top: -100px;
width: 700px;
height: 600px;
overflow: hidden;
}
.details img {
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="abbr">
<img src="small.jpg" />
<div class="mark"></div>
</div>
<div class="details">
<img class="big-img" src="big.jpg" alt="">
</div>
</div>
</body>
</html>
let abbr = document.querySelector('.abbr'),
mark = document.querySelector('.mark'),
details = document.querySelector('.details'),
bigImg = document.querySelector('.big-img');
let abbrInfo = abbr.getBoundingClientRect(),
abbrWidth = abbrInfo.width,
abbrHeight = abbrInfo.height,
abbrLeft = abbrInfo.left,
abbrTop = abbrInfo.top;
let detailsInfo = window.getComputedStyle(details),
dtlWidth = parseInt(detailsInfo.width),
dtlHeight = parseInt(detailsInfo.height),
dtlLeft = parseInt(detailsInfo.left),
dtlTop = parseInt(detailsInfo.top);
let markInfo = window.getComputedStyle(mark),
markWidth = parseInt(markInfo.width),
markHeight = parseInt(markInfo.height),
markLeft = parseInt(markInfo.left),
markTop = parseInt(markInfo.top);
let maxLeft = abbrWidth - markWidth,
maxTop = abbrHeight - markHeight;
bigImg.style.width = dtlWidth / (markWidth / abbrWidth) + "px";
bigImg.style.height = dtlHeight / (markHeight / abbrHeight) + "px";
const mouseenter = function mouseenter(ev) {
mark.style.display = "block";
details.style.display = "block";
mark.addEventListener('mousemove', mousemove);
}
const mouseleave = function mouseleave(ev) {
mark.style.display = "none";
details.style.display = "none";
mark.removeEventListener('mousemove', mousemove);
}
const mousemove = function mousemove(ev) {
let left = (ev.clientX - abbrLeft - markWidth / 2),
top = (ev.clientY - abbrTop - markHeight / 2);
left = left < 0 ? 0 : left > maxLeft ? maxLeft : left;
top = top < 0 ? 0 : top > maxTop ? maxTop : top;
mark.style.left = `${left}px`;
mark.style.top = `${top}px`;
bigImg.style.left = `-${left/abbrWidth*bigImg.clientWidth}px`;
bigImg.style.top = `-${top/abbrHeight*bigImg.clientHeight}px`
}
abbr.addEventListener('mouseenter', mouseenter);
abbr.addEventListener('mouseleave', mouseleave);