demo介绍:
咱们今天做的放大器来源于京东(想看效果的直接去京东商城随便点开一个商品就可以看到)。为了方便,咱们直接把效果图进行了标记,在我们没有将鼠标移入app中,mask和big都是不可见的,当我们将鼠标移入app时,mask和big可见,且我们在app中移动鼠标时,mask对应的区域将在big中显示。(mask区域的宽高是相对固定的哈,就是app宽高的一半,本来想做一个鼠标动态划定mask大小的,但是我学识尚浅,还没办法完成)。
思路:
html部分我们只需要写一个id="app"的div,css部分咱啥都不用写,javascript部分,我们需要封装名为imageZoom的函数,我们在这个函数里面完成变量定义、变量获取、css样式设置、动态交互等一系列工作。mask和big都是咱们通过javascript中创造的div容器,这样有什么好处呢?按照常规思路,我们应该把mask、big跟app一起直接写在html中,也是毫无疑问可以实现相同效果的。但是我们通过javascript创造mask和big两个div容器,并且在javascript中设置了mask和big的样式,那以后我们要做图片放大器的时候,直接写一个类似于id="app"的容器,然后调用imageZoom()方法就可以了,啥mask啥big咱都不用管了,大大减少了我们的代码量啊,就非常方便。至于其他细节咱就不多说了,下面的代码中都有比较详细的注释,当然,也非常欢迎你直接问我。
实现代码:
<!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>
</head>
<body>
<div id="app"></div>
<script src="/通用资源/publicPackage.js"></script>
<script>
var app = document.getElementById('app');
imageZoom(app, "/img/jd.jpg", 175, 175);
function imageZoom(dom, url, width, height) {
var mask = document.createElement('div');
var big = document.createElement('div');
app.appendChild(mask);
app.appendChild(big);
css(app, {
width: width + 'px',
height: height + 'px',
position: 'relative',
backgroundImage: 'url('+ url +')',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
border: '1px solid #ccc'
});
css(mask, {
width: width / 2 + 'px',
height: height / 2 + 'px',
position: 'absolute',
backgroundColor: 'rgba(214, 235, 99, 0.4)',
cursor: 'move',
display: 'none'
});
css(big, {
width: width + 'px',
height: height + 'px',
position: 'absolute',
top: 0,
left: '100%',
backgroundImage: 'url('+ url +')',
backgroundRepeat: 'no-repeat',
display: 'none'
});
//鼠标移入
bindEvent(app, 'mouseenter', function(e) {
//鼠标进入时位置
//mask和big显示
css(mask, {display: 'block'});
css(big, {display: 'block'});
//鼠标移动时位置
bindEvent(dom, 'mousemove', function(e) {
var mouse_newX = e.clientX;
var mouse_newY = e.clientY;
//mask左顶点位置
var mask_left = mouse_newX - width / 4;
var mask_top = mouse_newY - height /4;
//边界处理(mask始终不能跑到app外头)
if (mask_left < 0) {
mask_left = 0;
} else if (mask_left > width / 2) {
mask_left = width / 2;
};
if(mask_top < 0) {
mask_top = 0;
} else if (mask_top > height / 2) {
mask_top = height / 2;
}
//mask位置(mask随鼠标移动而移动)
css(mask, {left: mask_left + 'px', top: mask_top + 'px'});
//big背景图设置
css(big, {
backgroundPositionX: '-' + mask_left * 2 + 'px',
backgroundPositionY: '-' + mask_top * 2 + 'px',
backgroundSize: '200%'
})
});
});
// 鼠标移出
bindEvent(app, 'mouseleave', function(e) {
css(mask, {display: 'none'});
css(big, {display: 'none'});
});
}
</script>
</body>
</html>
上述代码中是不是出现了一些没见过的方法,没见过就对了,因为是我自己日常封装的方法,比如这里的bindEvent()、css(),应该就用了这两个吧。其实这些自己封装的方法都可以通过我们学的属性、方法去代替,照样可以实现效果,封装一是为了兼顾ie浏览器,二当然是为了以后直接调用可以偷懒啦。这里双手奉上我封装的一些方法,也就是上面代码中引入的publicPackage.js文件哈,里面还有一些这里没有用到的方法,不管它就行,当然你也可以自己在其他地方使用。
/***
获取任何元素样式
@element 元素
@styleName 样式
***/
function getStyle(element, styleName) {
if (window.getComputedStyle) {
return getComputedStyle(element)[styleName];
} else{
var style= element.currentStyle;
if (style) {
styleName = styleName.replace(/-([a-z])?/g, function(match, $1) {
return $1.toUpperCase();
})
return style[styleName];
};
};
};
/***
实现任何事件绑定(DOM0、DOM2、ie)
@dom 元素
@type 事件
@fn 回调函数
***/
function bindEvent(dom, type, fn) {
if (dom.addEventListener) {
dom.addEventListener(type, fn);
} else if (dom.attachEvent) {
dom.attachEvent('on' + type, function(e) {
e.target = e.srcElement;
e.currentTarget = this;
fn.call(dom, e); //this指向
});
} else {
var oldFun = dom['on' + type];
dom['on' + type] = function(e) {
oldFun && oldFun(e || window.event);
fn(e || window.event);
};
};
};
/***
实现任何事件移除(DOM0、DOM2、ie)
@dom 元素
@type 事件
@fn 回调函数
***/
function removeEvent(dom, type, fn) {
if (dom.removeEventListener) {
dom.removeEventListener(type, fn);
} else if (dom.detachEvent) {
dom.detachEvent('on' + type, fn);
} else {
dom['on' + type] = null;
}
}
/***
封装防抖函数
@fn 函数
***/
function throttle_event(fn) {
clearTimeout(fn.__timebar);
fn.__timebar = setTimeout(fn, 200);
};
/***
封装节流函数
@fn 函数
***/
function throttle_time(fn) {
//lock为true不执行
if (fn.__lock) {
return;
};
fn.__lock = true;
fn();
setTimeout(function() {
fn.__lock = false;
},1000);
};
/***
实现设置样式方法 css(dom, width, 200px) 或 css(dom, {width: '200px', color: 'red'})
@dom 元素
@key 样式名
@value 样式值
***/
function css(dom, key, value) {
if (typeof key === 'string') {
dom.style[key] = value;
} else {
for (var name in key) {
css(dom, name, key[name]);
}
}
}
效果展示:
放大器效果展示
踩坑:
1.鼠标定位,刚开始我使用的是offsetX和offsetY(鼠标位于元素内部的位置)。我绑定的是app这个容器,那么获取的就是鼠标到容器app的距离,而且相比clientX和clientY(鼠标在视口中的位置),我都不用去减去元素到document的距离,就可以轻松得到mask在app中的绝对位置。但是使用offsetX和offsetY后我发现,mask会随鼠标移动这没有问题,但是mask移动时一直闪烁,我换成clientX和clientY之后就没有闪烁的问题了,咋还对offsetX和offsetY有歧视呢,我百思不得其解。后来,老师说了原因,鼠标同时位于mask和app中时,它会返回到mask和到app的距离,这就混乱了,所以就会闪烁,根本上是因为产生了两个位置信息它不知道听谁的。
2.如果我们给app这个容器设置margin:300px 300px;(300只是一个比方,设置多少都行),我们就发现mask不听鼠标使唤了,咋回事呢?还是clientX和clientY的问题,clientX和clientY是到视口的距离,我们没有给app容器设置margin的时候,clientX减去app宽的1/4,clientY减去app高的1/4,得到mask左顶点的绝对位置,这是没问题的。但是当app容器有margin时,它并不是紧贴着document的,clientX减去app宽的1/4,clientY减去app高的1/4,得到的距离包含了margin,而且数值非常大已经超过边界了,所以mask只能死死地困在右下角。而对于这一问题,目前我还没找到合适的办法,如果直接减去margin,没有滚动页面的时候可以实现,但是一旦滚动页面又不行了(clientX和clientY在页面滚动时在视口中的位置会变)。