在网页中做图片放大镜,本质是把小图上的某一块区域按比例放大后画到另一个区域。html5提供了canvas绘图能力和精准的鼠标、触摸事件,让这件事可以完全在前端完成,不需要服务器生成多张不同尺寸的图。下面通过一个完整例子说明从结构搭建到交互实现的全部步骤。

一、页面结构设计
放大镜效果通常需要三个部分:左侧原图容器、跟随鼠标移动的半透明遮罩层、右侧显示放大结果的canvas。原图用img标签承载,遮罩用绝对定位的div,canvas则固定尺寸。结构清晰才能方便后续计算坐标。
需要注意原图容器要设置position:relative,遮罩层position:absolute并初始隐藏。右侧canvas大小可以独立于原图,比如原图400x300,canvas设800x600实现2倍放大。这种分离让放大倍数调整更灵活。
<div class="wrap">
<div class="small-box" id="smallBox">
<img src="https://ipipp.com/demo.jpg" width="400" height="300" id="smallImg" />
<div class="mask" id="mask"></div>
</div>
<canvas id="bigCanvas" width="800" height="600"></canvas>
</div>
二、样式与遮罩控制
遮罩层宽高一般取canvas宽高除以放大倍数,这样遮罩框住的区域刚好是canvas要画的内容。比如2倍放大时,遮罩宽就是800/2=400的一半即200,高300/2=150。用css把它做成半透明方块,并隐藏溢出。
右侧canvas用border区分,左侧small-box设置overflow:hidden避免遮罩超出。以下样式保证布局稳定,也不会因为图片加载慢而错位。
.small-box { position: relative; width: 400px; height: 300px; }
.mask { position: absolute; width: 200px; height: 150px; background: rgba(0,0,0,0.3); display: none; cursor: move; }
#bigCanvas { border: 1px solid #ccc; margin-left: 20px; }
三、JavaScript交互逻辑
核心是在small-box上监听mousemove,计算鼠标相对原图左上角的坐标,把遮罩中心对齐鼠标并限制在图内。然后用drawImage从原图按遮罩区域取样,画到canvas上,尺寸映射到canvas全尺寸实现放大。
这里最容易出错的是坐标算错导致放大区域偏移。我们用getBoundingClientRect获取容器位置,鼠标clientX减left得到相对坐标。drawImage九个参数分别为原图、原图裁切起点x,y、裁切宽高、画布起点0,0、画布宽高。放大倍数=canvas宽/遮罩宽。
var smallBox = document.getElementById('smallBox');
var mask = document.getElementById('mask');
var canvas = document.getElementById('bigCanvas');
var ctx = canvas.getContext('2d');
var img = document.getElementById('smallImg');
var scale = canvas.width / mask.offsetWidth;
smallBox.addEventListener('mousemove', function(e) {
var rect = smallBox.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var mx = x - mask.offsetWidth / 2;
var my = y - mask.offsetHeight / 2;
mx = Math.max(0, Math.min(mx, smallBox.offsetWidth - mask.offsetWidth));
my = Math.max(0, Math.min(my, smallBox.offsetHeight - mask.offsetHeight));
mask.style.left = mx + 'px';
mask.style.top = my + 'px';
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, mx * scale, my * scale, mask.offsetWidth * scale, mask.offsetHeight * scale, 0, 0, canvas.width, canvas.height);
});
smallBox.addEventListener('mouseenter', function() { mask.style.display = 'block'; });
smallBox.addEventListener('mouseleave', function() { mask.style.display = 'none'; });
四、移动端与高清屏适配
手机没有mousemove,要用touchmove并阻止默认滚动。同时retina屏devicePixelRatio大于1,canvas实际像素要乘dpr,否则放大图发虚。我们可以在js里动态设canvas.width为css宽乘dpr,再ctx.scale(dpr,dpr)。
触摸处理取touches[0].clientX即可,逻辑同鼠标。这样一套代码在桌面和移动端都能用,且高清屏清晰。完整考虑这些细节,html5放大镜就能稳定用于生产环境。
smallBox.addEventListener('touchmove', function(e) {
e.preventDefault();
var rect = smallBox.getBoundingClientRect();
var x = e.touches[0].clientX - rect.left;
var y = e.touches[0].clientY - rect.top;
var mx = Math.max(0, Math.min(x - mask.offsetWidth / 2, smallBox.offsetWidth - mask.offsetWidth));
var my = Math.max(0, Math.min(y - mask.offsetHeight / 2, smallBox.offsetHeight - mask.offsetHeight));
mask.style.left = mx + 'px';
mask.style.top = my + 'px';
ctx.drawImage(img, mx * scale, my * scale, mask.offsetWidth * scale, mask.offsetHeight * scale, 0, 0, canvas.width, canvas.height);
}, { passive: false });