在电商网站中,商品图片对用户购买决策有着重要影响,尤其是在细节展示方面,图片放大效果是一个很棒的功能。今天我们就来聊聊如何实现鼠标悬停在商品图片上时的局部放大效果,类似天猫商品详情页中的交互形式。别担心,这个功能其实并不复杂,只需要110行代码便可轻松实现。
一、需求分析
在天猫等电商平台的商品详情页,当用户将鼠标悬停在商品图片上时,会出现一个放大镜效果。具体行为如下:
- 左边是原始的商品图片,当用户将鼠标移入图片区域时,出现一个放大镜样式的遮罩层。
- 右边是放大后的商品图片,遮罩层移动时,右侧的大图片同步显示遮罩层内的商品细节部分。
我们将基于这个需求实现一种简单的局部放大效果,通过 CSS 和 JavaScript 来控制图片的同步移动。
二、实现思路
为了更好地控制图片的放大效果,我们采用以下实现方式:
- 两张图片: 一张小图片放在左边显示,当鼠标悬停时,会显示一个遮罩层;右边放置一张对应的大图片,显示小图片局部放大的区域。
- 遮罩层的移动: 通过监听鼠标在小图片上的移动位置,动态计算遮罩层的坐标,并将遮罩层在小图片上的位置同步到大图片上。
- 放大效果: 大图片的背景位置会随着遮罩层在小图片上的移动实时调整,从而展现出局部放大效果。
三、代码实现
1、HTML 结构
首先,我们需要设置好页面的基本结构。我们用两个盒子来展示图片:左边是放小图片的盒子,右边是放大图片的盒子。遮罩层会放在小图片上,当鼠标移动时,右边的大图片就会显示出对应的放大细节。
<div class="image-container">
<img id="small-image" class="small-image" src="https://picsum.photos/id/7/600/400.jpg" alt="小图片">
<div class="zoom-lens" id="zoom-lens"></div>
</div>
<div class="zoom-result" id="zoom-result"></div>
主要由两个部分组成,左侧的小图片image-container
和右侧的大图片展示区zoom-result
。遮罩层zoom-lens
在小图片上移动时,大图片背景同步更新。
2、CSS 样式
在这一步,我们要通过 CSS 来设置图片的样式以及遮罩层的效果。image-container
用于限制小图片的尺寸,zoom-lens
作为遮罩层,zoom-result
显示大图背景并控制其大小。
/* 小图片盒子 */
.image-container {
position: relative;
width: 300px; /* 小图片的宽度 */
height: 200px; /* 小图片的高度 */
overflow: hidden;
border: 1px solid #ddd;
display: inline-block;
margin-right: 20px;
}
/* 小图片 */
.small-image {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 遮罩层 */
.zoom-lens {
position: absolute;
width: 100px;
height: 100px;
border: 1px solid #000;
background-color: rgba(255, 255, 255, 0.4);
pointer-events: none; /* 禁用遮罩层上的鼠标事件 */
}
/* 大图片盒子 */
.zoom-result {
width: 400px; /* 放大图盒子的宽度 */
height: 300px; /* 放大图盒子的高度 */
overflow: hidden;
border: 1px solid #d4d4d4;
position: absolute;
left: 320px;
top: 0;
background-repeat: no-repeat;
background-size: 1200px 800px; /* 背景图大小与原图大小成比例 */
}
在这里,我们为小图片和大图片容器设置了尺寸,并通过 background-size
控制大图片的放大倍数。遮罩层是半透明的矩形,会随着鼠标移动在小图片上显现。
3、JavaScript 实现逻辑
接下来,我们通过 JavaScript 监听鼠标移动,实现遮罩层与大图片的同步移动效果。这一步是整个放大效果的核心。
- 实现逻辑:
- 监听鼠标在小图片上的移动事件,获取鼠标相对于图片的坐标。
- 遮罩层根据鼠标坐标移动,防止其超出小图片边界。
- 根据遮罩层的位置,动态计算大图片的背景显示位置,从而呈现出局部放大的效果。
const smallImage = document.getElementById('small-image');
const lens = document.getElementById('zoom-lens');
const result = document.getElementById('zoom-result');
// 设置大图背景为原始大图片
result.style.backgroundImage = `url('https://picsum.photos/id/7/1200/800.jpg')`;
smallImage.addEventListener('mousemove', moveLens);
smallImage.addEventListener('mouseenter', () => {
lens.style.display = 'block';
result.style.display = 'block';
});
smallImage.addEventListener('mouseleave', () => {
lens.style.display = 'none';
result.style.display = 'none';
});
function moveLens(e) {
const { left, top, width, height } = smallImage.getBoundingClientRect();
const lensWidth = lens.offsetWidth;
const lensHeight = lens.offsetHeight;
// 计算鼠标在图片中的位置
let x = e.pageX - left;
let y = e.pageY - top;
// 防止遮罩层超出小图片边界
if (x < lensWidth / 2) x = lensWidth / 2;
if (x > width - lensWidth / 2) x = width - lensWidth / 2;
if (y < lensHeight / 2) y = lensHeight / 2;
if (y > height - lensHeight / 2) y = height - lensHeight / 2;
// 移动遮罩层
lens.style.left = `${x - lensWidth / 2}px`;
lens.style.top = `${y - lensHeight / 2}px`;
// 计算大图片背景的位置,按比例移动
const bgX = ((x - lensWidth / 2) / width) * 1200; // 大图宽度 1200
const bgY = ((y - lensHeight / 2) / height) * 800; // 大图高度 800
// 设置大图片的背景位置,确保大图只移动遮罩层覆盖区域
result.style.backgroundPosition = `-${bgX}px -${bgY}px`;
}
上述便是放大效果的逻辑,效果图如下:
演示地址:stackblitzstarters4zbw2m-3bzd--8080--34c588ed.local-credentialless.webcontainer.io/page55.html
4、 完整代码
最后再给出我们的完整代码。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片放大效果</title>
<style>
/* 小图片盒子 */
.image-container {
position: relative;
width: 300px; /* 小图片的宽度 */
height: 200px; /* 小图片的高度 */
overflow: hidden;
border: 1px solid #ddd;
display: inline-block;
margin-right: 20px;
}
/* 小图片 */
.small-image {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 遮罩层 */
.zoom-lens {
position: absolute;
width: 100px;
height: 100px;
border: 1px solid #000;
background-color: rgba(255, 255, 255, 0.4);
pointer-events: none; /* 禁用遮罩层上的鼠标事件 */
}
/* 大图片盒子 */
.zoom-result {
width: 400px; /* 放大图盒子的宽度 */
height: 300px; /* 放大图盒子的高度 */
overflow: hidden;
border: 1px solid #d4d4d4;
position: absolute;
left: 320px;
top: 0;
background-repeat: no-repeat;
background-size: 1200px 800px; /* 背景图大小与原图大小成比例 */
}
</style>
</head>
<body>
<div class="image-container">
<img
id="small-image"
class="small-image"
src="https://picsum.photos/id/7/600/400.jpg"
alt="小图片"
/>
<div class="zoom-lens" id="zoom-lens"></div>
</div>
<div class="zoom-result" id="zoom-result"></div>
<script>
const smallImage = document.getElementById('small-image');
const lens = document.getElementById('zoom-lens');
const result = document.getElementById('zoom-result');
// 设置大图背景为原始大图片
result.style.backgroundImage = `url('https://picsum.photos/id/7/1200/800.jpg')`;
smallImage.addEventListener('mousemove', moveLens);
smallImage.addEventListener('mouseenter', () => {
lens.style.display = 'block';
result.style.display = 'block';
});
smallImage.addEventListener('mouseleave', () => {
lens.style.display = 'none';
result.style.display = 'none';
});
function moveLens(e) {
const { left, top, width, height } = smallImage.getBoundingClientRect();
const lensWidth = lens.offsetWidth;
const lensHeight = lens.offsetHeight;
// 计算鼠标在图片中的位置
let x = e.pageX - left;
let y = e.pageY - top;
// 防止遮罩层超出小图片边界
if (x < lensWidth / 2) x = lensWidth / 2;
if (x > width - lensWidth / 2) x = width - lensWidth / 2;
if (y < lensHeight / 2) y = lensHeight / 2;
if (y > height - lensHeight / 2) y = height - lensHeight / 2;
// 移动遮罩层
lens.style.left = `${x - lensWidth / 2}px`;
lens.style.top = `${y - lensHeight / 2}px`;
// 计算大图片背景的位置,按比例移动
const bgX = ((x - lensWidth / 2) / width) * 1200; // 大图宽度 1200
const bgY = ((y - lensHeight / 2) / height) * 800; // 大图高度 800
// 设置大图片的背景位置,确保大图只移动遮罩层覆盖区域
result.style.backgroundPosition = `-${bgX}px -${bgY}px`;
}
</script>
</body>
</html>