js图片放大镜效果实现方法详解


Posted in Javascript onOctober 28, 2020

由项目需要,原生写了个详情页图片放大镜的效果,扔上代码供学习分享,也作为日常笔记...

效果如图(例子中偷偷链了张天猫的图片,希望没啥事 -。-):

js图片放大镜效果实现方法详解

实现过程教简单,但我们还是从css开始分析,过程如下(图片已正方形为例):

css:

/* 图片容器 */
 .imgBox{
 width: 200px; /* 各位大老爷们看着办 */
 height: 200px; /* 各位大老爷们看着办 */
 position: relative; /* 必需 */
 }

 /* 图片标签 */
 .mainImg{
 width: 100%; /* 各位大老爷们看着办,尽量100%好看些[斜眼笑] */
 height: 100%; /* 各位大老爷们看着办,尽量100%好看些[斜眼笑] */
 }

 /* 遮罩层-既放大区域 */
 .glass{
 position: absolute; /* 必需 */
 width: 50px; /* 遮罩层宽度 此处是放大4倍,所以为200/4=50 */
 height: 50px; /* 遮罩层高度 此处是放大4倍,所以为200/4=50 */
 top: -9999px; /* 绝对位置,先放远些 */
 left: -9999px; /* 绝对位置,先放远些 */
 cursor: move; /* 鼠标样式,好看些 */
 background: rgba(0,0,180,0.5); /* 遮罩层样式,好看些 */
 }

 /* 大图所在的容器 */
 .imgMax{
 position: absolute; /* 必需 */
 overflow: hidden; /* 必需,盖掉超出的大图[斜眼笑] */
 left: 210px; /* 必需,此处为距原图左边10像素 */
 top: 0; /* 必需,此处为距上边0像素 */
 width: 200px; /* 放大图片容器的宽度 此处此处是放大4倍,为200,保持和原图容器一般大,若此处为400,则是放大2*4倍,那么相应的放大图片应该是200*4*2=1600 */
 height: 200px; /* 放大图片容器的高度 此处此处是放大4倍,为200,保持和原图容器一般大,若此处为400,则是放大2*4倍,那么相应的放大图片应该是200*4*2=1600 */
 display: none; /* 先隐藏 */
 }
 .maxImg{
 position: absolute; /* 必需 */
 width: 800px; /* 此处是放大4倍,所以为200*4=800 受放大图片所在的容器影响,规则如上 */
 height: 800px; /* 此处是放大4倍,所以为200*4=800 受放大图片所在的容器影响,规则如上 */
 }

上面css中需要注意的就是几个position和缩放比例,注意调整下即可

写完样式,来看看布局:

html:

<!-- 图片容器 -->
 <div class="J_imgBox imgBox">
 <!-- 需要放大的图片-原始图 -->
 <img class="J_mainImg mainImg" src="http://img.alicdn.com/bao/uploaded/i7/TB1Xpe_NXXXXXXRXFXXGTq09XXX_035318.jpg_430x430q90.jpg" />
 <!-- 遮罩-既放大的区域 -->
 <div class="J_glass glass"></div>
 <!-- 大图的容器 -->
 <div class="J_imgMax imgMax">
 <!-- 大图 -->
 <img class="J_maxImg maxImg" />
 </div>
 </div>

接下来是主要的js代码,一如既往的带注解:

js:

(function(){
 /* 放大镜函数
 ** @imgContainer 需要实现放大镜效果的图片容器 此处是 class 为 J_imgBox 的 div
 */
 function imgZoom(imgContainer){

 // 取大图url,不知道淘宝图片规则如何,反正看了详情页的大图和小图url对比,随便写了个替换
 var imgUrl = imgContainer.querySelector('.J_mainImg').src.replace(/\.(jpg|jpeg|png|gif)(_)(\d+)(x)(\d+)(q90)?/g,'');

 // 取大图标签的节点
 var maxImg = imgContainer.querySelector('.J_maxImg');

 // 给该节点的src属性赋值为大图的url
 maxImg.src = imgUrl;

 // 取大图所在的容器
 var maxImgContainer = imgContainer.querySelector('.J_imgMax');

 // 取遮罩块
 var glassBlock = imgContainer.querySelector('.J_glass');

 // 取消放大镜效果
 var hideMaxImg = function(){
 glassBlock.style.top = '-9999px';
 glassBlock.style.left = '-9999px';
 maxImgContainer.style.display = 'none';
 }

 // 鼠标移出图片区域,取消放大镜效果
 imgContainer.onmouseout = function(event){
 event.stopPropagation();
 hideMaxImg();
 };

 // 鼠标在图片区域内移动事件
 imgContainer.onmousemove = function(event) {
 event.stopPropagation();

 // 取图片容器的大小及其相对于视口的位置,需要实时取,所以放在move事件里
 var clientRect = event.currentTarget.getBoundingClientRect();

 // 获取距鼠标距的上和左的坐标
 var leftX = event.clientX - clientRect.left;
 var leftY = event.clientY - clientRect.top;

 // 动态设置遮罩块的left和top位置 这里需要减去遮罩层的一半,因为鼠标位于遮罩块中心点
 var pointerLeft = leftX - 25;
 var pointerTop = leftY - 25;

 // 如果鼠标坐标移动超出原始图片区域边缘 则取消放大镜效果 因为这里存在快速移动鼠标到大图区域时,鼠标仍处在外层的图片区域内,并不会触发mouseout事件(虽然中间隔了小小的间距,但是快速移动仍能产生这个bug,如代码下面的图所示)
 if((pointerLeft+25) > clientRect.width || pointerLeft < 0 - 25 || (pointerTop+25) > clientRect.height || pointerTop < 0 - 25){
 hideMaxImg();
 return !1;
 };

 // 遮罩块在最左边的时候,鼠标仍在图片区域内,可在遮罩块左边缘至中心线区域内移动,且这时遮罩块为距左0像素
 if(pointerLeft < 0){
 pointerLeft = 0;
 };

 // 同上 右边限制
 if(pointerLeft > clientRect.width - 50){
 pointerLeft = clientRect.width - 50;
 };

 // 同上 顶部限制
 if(pointerTop < 0){
 pointerTop = 0;
 };

 // 同上 底部限制
 if(pointerTop > clientRect.height - 50){
 pointerTop = clientRect.height - 50;
 };

 // 设置遮罩块的位置
 glassBlock.style.left = pointerLeft;
 glassBlock.style.top = pointerTop;

 // 取遮罩快距离左边的位置和图片区域的宽高比,用于计算大图偏移距离,展示遮罩块所对应的图片区域
 var percentLeft = pointerLeft/clientRect.width;
 var percentHeight = pointerTop/clientRect.height;

 // 设置大图偏移距离 因为其父元素存在 overflow:hidden 所以只会展示对应区块
 maxImg.style.left = -(percentLeft*maxImg.clientWidth)+'px';
 maxImg.style.top = -(percentHeight*maxImg.clientHeight)+'px';
 };
 }

 var elem = document.querySelectorAll('.J_imgBox');

 elem.forEach(function(item,idx){
 imgZoom(item)
 })
 })()

补bug图:

js图片放大镜效果实现方法详解

看完后是不是觉得简直不要太简单,接下来就来理一理以上代码中能够抽取出来在平常开发中比较实用的知识点:

Element.getBoundingClientRect()

Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置

例子:

<body style="width:1400;height:1000">
<div id="testDiv" style="width:10px;height:20px;background:#f00"></div>
<script>
(function(){
var elem = document.getElementById('testDiv');
document.body.addEventListener('click',function(){
console.log(elem.getBoundingClientRect())
},false)
})()
</script>
</body>

效果如图:

js图片放大镜效果实现方法详解

从效果图上不难看出,当我移动视图后再点击body,打印的对象都能够正确返回元素的大小及其相对于视口的位置

这个方法也可以用于实现当某元素滚动到底/顶部时触发对应事件,相当方便。

Event

1.event.target 和 event.currentTarget

    target:指向触发事件的元素

    currentTarget:指向被绑定事件句柄的元素

    只有当绑定的事件处理程序与触发该事件处理程序都为同一个对象的时候,两者相同

    例子代码:

    html:

<div id="aDiv">
 123
 <div id="bDiv">456</div>
 </div>

js:

document.getElementById('aDiv').addEventListener('click',function(e){
 if(e.target === e.currentTarget) {
 console.log('target === currentTarget')
 }else{
 console.log('target !== currentTarget')
 }
 console.log('target',e.target)
 console.log('currentTarget',e.currentTarget)
 },false)

    效果图:

js图片放大镜效果实现方法详解

    从效果图中,我们可以看到,当点击456时,target指向的是456所在的bDiv,currentTarget则指向aDiv,因为事件是绑定在aDiv上,但触发是在bDiv上,而且bDiv又在aDiv内,当点击123时,则target与currentTarget一致,绑定和触发都在aDiv上。

2.event.preventDefault() & event.stopPropagation()

    preventDefault:如果事件可取消,则取消该事件,而不停止事件的进一步传播

    stopPropagation:阻止捕获和冒泡阶段中当前事件的进一步传播

3.event.stopPropagation() & event.stopImmediatePropagation()

    stopPropagation:阻止捕获和冒泡阶段中当前事件的进一步传播

    stopImmediatePropagation:阻止元素上调用相同事件的其他事件监听并阻止冒泡

    两者区别的例子:

  html:

<div id="aDiv">
 123
 <div id="bDiv">456</div>
 </div>

    js:

document.getElementById('aDiv').addEventListener('click',function(){
 console.log('click aDiv')
 },false)
 document.getElementById('bDiv').addEventListener('click',function(e){
 e.stopImmediatePropagation();
 console.log('click bDiv')
 },false)
 document.getElementById('bDiv').addEventListener('click',function(){
 console.log('click me too')
 },false)

上面代码执行结果为:

click bDiv

注释掉 e.stopImmediatePropagation(); 的结果为:

click bDiv
click me too
click aDIV
 

虽然都是些简单的知识点,在平常开发中也是很实用的,希望能从细节出发,没事多复习复习 -。-~

后来一时兴起将放大镜写的更傻瓜式配置的插件了... 点我看代码(github地址)

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

Javascript 相关文章推荐
用javascript连接access数据库的方法
Nov 17 Javascript
javascript开发技术大全-第1章javascript概述
Jul 03 Javascript
那些年,我还在学习jquery 学习笔记
Mar 05 Javascript
jQuery下通过replace字符串替换实现大小图片切换
May 22 Javascript
jquery remove方法应用详解
Nov 22 Javascript
JS 仿腾讯发表微博的效果代码
Dec 25 Javascript
jQuery基于ajax()使用serialize()提交form数据的方法
Dec 08 Javascript
Node.js开发者必须了解的4个JS要点
Feb 21 Javascript
用js动态添加html元素,以及属性的简单实例
Jul 19 Javascript
在vue中使用vue-echarts-v3的实例代码
Sep 13 Javascript
vant 自定义 van-dropdown-item的用法
Aug 05 Javascript
ES6 十大特性简介
Dec 09 Javascript
js a标签点击事件
Mar 30 #Javascript
JS+html5制作简单音乐播放器
Sep 13 #Javascript
TypeScript入门-接口
Mar 30 #Javascript
如何编写jquery插件
Mar 29 #jQuery
基于JavaScript实现瀑布流效果
Mar 29 #Javascript
Angular动态添加、删除输入框并计算值实例代码
Mar 29 #Javascript
JS变量及其作用域
Mar 29 #Javascript
You might like
某大型网络公司应聘时的笔试题目附答案
2008/03/27 PHP
PHP取进制余数函数代码
2012/01/19 PHP
ThinkPHP模板范围判断输出In标签与Range标签用法详解
2014/06/30 PHP
php使用wordwrap格式化文本段落的方法
2015/03/17 PHP
PHP将字符串首字母大小写转换的实例
2017/01/21 PHP
jQuery EasyUI API 中文文档 - ValidateBox验证框
2011/10/06 Javascript
js图片闪动特效可以控制间隔时间如几分钟闪动一下
2014/08/12 Javascript
js css自定义分页效果
2017/02/24 Javascript
基于ES6作用域和解构赋值详解
2017/11/03 Javascript
vue2.0 axios跨域并渲染的问题解决方法
2018/03/08 Javascript
express.js中间件说明详解
2019/03/19 Javascript
JS匿名函数内部this指向问题详析
2019/05/10 Javascript
Vue.js中的高级面试题及答案
2020/01/13 Javascript
Vue项目中使用flow做类型检测的方法
2020/03/18 Javascript
Vue移动端用淘宝弹性布局lib-flexible插件做适配的方法
2020/05/26 Javascript
[01:01:18]DOTA2上海特级锦标赛主赛事日 - 2 败者组第二轮#2COL VS LGD
2016/03/03 DOTA
使用url_helper简化Python中Django框架的url配置教程
2015/05/30 Python
Python实现动态图解析、合成与倒放
2018/01/18 Python
python2.7和NLTK安装详细教程
2018/09/19 Python
浅析python3字符串格式化format()函数的简单用法
2018/12/07 Python
python issubclass 和 isinstance函数
2019/07/25 Python
python中类的输出或类的实例输出为这种形式的原因
2019/08/12 Python
Python文件读写w+和r+区别解析
2020/03/26 Python
使用python+poco+夜神模拟器进行自动化测试实例
2020/04/23 Python
使用Python封装excel操作指南
2021/01/29 Python
ASOS亚洲:ASOS Asia
2018/03/04 全球购物
巴西最大的巴士票务门户:Quero Passagem
2020/11/21 全球购物
售后服务经理岗位职责
2014/02/25 职场文书
八一建军节感言
2014/02/28 职场文书
大学生学习2014年全国两会心得体会
2014/03/12 职场文书
教育英语专业毕业生的求职信
2014/03/13 职场文书
质量负责人任命书
2014/06/06 职场文书
销售队伍口号
2014/06/11 职场文书
幼儿园小班家长评语
2014/12/30 职场文书
李清照的诗词赏析(20首)
2019/08/22 职场文书
mysql连接查询中and与where的区别浅析
2021/07/01 MySQL