【JS案例】JS实现图片放大镜功能

news2025/4/8 8:46:46

JS案例·图片放大镜

🌟效果展示 

🌟HTML结构

🌟CSS样式

🌟实现思路  

🌟具体实现 

1.初始化数据图片

 2.获取所需DOM元素

 3.初始化页面

初始化缩略图

绑定事件

🌟完整代码

🌟写在最后 


🌟效果展示 


🌟HTML结构

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
  <!--最外层容器 -->
  <div class="container">
    <!-- 左侧原图 -->
    <div class="left-img">
      <!-- 遮罩层 -->
      <div class="mask"></div>
    </div>
    <!-- 右侧放大图片 -->
    <div class="right-img"></div>
    <!-- 缩略图集合 -->
    <div class="img-list-wrapper">
      <ul class="img-list">
        <li></li>
      </ul>
    </div>
  </div>
  <script src="./index.js"></script>
</body>
</html>

🌟CSS样式

* {
  margin: 0;
  padding: 0;
  list-style: none;
}

.container {
  width: 1000px;
  height: 600px;
  margin: 50px auto;
  font-size: 0;
}

.left-img {
  width: 490px;
  height: 510px;
  margin-right: 16px;
  border: 1px solid #eee;
  display: inline-block;
  /* 图片 */
  background-image: url(./images/imgA_2.jpg);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  /* 遮罩层相对我进行定位 */
  position: relative;
}
.mask {
  width: 230px;
  height: 230px;
  background-image: url(./images/bg.png);
  position: absolute;
  top: 0;
  left: 0;
  /* opacity: 0; */
}
.right-img {
  width: 490px;
  height: 510px;
  border: 1px solid #eee;
  display: inline-block;
  background-image: url(./images/imgA_3.jpg);
  background-repeat: no-repeat;
  /* opacity: 0; */
}

.img-list-wrapper {
  width: 490px;
  text-align: center;
  margin-top: 10px;
}
.img-list {
  display: inline-block;
}
.img-list li {
  display: inline-block;
  width: 60px;
  height: 60px;
  margin: 0 5px;
  cursor: pointer;
  background-image: url(./images/imgA_1.jpg);
  background-repeat: no-repeat;
  border: 2px solid #000;
  /* border: 1px solid #eee; */
}

🌟实现思路  

在敲完上面HTML文件和CSS文件后可以看到下图效果(图片素材及完整代码文末可下载):

可以看到整体分为四块:

1. 阴影区域:其实是一张很小的像素图铺满元素形成,后续跟随鼠标移动,所选区域在右侧高清展示。

2.图片大图:就是展示的图片。

3.缩略图列表:点击切换图片。

4.高清大图展示区域。

在项目中②和③一般可以用同一种图,④会使用一张高清图。我们这里是使用三张图片来开发,缩略图,普通展示图,高清图。

因为①和④是鼠标移入才出现,所以这里 给.mask.right-img加上opacity: 0;属性如下:

接下来我们需要做的就是在鼠标移入和移出时在右侧展示与隐藏高清图,从而实现图片放大镜效果。


🌟具体实现 

1.初始化数据图片

 这里用的就是本地图片,所以先初始化数据图片,三种大小对应缩略图,展示图,高清图:

// 初始化数据图片
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']
}

 2.获取所需DOM元素

 因为需要频繁操作DOM元素,这里简单封装一个获取DOM元素方法:

// 单一元素
function $(selector) {
  return document.querySelector(selector);
}

// 多个元素
function $$(selector) {
  return document.querySelectorAll(selector);
}

接下来获取需要用到的元素:

 3.初始化页面

初始化缩略图

初始化所有缩略图,及③区域的缩略图列表。拿到缩略图值拼接字符串,将<li></li>插入页面,并默认选中第一张缩略图:

// 初始化所有缩略图
 let str = '';
 for(var i=0; i<imgs.small.length; i++) {
   str += '<li style="background-image: url(./images/'+ imgs.small[i] +');"></li>'
 }
 smallImg.innerHTML = str
// 默认选中第一个缩略图
$('.img-list li').style.border = '2px solid #000';

绑定事件

该效果中一共有两种事件,一个是点击缩略图,另一个是鼠标移入移出。

点击缩略图切换展示图片

smallImg.onclick = function (e) {
  // 判断我点击的元素是li元素
  if (e.target.tagName == 'LI') {
    // 让所有li元素取消border
    let lis = $$('li');
    for (let i = 0; i < lis.length; i++) {
      lis[i].style.border = 'none';
    }
    // 让选中的li元素添加border
    e.target.style.border = '2px solid #000';

    // 点击缩略图后,原图和大图也需要跟着变换,
    // 点的是第几个元素, 获取元素索引
    // [1,2,3].indexOf(3) == 2
    let index = [].indexOf.call(lis, e.target);
    midImg.style.backgroundImage = 'url(./images/' + imgs.middle[index] + ')';
    largeImg.style.backgroundImage = 'url(./images/' + imgs.large[index] + ')';
  }
}

移入移出事件

这里需要计算遮罩层离边框的距离,可得到的距离有:

绿色:边框距离浏览器左边的距离

红色:鼠标距离浏览器左边的距离

黄色:遮罩层一半的距离

通过这几个值就可求出遮罩层距离边框的距离left了,同理top值一样的求法

鼠标移入

midImg.onmousemove = function (e) {
    // 让遮罩层和大图展示
    mask.style.opacity = 1;
    largeImg.style.opacity = 1;

    // 根据鼠标位置计算遮罩层的位置
    let left = e.clientX - midImg.offsetLeft - mask.offsetWidth / 2;
    // 同理
    let top = e.clientY - midImg.offsetTop - mask.offsetHeight / 2;

    // 边界条件
    if (left <= 0) {
      left = 0;
    }
    if (top <= 0) {
      top = 0;
    }
    if (left >= midImg.offsetWidth - mask.offsetWidth) {
      left = midImg.offsetWidth - mask.offsetWidth
    }
    if (top >= midImg.offsetHeight - mask.offsetHeight) {
      top = midImg.offsetHeight - mask.offsetHeight
    }
    // 根据top和left调整mask的位置
    mask.style.left = left + 'px';
    mask.style.top = top + 'px';

    // 根据top 和 left,修改大图的位置,background-position-x
    largeImg.style.backgroundPositionX = -left + 'px';
    largeImg.style.backgroundPositionY = -top + 'px';

  }

鼠标移出

  midImg.onmouseleave = function (e) {
    // 让遮罩层和大图消失
    mask.style.opacity = 0;
    largeImg.style.opacity = 0;
  }

🌟完整代码

最后在把js代码进行整理与二次封装,完整代码如下:

index.html

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
  <!--最外层容器 -->
  <div class="container">
    <!-- 左侧原图 -->
    <div class="left-img">
      <!-- 遮罩层 -->
      <div class="mask"></div>
    </div>
    <!-- 右侧放大图片 -->
    <div class="right-img"></div>
    <!-- 缩略图集合 -->
    <div class="img-list-wrapper">
      <ul class="img-list">
      </ul>
    </div>
  </div>
  <script src="./index.js"></script>
</body>
</html>

index.css

* {
  margin: 0;
  padding: 0;
  list-style: none;
}

.container {
  width: 1000px;
  height: 600px;
  margin: 50px auto;
  font-size: 0;
}

.left-img {
  width: 490px;
  height: 510px;
  margin-right: 16px;
  border: 1px solid #eee;
  display: inline-block;
  /* 图片 */
  background-image: url(./images/imgA_2.jpg);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  /* 遮罩层相对我进行定位 */
  position: relative;
}
.mask {
  width: 230px;
  height: 230px;
  background-image: url(./images/bg.png);
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
}
.right-img {
  width: 490px;
  height: 510px;
  border: 1px solid #eee;
  display: inline-block;
  background-image: url(./images/imgA_3.jpg);
  background-repeat: no-repeat;
  opacity: 0;
}

.img-list-wrapper {
  width: 490px;
  text-align: center;
  margin-top: 10px;
}
.img-list {
  display: inline-block;
}
.img-list li {
  display: inline-block;
  width: 60px;
  height: 60px;
  margin: 0 5px;
  cursor: pointer;
  background-image: url(./images/imgA_1.jpg);
  background-repeat: no-repeat;
  /* border: 2px solid #000; */
  border: 1px solid #eee;
}

index.js

// 封装一个获取DOM元素的方法

// 单一元素
function $(selector) {
  return document.querySelector(selector);
}

// 多个元素
function $$(selector) {
  return document.querySelectorAll(selector);
}

// 初始化数据图片
let 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']
}

// 获取一些将要使用的dom元素
let container = $('.container');
let largeImg = $('.right-img');
let midImg = $('.left-img');
let smallImg = $('.img-list');
let mask = $('.mask');

// 两大类事件类型, 点击, 鼠标移入移出
// 1. 点击事件, 事件委托
smallImg.onclick = function (e) {
  // 判断我点击的元素是li元素
  if (e.target.tagName == 'LI') {
    // 让所有li元素取消border
    let lis = $$('li');
    for (let i = 0; i < lis.length; i++) {
      lis[i].style.border = 'none';
    }
    // 让选中的li元素添加border
    e.target.style.border = '2px solid #000';

    // 点击缩略图后,原图和大图也需要跟着变换,
    // 点的是第几个元素, 获取元素索引
    // [1,2,3].indexOf(3) == 2
    let index = [].indexOf.call(lis, e.target);
    midImg.style.backgroundImage = 'url(./images/' + imgs.middle[index] + ')';
    largeImg.style.backgroundImage = 'url(./images/' + imgs.large[index] + ')';
  }
}

// 函数封装,一键启动
// 函数本质:若干步骤的集合
// 初始化页面函数
function initPage() {
  var str = '';
  for (let i = 0; i < imgs.small.length; i++) {
    str += '<li style="background-image: url(./images/' + imgs.small[i] + ');"></li>'
  }
  smallImg.innerHTML = str;
  // 2. 默认选中第一个缩略图
  $('.img-list li').style.border = '2px solid #000';
}

// 绑定事件
function bindEvent() {
  midImg.onmousemove = function (e) {
    // 让遮罩层和大图展示
    mask.style.opacity = 1;
    largeImg.style.opacity = 1;

    // 根据鼠标位置计算遮罩层的位置
    let left = e.clientX - midImg.offsetLeft - mask.offsetWidth / 2;
    // 同理
    let top = e.clientY - midImg.offsetTop - mask.offsetHeight / 2;

    // 边界条件
    if (left <= 0) {
      left = 0;
    }
    if (top <= 0) {
      top = 0;
    }
    if (left >= midImg.offsetWidth - mask.offsetWidth) {
      left = midImg.offsetWidth - mask.offsetWidth
    }
    if (top >= midImg.offsetHeight - mask.offsetHeight) {
      top = midImg.offsetHeight - mask.offsetHeight
    }
    // 根据top和left调整mask的位置
    mask.style.left = left + 'px';
    mask.style.top = top + 'px';

    // 根据top 和 left,修改大图的位置,background-position-x
    largeImg.style.backgroundPositionX = -left + 'px';
    largeImg.style.backgroundPositionY = -top + 'px';

  }

  // 2. 移出
  midImg.onmouseleave = function (e) {
    // 让遮罩层和大图消失
    mask.style.opacity = 0;
    largeImg.style.opacity = 0;
  }
}

// 一键启动,执行一个函数
function main() {
  initPage();
  bindEvent();
}

main();

🌟写在最后 

本专栏将持续更新原生JS案例,提供一些工作中也能用上的一些小案例,详细讲解分析,提升JS开发水平与开发思路的积累,如果文中出现有瑕疵的地方各位通过评论或者私信联系我,我们一起进步,有兴趣的伙伴可以订阅一下:点击关注JS经典案例专栏

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/950775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

原子操作的原理和实现

目录 相关术语 处理器如何实现原子操作 Java如何实现原子操作 循环CAS实现原子操作 使用锁机制实现原子操作 原子操作是指一个或者多个不可再分割的操作。这些操作的执行顺序不能被打乱。 相关术语 缓存行&#xff1a;缓存的最小操作单位 &#xff08;面试题、重点&…

GP服务使用本地上传的文件进行分析

1、需求&#xff1a; 自己选择本地的文件上传在gp服务中进行分析&#xff0c;例如实现这个需求&#xff1a; 2、遇到的困境 发布创建TIN工具时要输入值表&#xff0c;但是我这里选择了本地的SHP文件和高程值后&#xff0c;发布出去就是一个常量值了&#xff0c;没法自己选择文…

App与小程序工具总结

文章目录 前言Burpsuite抓包问题LPosedJustTrustMe 绕过 SSL Pining小程序的反编译APP脱壳&#xff0c;反射大师、frida反射大师Frida 总结 前言 在进行渗透工作的时候&#xff0c;遇到过的App、小程序也不少了&#xff0c;有简单的&#xff0c;也有加固的比较不错的&#xff…

【软考】系统集成项目管理工程师(一)信息化基础知识【6分】

一、信息与信息系统 1、信息技术 为解决信息的采集、加工、存储、传输、处理、计算、转换、表现等问题而不断繁荣发展 核心-传输技术&#xff08;通常指通信、网络等&#xff09; 2、信息的质量属性 特点&#xff1a;客观性、普遍性 属性描述精确性对事物状态描述的精准程度…

接口幂等性设计的最佳实现

一、什么是幂等 二、为什么需要幂等 三、接口超时了&#xff0c;到底如何处理&#xff1f; 四、如何设计幂等 全局的唯一性ID 幂等设计的基本流程 五、实现幂等的8种方案 selectinsert主键/唯一索引冲突 直接insert 主键/唯一索引冲突 状态机幂等 抽取防重表 token令牌 悲观锁…

stm32之27.iic协议oled显示

屏幕如果无法点亮&#xff0c;需要用GPIO_OType_PP推挽输出&#xff0c;加并上拉电阻 1.显示字符串代码 2.显示图片代码&#xff08;unsigned强制转换&#xff08;char*&#xff09;&#xff09; 汉字显示

​iOS上架审核宝典:如何避免被拒一次提交成功

Xcode 摘要&#xff1a;本文整理了iOS上架需要注意的事项&#xff0c;以帮助从事iOS开发和App运营的朋友们避免审核被拒的困扰。通过遵循这些指南&#xff0c;我们可以在一次提交中成功地将应用上架&#xff0c;并节省宝贵的时间和精力。 引言&#xff1a; 从事iOS开发和App运…

【一等奖方案】大规模金融图数据中异常风险行为模式挖掘赛题「NUFE」解题思路

第十届CCF大数据与计算智能大赛&#xff08;2022 CCF BDCI&#xff09;已圆满结束&#xff0c;大赛官方竞赛平台DataFountain&#xff08;简称DF平台&#xff09;正在陆续释出各赛题获奖队伍的方案思路&#xff0c;欢迎广大数据科学家交流讨论。 本方案为【大规模金融图数据中…

AcWing 4405. 统计子矩阵(每日一题)

如果你觉得这篇题解对你有用&#xff0c;可以点点关注再走呗~ 题目描述 给定一个 NM 的矩阵 A&#xff0c;请你统计有多少个子矩阵 (最小 11&#xff0c;最大 NM) 满足子矩阵中所有数的和不超过给定的整数 K ? 输入格式 第一行包含三个整数 N,M 和 K。 之后 N 行每行包含 …

智慧展馆展厅5G+LoRa+蓝牙人员定位系统解决方案

展览业是现代高端服务业的重要组成部分&#xff0c;作为新兴的服务行业&#xff0c;展览业串联着工业、农业、商贸等诸多产业&#xff0c;能够有效拉动产业和消费增长&#xff0c;是中国发展潜力较大的行业之一。如今各个行业越来越多地举办各类展会&#xff0c;由于展馆展厅规…

人体学接口设备 (HID)

参考链接 windows-hardware drivers hid | Microsoft Learnhttps://learn.microsoft.com/pdf?urlhttps%3A%2F%2Flearn.microsoft.com%2Fzh-cn%2Fwindows-hardware%2Fdrivers%2Fhid%2Ftoc.json人体学接口设备 (HID) 简介 - Windows drivers | Microsoft Learnhttps://learn.mi…

遇到 Binder这些面试题,你会怎么答?

作为开发人员&#xff0c;每个人都有每个人擅长领域&#xff0c;自然也有自己不擅长的领域&#xff0c;很难成为完美的一个全栈开发。在面试中最怕遇见的一件事是面试官专挑你不擅长的领域进行提问&#xff0c;目的就是看你遇到问题的应变能力。 接下给大家分享一个面试中容易被…

CUDA小白 - NPP(3) 图像处理 Color and Sampling Conversion

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…

标准库STL容器使用值语义

C自学精简实践教程 目录(必读) 标准库STL的容器都是值语义的。 即&#xff0c;无法将一个变量放到容器里。容器里存放的只是我们放进去的变量的拷贝&#xff08;副本&#xff09;。 示例&#xff1a; #include <iostream> #include <vector> using namespace s…

【Apollo学习笔记】——规划模块TASK之RULE_BASED_STOP_DECIDER

文章目录 前言RULE_BASED_STOP_DECIDER相关配置RULE_BASED_STOP_DECIDER总体流程StopOnSidePassCheckClearDoneCheckSidePassStopIsPerceptionBlockedIsClearToChangeLaneCheckSidePassStopBuildStopDecisionELSE:涉及到的一些其他函数NormalizeAngleSelfRotate CheckLaneChang…

创建python环境——Anaconda

在Windows中安装Anaconda和简单使用 一.Anaconda发行概述 Anaconda是一个可以便捷获取和管理包&#xff0c;同时对环境进行统一管理的发行版本&#xff0c;它包含了conda、 Python在内的超过180个科学包及其依赖项。 1.Anaconda发行版本具有以下特点&#xff1a; (1)包含了…

Qt实现一个简单的放射式弹出菜单

Qt实现一个简单的放射式弹出菜单 实现效果&#xff1a; GitHub源码 支持特性 可设置弹出选项的起始角度与角度范围 按住中心按钮&#xff0c;可以拖拽窗口 动画支持&#xff0c;可以设置动画时间、选项的延迟弹出 设置菜单的布局对齐、边距&#xff0c;方便根据场景放置在…

YOLO目标检测——蔬菜检测数据集下载分享

YOLO蔬菜检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;图片格式为jpg&#xff0c;共21000张图片。 数据集点击下载&#xff1a;YOLO蔬菜检测数据集21000图片数据说明.rar

科研无人机平台P600进阶版,突破科研难题!

随着无人机技术日益成熟&#xff0c;无人机的应用领域不断扩大&#xff0c;对无人机研发的需求也在不断增加。然而&#xff0c;许多开发人员面临着无法从零开始构建无人机的时间和精力压力&#xff0c;同时也缺乏适合的软件平台来支持他们的开发工作。为了解决这个问题&#xf…

7.Redis-list

list list常用命令lpushlrangelpushxrpushrpushxlpop / rpoplindexlinsertllenlremltrimlset 阻塞版本命令blpop/brpop 总结内部编码应用场景使用redis作为消息队列 redis中的 list 是一个双端队列, list 相当于是数组或者顺序表。list 并非是一个简单的数组&#xff0c;而是更…