原生js实现放大镜组件


Posted in Javascript onJanuary 22, 2021

本文实例为大家分享了js实现放大镜组件开发的具体代码,供大家参考,具体内容如下

功能需求:

1、根据图片数组创建图标列表;
2、鼠标滑过图标时,当前图标增加红色边框;
3、鼠标滑过图标时,上方图片区域显示对应的图片,右侧显示放大后的图片内容;
4、鼠标在图片区域移动时,在右侧实现放大效果;
5、下方图标列表,点击左右按钮,实现翻页效果;
6、当图标内容不够一页时,只移动到最后一个图标的位置;

以京东的详情页为例,看一下效果:

原生js实现放大镜组件

放大镜内容写在 Zoom.js 文件里,下方的图标列表内容写在 IconList.js 文件里,当鼠标滑过下面的图标时,需要更改放大镜里div的背景图片,这里用到了事件抛发。

下面附上代码:

html结构 :

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>zoom</title>
</head>
<body>
 <script type="module">
  import Zoom from './js/Zoom.js';
  //图标数组
  let list=["a_icon.jpg","e_icon.jpg","f_icon.jpg","g_icon.jpg","h_icon.jpg","i_icon.jpg","j_icon.jpg",];
  init();
  function init(){
   let zoom=new Zoom(list,"./img/");
   zoom.appendTo("body");
  }
 </script>
</body>
</html>

Zoom.js文件,创建放大镜组件:

import Utils from "./Utils.js";
import IconList from './IconList.js';
export default class Zoom{
 static styles=false;
 static small_width=450;
 static mask_width=303.75;
 static zoom_width=540;
 static SET_BG_IMG="set_bg_img";
 constructor(_list,_basePath){
  if(_basePath) _list=_list.map(item=>_basePath+item);
  //创建外层的div容器
  this.elem=this.createE();
  //监听事件,改变zoomSmall的背景图
  document.addEventListener(Zoom.SET_BG_IMG,e=>this.setBgImg(e));
  //创建下方的icon列表
  this.createIconList(_list,this.elem);
 }
 createE(){
  //创建外层div容器
  let div=Utils.createE("div");
  div.className="zoomContainer";
  div.innerHTML=`<div class="zoomSmall" id="zoomSmall"><div class="zoomMask" id="zoomMask"></div></div>
  <div class="zoomContent" id="zoomCont"></div>`;
  //设置样式
  Zoom.setStyle();
  //获取样式
  Utils.getIdElem(div,this);
  //监听鼠标滑入事件
  this.zoomSmall.addEventListener("mouseenter",e=>this.mouseHandler(e));
  return div;
 }
 appendTo(parent){
  Utils.appendTo(this.elem,parent);
 }
 setBgImg(e){
  //设置背景图片
  this.zoomSmall.style.backgroundImage=`url(${e.src})`;
  this.zoomCont.style.backgroundImage=`url(${e.src})`;
 }
 createIconList(list,parent){
  //创建下方icon图标列表
  let iconList=new IconList(list);
  Utils.appendTo(iconList.elem,parent);
 }
 mouseHandler(e){
  switch (e.type) {
   case "mouseenter":
    //鼠标滑入后,显示遮罩和右侧大图片
    this.zoomMask.style.display="block";
    this.zoomCont.style.display="block";
    //监听鼠标移动和滑出事件
    this.mouseHandlers=e=>this.mouseHandler(e);
    this.zoomSmall.addEventListener("mousemove",this.mouseHandlers);
    this.zoomSmall.addEventListener("mouseleave",this.mouseHandlers);
    break;
   case "mousemove":
    //遮罩移动
    this.zoomMaskMove(e);
    break;
   case "mouseleave":
    //鼠标滑出后,显示遮罩和右侧大图片
    this.zoomMask.style.display="none";
    this.zoomCont.style.display="none";
    //移除鼠标移动和滑出事件
    this.zoomSmall.removeEventListener("mousemove",this.mouseHandlers);
    this.zoomSmall.removeEventListener("mouseleave",this.mouseHandlers);
    break;
  }
 }
 zoomMaskMove(e){
  //遮罩移动
  let rect=this.elem.getBoundingClientRect();
  //计算let和top的值,等于鼠标的坐标-父容器的left值-遮罩的一半宽
  let x=e.clientX-rect.x-Zoom.mask_width/2;
  let y=e.clientY-rect.y-Zoom.mask_width/2;
  //判断left和top的范围
  if(x<0) x=0;
  if(x>Zoom.small_width-Zoom.mask_width) x=Zoom.small_width-Zoom.mask_width;
  if(y<0) y=0;
  if(y>Zoom.small_width-Zoom.mask_width) y=Zoom.small_width-Zoom.mask_width;
  this.zoomMask.style.left=x+"px";
  this.zoomMask.style.top=y+"px";
  //大图片移动
  this.zoomContMove(x,y);
 }
 zoomContMove(_x,_y){
  //计算大图片的背景定位,公式:zoom的宽/mask的宽=zoom的背景left值/mask的left值
  let x=-Zoom.zoom_width/Zoom.mask_width*_x;
  let y=-Zoom.zoom_width/Zoom.mask_width*_y;
  this.zoomCont.style.backgroundPosition=x+"px "+y+"px";
 }
 static setStyle(){
  //设置样式
  if(Zoom.styles) return;
  Zoom.styles=true;
  Utils.insertCss(".zoomContainer",{
   width:Zoom.small_width+"px",
   height:Zoom.small_width+"px",
   position:"relative"
  })
  Utils.insertCss(".zoomSmall",{
   width:Zoom.small_width+"px",
   height:Zoom.small_width+"px",
   border: "1px solid #000",
   backgroundSize: "100% 100%",
   position:"absolute",
   left:"0px",
   top:"0px"
  })
  Utils.insertCss(".zoomMask",{
   width: this.mask_width + "px",
   height: this.mask_width + "px",
   backgroundColor: "rgba(200,170,0,0.3)",
   position: "absolute",
   left: "0px",
   top: "0px",
   display: "none"
  })
  Utils.insertCss(".zoomContent",{
   width: this.zoom_width + "px",
   height: this.zoom_width + "px",
   border: "1px solid #ccc",
   position: "absolute",
   left: (this.small_width + 2) + "px",
   top: "0px",
   display: "none"
  })
 }
}

IconList.js文件,创建下方图标列表,并完成翻页效果:

import Utils from "./Utils.js";
import Zoom from "./Zoom.js";
export default class IconList{
 static styles=false;
 static num=5;//每页显示的图标数
 static gap=0;//表示li的左右间距
 position=0;//当前显示的图标为第几页
 x=0;//列表的left值
 prepIcon;//上一个点击的图标
 static SET_BG_IMG="set_bg_img";
 constructor(list){
  this.list=list;
  this.elem=this.createE();
 }
 createE(){
  //创建外层容器
  let div=Utils.createE("div");
  div.className="iconContainer";
  div.innerHTML=`<img class="prevBtn" src="./img/prev.png"><div class="iconListCont">${this.createIcon()}</div><img class="nextBtn" src="./img/next.png">`;
  //设置css样式
  IconList.setStyles(this.list);
  //获取元素
  Utils.getIdElem(div,this);
  //外层容器监听点击事件
  div.addEventListener("click",e=>this.clickHandler(e));
  //图标列表监听鼠标滑过事件
  this.iconList.addEventListener("mouseover",e=>this.mouseHandler(e));
  //默认显示第一个图标的边框
  this.setIconState(this.iconList.firstElementChild);
  //默认显示第一个图片
  this.setBgImg(this.iconList.firstElementChild.firstElementChild);
  return div;
 }
 createIcon(){
  //创建图标列表
  let str=`<ul class="iconList clearfix" id="iconList">`;
  this.list.forEach(item=>{
   str+=`<li><img src="${item}"></li>`;
  })
  str+="</ul>";
  return str;
 }
 clickHandler(e){
  let src=e.target.src;
  //如果点击的不是左右按钮,直接跳出
  if(!/prev/.test(src)&&!/next/.test(src)) return;
  //每一个li的实际宽度,width+border+margin
  let liWidth=54+4+IconList.gap;
  //page为一共有几个整数页
  let page=Math.floor(this.list.length/IconList.num)-1;
  //remainder为最后不够一页的剩余图标数
  let remainder=this.list.length%IconList.num;
  if(/prev/.test(src)){
   //如果点击的是上一页按钮
   if(this.x===0) return;
   //移动到最后一页时
   if(this.position===0&&remainder>0){
    //移动的距离加等于li宽度*剩余图标数
    this.x+=liWidth*remainder;
   }
   else if(this.position<=page){
    this.position--;
    //移动的距离加等于li的宽度*每页显示的图标数(5个)
    this.x+=liWidth*IconList.num;
   }
  }else if(/next/.test(src)){
   //如果点击的是下一页按钮
   if(this.x===-(this.list.length-IconList.num)*liWidth) return;
   if(this.position===page&&remainder>0){
    //移动的距离减等于li宽度*剩余图标数
    this.x-=liWidth*remainder;
   }
   else if(this.position<page){
    this.position++;
    //移动的距离减等于li的宽度*每页显示的图标数(5个)
    this.x-=liWidth*IconList.num;
   }
  }
  //设置图标列表的left值
  this.iconList.style.left=this.x+"px";
 }
 mouseHandler(e){
  //如果滑过的不是Img标签,直接跳出
  if(e.target.constructor!==HTMLImageElement) return;
  //设置背景图片
  this.setBgImg(e.target);
  //设置当前滑过图标的样式
  this.setIconState(e.target.parentElement);
 }
 setIconState(target){
  //移除上一个滑过图标的active样式
  if(this.prepIcon) Utils.removeClass(this.prepIcon,"active");
  //将当前滑过的对象赋值给this.prepIcon
  this.prepIcon=target;
  //给当前滑过图标增加active样式
  Utils.addClass(this.prepIcon,"active");
 }
 setBgImg(target){
  //抛发事件,将当前图片的src传过去
  let src=target.src.replace("_icon","");
  let evt=new Event(IconList.SET_BG_IMG);
  evt.src=src;
  document.dispatchEvent(evt);
 }
 static setStyles(list){
  //设置样式
  if(IconList.styles) return;
  IconList.styles=true;
  Utils.insertCss(".iconContainer",{
   width:Zoom.small_width+2+"px",
   height: "58px",
   position: "absolute",
   top: Zoom.small_width+2+"px",
   left: "0px",
  })
  Utils.insertCss(".iconContainer>img",{
   width:"22px",
   height:"32px",
   cursor:"pointer",
   position:"absolute",
   top:"13px",
  })
  Utils.insertCss(".prevBtn",{
   left:"8px"
  })
  Utils.insertCss(".nextBtn",{
   right:"8px"
  })
  Utils.insertCss(".iconListCont",{
   width:Zoom.small_width-30*2+"px",
   height:"58px",
   position:"relative",
   left:"30px",
   overflow:"hidden"
  })
  IconList.gap=((Zoom.small_width-30*2)-(54+4)*IconList.num)/IconList.num;
  Utils.insertCss(".iconList",{
   width:(54+4+IconList.gap)*list.length+"px",
   listStyle:"none",
   padding:"0px",
   margin:"0px",
   position:"absolute",
   left:"0px",
   top:"0px",
   transition:"all .3s"
  })
  Utils.insertCss(".iconList li",{
   float:"left",
   width:"54px",
   height:"54px",
   margin:"0px "+IconList.gap/2+"px",
   cursor:"pointer",
   border:"2px solid transparent"
  })
  Utils.insertCss(".iconList li.active",{
   borderColor:"#f00"
  })
  Utils.insertCss(".iconList li>img",{
   width:"54px",
   height:"54px"
  })
  Utils.insertCss(".clearfix::after",{
   content:"\".\"",
   display:"block",
   height:"0px",
   clear:"both",
   overflow:"hidden",
   visibility:"hidden"
  })
 }
}

Utils.js文件,是一个工具包:

export default class Utils{
 static createE(elem,style,prep){
  elem=document.createElement(elem);
  if(style) for(let prop in style) elem.style[prop]=style[prop];
  if(prep) for(let prop in prep) elem[prop]=prep[prop];
  return elem;
 }
 static appendTo(elem,parent){
  if (parent.constructor === String) parent = document.querySelector(parent);
  parent.appendChild(elem);
 }
 static insertBefore(elem,parent){
  if(parent.constructor === String) parent=document.querySelector(parent);
  parent.insertBefore(elem,parent.firstElementChild);
 }
 static randomNum(min,max){
  return Math.floor(Math.random*(max-min)+min);
 }
 static randomColor(alpha){
  alpha=alpha||Math.random().toFixed(1);
  if(isNaN(alpha)) alpha=1;
  if(alpha>1) alpha=1;
  if(alpha<0) alpha=0;
  let col="rgba(";
  for(let i=0;i<3;i++){
   col+=Utils.randomNum(0,256)+",";
  }
  col+=alpha+")";
  return col;
 }
 static insertCss(select,styles){
  if(document.styleSheets.length===0){
   let styleS=Utils.createE("style");
   Utils.appendTo(styleS,document.head);
  }
  let styleSheet=document.styleSheets[document.styleSheets.length-1];
  let str=select+"{";
  for(var prop in styles){
   str+=prop.replace(/[A-Z]/g,function(item){
    return "-"+item.toLocaleLowerCase();
   })+":"+styles[prop]+";";
  }
  str+="}"
  styleSheet.insertRule(str,styleSheet.cssRules.length);
 }
 static getIdElem(elem,obj){
  if(elem.id) obj[elem.id]=elem;
  if(elem.children.length===0) return obj;
  for(let i=0;i<elem.children.length;i++){
   Utils.getIdElem(elem.children[i],obj);
  }
 }
 static addClass(elem,className){
  let arr=(elem.className+" "+className).match(/\S+/g);
  arr=arr.filter((item,index)=>arr.indexOf(item,index+1)<0)
  elem.className=arr.join(" ");
 }
 static removeClass(elem,className){
  if(!elem.className) return;
  let arr=elem.className.match(/\S+/g);
  let arr1=className.match(/\S+/g);
  arr1.forEach(item=>{
   arr=arr.filter(t=>t!==item)
  })
  elem.className=arr.join(" ");
 }
 static hasClass(elem,className){
  if(!elem.className) return false;
  let arr=elem.className.match(/\S+/g);
  let arr1=className.match(/\S+/g);
  let res;
  arr1.forEach(item=>{
   res= arr.some(it=>it===item)
  })
  return res;
 }
 static loadImg({list,basePath,callback}){
  if(!list || list.length===0) return;
  if(basePath) list=list.map(item=>basePath+item);
  let img=Utils.createE("img");
  img.data={
   list:list,
   callback:callback,
   resultList:[],
   num:0
  }
  img.addEventListener("load",Utils.loadImgHandler);
  img.src=list[img.data.num];
 }
 static loadImgHandler(e){
  let data=e.currentTarget.data;
  data.resultList.push(e.currentTarget.cloneNode(false));
  data.num++;
  if(data.num>data.list.length-1){
   e.currentTarget.removeEventListener("load",Utils.loadImgHandler);
   data.callback(data.resultList);
   data=null;
   return;
  }
  e.currentTarget.src=data.list[data.num];
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Code:findPosX 和 findPosY
Dec 20 Javascript
MVC Ajax Helper或Jquery异步加载部分视图
Nov 29 Javascript
jQuery插件EasyUI获取当前Tab中iframe窗体对象的方法
Aug 05 Javascript
Bootstrap Table的使用总结
Oct 08 Javascript
微信小程序 WebSocket详解及应用
Jan 21 Javascript
JS中使用 after 伪类清除浮动实例
Mar 01 Javascript
JS仿QQ好友列表展开、收缩功能(第二篇)
Jul 07 Javascript
浅谈vuepress 踩坑记
Apr 18 Javascript
基于Proxy的小程序状态管理实现
Jun 14 Javascript
Vue列表循环从指定下标开始的多种解决方案
Apr 08 Javascript
JS如何实现基于websocket的多端桥接平台
May 14 Javascript
插件导致ECharts被全量引入的坑示例解析
Sep 23 Javascript
Vue仿Bibibili首页的问题
Jan 21 #Vue.js
如何在vue 中使用柱状图 并自修改配置
Jan 21 #Vue.js
Vue看了就会的8个小技巧
Jan 21 #Vue.js
原生js实现滑块区间组件
Jan 20 #Javascript
原生js实现下拉框选择组件
Jan 20 #Javascript
原生js实现自定义滚动条组件
Jan 20 #Javascript
原生js实现自定义滚动条
Jan 20 #Javascript
You might like
PHP与javascript的两种交互方式
2006/10/09 PHP
php设计模式 Template (模板模式)
2011/06/26 PHP
PHP面向对象三大特点学习(充分理解抽象、封装、继承、多态)
2012/05/07 PHP
修改jQuery Validation里默认的验证方法
2012/02/14 Javascript
javascript里模拟sleep(两种实现方式)
2013/01/25 Javascript
javascript设置金额样式转换保留两位小数示例代码
2013/12/04 Javascript
javascript中键盘事件用法实例分析
2015/01/30 Javascript
javascript精确统计网站访问量实例代码
2015/12/19 Javascript
细说webpack源码之compile流程-rules参数处理技巧(2)
2017/12/26 Javascript
原生javascript AJAX 三级联动的实现代码
2018/05/04 Javascript
微信小程序利用canvas 绘制幸运大转盘功能
2018/07/06 Javascript
VuePress 快速踩坑小结
2019/02/14 Javascript
如何检查一个对象是否为空
2019/04/11 Javascript
详解微信小程序用定时器实现倒计时效果
2019/04/30 Javascript
vue 组件中使用 transition 和 transition-group实现过渡动画
2019/07/09 Javascript
vue 使用 vue-pdf 实现pdf在线预览的示例代码
2020/04/26 Javascript
Python 爬虫学习笔记之多线程爬虫
2016/09/21 Python
Python基础语言学习笔记总结(精华)
2017/11/14 Python
Python中一行和多行import模块问题
2018/04/01 Python
python实现可视化动态CPU性能监控
2018/06/21 Python
一百多行python代码实现抢票助手
2018/09/25 Python
Pycharm代码无法复制,无法选中删除,无法编辑的解决方法
2018/10/22 Python
Python 类的魔法属性用法实例分析
2019/11/21 Python
用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)
2020/05/17 Python
python Tornado框架的使用示例
2020/10/19 Python
时尚圣经:The Fashion Bible
2019/03/03 全球购物
出国导师推荐信
2014/01/16 职场文书
幼儿园教师的考核评语
2014/04/18 职场文书
教师节演讲稿
2014/05/06 职场文书
合伙购房协议样本
2014/10/06 职场文书
教师思想作风整顿个人剖析材料
2014/10/10 职场文书
就业推荐表导师评语
2014/12/31 职场文书
教师专业技术工作总结2015
2015/05/13 职场文书
2016年端午节红领巾广播稿
2015/12/18 职场文书
2017年寒假社区服务活动总结
2016/04/06 职场文书
centos8安装nginx1.9.1的详细过程
2021/08/02 Servers