JS 打造「放大镜 + 缩略图」一体组件

电商详情页的经典三件套:缩略图列表 + 中等图展示 + 局部放大图。本文js原生代码实现一条无依赖、可复用、可扩展的放大镜链路,涵盖事件委托、边界计算、背景定位三大核心技能。

效果预览

一、HTML 骨架

html 复制代码
<div class="container">
  <!-- 中等图 -->
  <div class="left-img">
    <div class="mask"></div>
  </div>
  <!-- 放大图 -->
  <div class="right-img"></div>
  <!-- 缩略图列表 -->
  <ul class="img-list"></ul>
</div>
  • left-img:中等图,承担点击切换与鼠标探测双重职责
  • mask:绝对定位遮罩,用于局部高亮与边界计算
  • right-img:放大图,背景图定位实现局部放大效果

二、数据约定

js 复制代码
var imgs = {
  small: ['imgA_1.jpg', 'imgB_1.jpg', 'imgC_1.jpg'],
  middle: ['imgA_2.jpg', 'imgB_2.jpg', 'imgC_2.jpg'],
  large: ['imgA_3.jpg', 'imgB_3.jpg', 'imgC_3.jpg']
}
  • 三张图按索引一一对应,切换时只需同步 backgroundImage
  • 缩略图定宽定高,中等图与放大图定宽不定高,保持比例

三、JS链式事件三步走

1.初始化:渲染缩略图 + 默认激活

js 复制代码
function initPage() {
  let html = ''
  for (let i = 0; i < imgs.small.length; i++) {
    html += `<li style="background-image:url(./images/${imgs.small[i]});"></li>`
  }
  $('.img-list').innerHTML = html
  $('.img-list li').style.border = '2px solid #000'   // 默认选中第一张
}

2.点击缩略图:同步切换中等图与大图

js 复制代码
$('.img-list').onclick = function (e) {
  if (e.target.tagName !== 'LI') return
  // 让所有 LI 失活
  $$('li').forEach(li => li.style.border = 'none')
  // 让当前 LI 激活
  e.target.style.border = '2px solid #000'

  const index = [].indexOf.call(this.children, e.target)
  $('.left-img').style.backgroundImage = `url(./images/${imgs.middle[index]})`
  $('.right-img').style.backgroundImage = `url(./images/${imgs.large[index]})`
}

使用事件委托,缩略图数量随意增减,无需重新绑定。

3.鼠标移动:遮罩层跟随 + 大图定位

js 复制代码
$('.left-img').onmousemove = function (e) {
  const mask = $('.mask')
  const large = $('.right-img')
  mask.style.opacity = 1
  large.style.opacity = 1

  // 计算遮罩层中心坐标
  let left = e.clientX - this.offsetLeft - mask.offsetWidth / 2
  let top = e.clientY - this.offsetTop - mask.offsetHeight / 2

  // 边界条件:不让遮罩跑出中等图
  left = Math.max(0, Math.min(left, this.offsetWidth - mask.offsetWidth))
  top = Math.max(0, Math.min(top, this.offsetHeight - mask.offsetHeight))

  mask.style.left = left + 'px'
  mask.style.top = top + 'px'

  // 大图反向移动,实现局部放大
  large.style.backgroundPositionX = -left + 'px'
  large.style.backgroundPositionY = -top + 'px'
}

边界计算拆解

  • left ≤ 0 时强制归零
  • left ≥ (中等图宽 - 遮罩宽) 时强制贴边
  • 同理处理 top 高度方向

背景定位技巧

遮罩向右移动 10px,大图背景向左移动 10px,形成「窗口」效果;Y 轴同理。


四、鼠标离开:自动隐藏

js 复制代码
$('.left-img').onmouseleave = function () {
  $('.mask').style.opacity = 0
  $('.right-img').style.opacity = 0
}

只用 opacity 控制显隐,避免 display: none 触发重排;背景图已预加载,无闪烁。

性能 & 扩展

  • 零依赖:原生 DOM API,gzip < 1 KB
  • 零重排:仅用 opacitybackground-position,不改动布局
  • 可异步:把 initPage 换成 fetch 即可接入后端图片列表
  • 可响应:把 offsetWidth 换成 getBoundingClientRect 即可适配缩放
相关推荐
Hexene...7 分钟前
【前端Vue】npm install时根据新的状态重新引入实际用到的包,不引入未使用到的
前端·vue.js·npm
cyforkk11 分钟前
14、Java 基础硬核复习:数据结构与集合源码的核心逻辑与面试考点
java·数据结构·面试
2301_7806698613 分钟前
Vue(入门配置、常用指令)、Ajax、Axios
前端·vue.js·ajax·javaweb
码农幻想梦13 分钟前
Vue3入门到实战【尚硅谷】
前端·vue
hudou_k15 分钟前
利用WebNaket实现Web应用直接访问硬件设备
前端
吃茄子的猫15 分钟前
若依框架根据当前登录人信息,显示不同的静态公司logo
前端·vue
Warren9823 分钟前
Pytest Fixture 到底该用 return 还是 yield?
数据库·oracle·面试·职场和发展·单元测试·pytest·pyqt
LZQ <=小氣鬼=>24 分钟前
React + Ant Design (antd) 国际化完整实战教程
前端·react.js·前端框架·antd·moment
星海拾遗27 分钟前
react源码从入门到入定
前端·javascript·react.js
小满zs29 分钟前
Next.js第二十五章(CSS方案)
开发语言·javascript·css