High一下!

图片放大镜效果实现

纸上得来终觉浅,绝知此事要躬行

前言

之前放大镜效果都是用jquery的插件,具体实现也没认真想过,看到一个demo感觉不错,mark下来,原来地址猛戳

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

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


实现过程教简单css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

/* 图片容器 */
.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

1
2
3
4
5
6
7
8
9
10
11
12
13

<!-- 图片容器 -->
<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代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

(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+'px';
glassBlock.style.top = pointerTop+'px';

// 取遮罩快距离左边的位置和图片区域的宽高比,用于计算大图偏移距离,展示遮罩块所对应的图片区域
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';
maxImgContainer.style.display = 'block';//显示大图容器
};
}

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

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

出来在平常开发中比较实用的知识

  1. Element.getBoundingClientRect()
    Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置
  2. event.target 和 event.currentTarget
    • target:指向触发事件的元素
    • currentTarget:指向被绑定事件句柄的元素,只有当绑定的事件处理程序与触发该事件处理程序都为同一个对象的时候,两者相同
  3. event.preventDefault() & event.stopPropagation()
    • preventDefault:如果事件可取消,则取消该事件,而不停止事件的进一步传播
    • stopPropagation:阻止捕获和冒泡阶段中当前事件的进一步传播
  4. event.stopPropagation() &event.stopImmediatePropagation()
    • stopPropagation:阻止捕获和冒泡阶段中当前事件的进一步传播
    • stopImmediatePropagation:阻止元素上调用相同事件的其他事件监听并阻止冒泡

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

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

imageZoom.js

最后我把作者封装的插件代码写在下面
只需要使用者写一个标签和写图片大小即可的了 0.0 , 只需要配置imgContainer的大小和必须的position:relative,然后写个div ,写上对应class和 图片地址data-src,然后js里调用下imageZoom就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

(function(window){
function imageZoom(elem,options){

var settings = {
imgZoom : options && options.imgZoom || 2,
marginLeft: options && options.marginLeft || 0,
marginTop: options && options.marginTop || 0,
}

// 获取图片节点
var imgContainer = elem;

// 获取图片url
var imgUrl = imgContainer.getAttribute('data-src')

// you need to write your own RegExp to replace my RegExp . This is important !!!
// 用自己的正则代替这里的 因为对图片处理的参数不尽相同,比如我们公司就是?imageView/w/300/h/300
var originalUrl = imgUrl.replace(/\.(jpg|jpeg|png|gif)(_)(\d+)(x)(\d+)(q90)?/g,'');

// 获取图片节点位置
var elemClientRect = imgContainer.getBoundingClientRect();

// 生成小图html
var minImgHtml = '<img style="width:100%;height:100%" src="' + imgUrl + '" />';

if(!settings.marginLeft && !settings.marginTop){
settings.marginLeft = elemClientRect.width+10;
}

// 生成遮罩html
var glassWidth = elemClientRect.width/settings.imgZoom;
var glassHeight = elemClientRect.height/settings.imgZoom;
var glassStyle = 'width:' + glassWidth + 'px;height:' + glassHeight + 'px;'+'position:absolute;top: -9999px;left: -9999px;cursor: move;background: rgba(0,0,180,0.5);'
var glassHtml = '<div class="J_glass" style="' + glassStyle + '" ></div>';

// 生成大图
var maxImgStyle = 'position:absolute;width:' + (elemClientRect.width * settings.imgZoom) + 'px;height:' + (elemClientRect.height * settings.imgZoom) + 'px;';
var maxImgHtml = '<img class="J_maxImg" src="' + originalUrl + '" style="' + maxImgStyle + '" />';

// 生成大图容器
var maxContainerStyle = 'position:absolute;left:' + settings.marginLeft + 'px;top:'+ settings.marginTop +'px;width:' + elemClientRect.width + ';height:' + elemClientRect.height + ';overflow:hidden;display:none';
var maxContainerHtml = '<div class="J_imgMax" style="' + maxContainerStyle + '">' + maxImgHtml + '</div>';

var contentHtml = minImgHtml + glassHtml + maxContainerHtml;

imgContainer.innerHTML = contentHtml;

var maxImgContainer = imgContainer.querySelector('.J_imgMax');
var glassBlock = imgContainer.querySelector('.J_glass');
var maxImg = imgContainer.querySelector('.J_maxImg');

// 隐藏大图&遮罩
var hideMaxImg = function(){
glassBlock.style.top = '-9999px';
glassBlock.style.left = '-9999px';
maxImgContainer.style.display = 'none';
}

imgContainer.onmouseover = function(event){
event.stopPropagation();
maxImgContainer.style.display = 'block';
};

imgContainer.onmouseout = function(event){
event.stopPropagation();
hideMaxImg();
};

imgContainer.onmousemove = function(event) {
event.stopPropagation();
var clientRect = event.currentTarget.getBoundingClientRect();
// 获取左右上下坐标
var leftX = event.clientX - clientRect.left;
var leftY = event.clientY - clientRect.top;
// 动态设置遮罩范围值
var pointerLeft = leftX - glassWidth/2;
var pointerTop = leftY - glassHeight/2;
if((pointerLeft+glassWidth/2) > clientRect.width || pointerLeft < 0 - glassWidth/2 || (pointerTop+glassHeight/2) > clientRect.height || pointerTop < 0 - glassHeight/2){
hideMaxImg();
return !1;
};
if(pointerLeft < 0){
pointerLeft = 0;
};
if(pointerLeft > clientRect.width - glassWidth){
pointerLeft = clientRect.width - glassWidth;
};
if(pointerTop < 0){
pointerTop = 0;
};
if(pointerTop > clientRect.height - glassHeight){
pointerTop = clientRect.height - glassHeight;
};
glassBlock.style.left = pointerLeft;
glassBlock.style.top = pointerTop;
var percentLeft = pointerLeft/clientRect.width;
var percentHeight = pointerTop/clientRect.height;
maxImg.style.left = -(percentLeft*maxImg.clientWidth)+'px';
maxImg.style.top = -(percentHeight*maxImg.clientHeight)+'px';
};
}
window.imageZoom = imageZoom;//挂载到全局对象
})(window)

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章