今天,我们要实现一个放大镜效果,具体效果如下:
首先,先写结构。
我们需要两个div,分别存放放大前和放大后的图片。
其次就是需要一个可以随着鼠标移动的盒子,表示放大的区域。
为了方便叙述,所以我把存放放大前图片的盒子成为小盒子,存放放大后图片的盒子成为大盒子,随鼠标移动的盒子成为鼠标盒子
/*存放放大前图片的盒子*/
<div class="small">
<img src="./img/1.jpg" alt="">
/*放大区域*/
<span class="grayBox"></span>
</div>
/*存放当大后图片的盒子*/
<div class="big">
<img src="./img/1.jpg" alt="">
</div>
然后,我们写一些CSS样式美化一下界面。
/*我这里是为了偷懒,如果这样写会浪费一些性能*/
*{
margin: 0;
padding: 0;
}
.small {
width: 400px;
height: 400px;
position: relative;
margin-left: 200px;
margin-top: 100px;
border:4px solid #ddd;
box-shadow: 0 0 5px rgba(0,0,0,.5);
}
.small img{
width: 100%;
height: 100%;
}
.small .grayBox{
width: 200px;
height: 200px;
background-image: url(../img/1.jpg);
background-size:400px 400px;
background-position: 0 0;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
position: absolute;
left: 0;
top: 0;
display : none;
}
.big{
width: 400px;
height: 400px;
position: absolute;
left: 700px;
top: 100px;
border:4px solid #f10;
overflow: hidden;
display : none;
}
.big img{
position: absolute;
}
效果如下:
结构和样式写完了,我们要开始实现功能了。
我认为的面向对象的编程方式应该有有三个阶段,分别是OOA(面对对象分析)、OOD(面对对象设计)和OOP(面对对象编程)。
首先就是OOA阶段,找出自己想要的功能
//OOA
//初始化
//元素选择器
//事件绑定器
//显示隐藏器
//位置加工器
//跟随移动器
//比例获取器
大致就这多,数量也不一定准确,可以后期再进行添加补充。
其次是OOD阶段对于OOA阶段功能的设计。
//OOD
function Magnifier(){}
Magnifier.prototype = {
//更改指向方向
constructor : Magnifier,
//初始化
init : function(){},
//元素选择器
$ : function(){},
//事件绑定器
bindEvent : function(){},
//显示隐藏器
eleToggle : function(){},
//位置加工器
factoryPosition : function(){},
//跟随移动器
eleMove : function(){},
//比例获取器
getPercentage : function(){}
}
最后是OOP对OOD设计的完善与实现。
//OOP
function Magnifier(options){
//从实例对象里传入options,里面存储着
this.init(options);
}
Magnifier.prototype = {
//更改指向方向
constructor : Magnifier
}
- 初始化
//初始化
init : function(options){
for(var attr in options){
//使用for...in遍历对象,获取DOM
this[attr+"_ele"] = this.$(options[attr]);
}
//获取小盒子的属性,方便运算时使用,因为offset比较消耗性能,所以尽量在全局获取
this.small_box_offset = {
left : this.small_box_ele.offsetLeft,
top : this.small_box_ele.offsetTop,
width : parseInt(getComputedStyle(this.small_box_ele).width),
height : parseInt(getComputedStyle(this.small_box_ele).height)
}
//小盒子在使用offset获取其宽高会获取到边框,使宽高数据不精确
//鼠标盒子在鼠标未移入的时候是隐藏的,使用offset不能获取正确的数据
//所以使用getComputedStyle代替,直接获取元素的非行内样式的数据
//因为非行内样式宽高一般带着单位,需要使用parseInt去掉单位
this.cutting_box_offset = {
width : parseInt( getComputedStyle(this.cutting_box_ele).width ),
height : parseInt( getComputedStyle(this.cutting_box_ele).height ),
}
//让事件绑定器生效
this.bindEvent();
}
- 元素选择器
//元素选择器
$ : function(selector){
//元素选择器的作用返回是获取相对应属性的DOM
return docement.querySelector(selector);
}
- 事件绑定器
//事件绑定器
bindEvent : function(){
this.small_box_ele.addEventListener( "mouseover" , function(){
// 鼠标移入元素显示;
this.eleToggle("show");
}.bind(this));
this.small_box_ele.addEventListener( "mouseout" , function(){
// 鼠标移出元素隐藏;
this.eleToggle("hide");
}.bind(this));
// 元素运动;
this.small_box_ele.addEventListener("mousemove" , function( ev ){
var e = ev || event;
//获取鼠标距离屏幕的距离
var x = e.clientX ;
var y = e.clientY ;
//然后使用位置加工器加工位置
this.res = this.factoryPosition( x , y )
this.eleMove( this.res.x , this.res.y );
//改变大盒子图片的位置和宽高
this.getPercentage();
//让鼠标盒子的背景图片随着鼠标反向移动,实现鼠标盒子和小盒子背景的一致
this.cutting_box_ele.style.backgroundPosition =(-this.res.x)+"px "+(-this.res.y)+"px";
}.bind(this));
}
- 跟随移动器
//跟随移动器
eleMove : function(x,y){
//鼠标盒子随着传入的参数x和y进行移动
this.cutting_box_ele.style.left = x + "px";
this.cutting_box_ele.style.top = y + "px";
}
- 显示隐藏器
//显示隐藏器
eleToggle : function(type){
//判断鼠标盒子和大盒子在type是show的时候显示,在type为hide的时候隐藏
this.cutting_box_ele.style.display = type === "show" ? "block" : "none";
this.big_box_ele.style.display = type === "show" ? "block" : "none";
}
- 位置加工器
//位置加工器
factoryPosition : function(x,y){
//为了让光标永远在鼠标盒子的中心
//x是鼠标在屏幕的位置,减去小盒子到屏幕左侧的距离,剩下的是鼠标位置距离小盒子边框的距离
//在减去鼠标盒子宽高的一半,让鼠标盒子向左移动,让鼠标在水平方位上在鼠标盒子中间
//y也是如此,让鼠标在垂直方向上在鼠标盒子中间
var _left = x - this.small_box_offset.left - this.cutting_box_offset.width / 2;
var _top = y - this.small_box_offset.top - this.cutting_box_offset.height / 2
// 边界监测,防止鼠标盒子跟随鼠标移出小盒子
//小盒子的宽高减去鼠标盒子的宽高是鼠标盒子位置坐标的最大值
//大于最大值让其坐标等于最大值,以免鼠标盒子越界
var left_max = this.small_box_offset.width - this.cutting_box_offset.width;
var top_max = this.small_box_offset.height - this.cutting_box_offset.height;
//最小值边界监测;
_left = _left <= 0 ? 0 : _left;
_top = _top <= 0 ? 0 : _top;
//最大值边界检测
_left = _left >= left_max ? left_max : _left;
_top = _top >= top_max ? top_max : _top;
return {
x : _left,
y : _top
}
}
- 比例获取器
//比例获取器
getPercentage : function(){
//获得大盒子的宽高
this.big_box_wh ={
width : parseInt(getComputedStyle(this.big_box_ele).width),
height : parseInt(getComputedStyle(this.big_box_ele).height),
}
//获取小盒子和鼠标盒子的比例
var width_per = this.small_box_offset.width / this.cutting_box_offset.width ;
var height_per = this.small_box_offset.height / this.cutting_box_offset.height;
//按照相同的比例设置大框图片的宽高
this.big_img_ele.style.width = this.big_box_wh.width * width_per +"px";
this.big_img_ele.style.height = this.big_box_wh.height * height_per +"px";
//让大盒子的图片按照比例反方向移动
this.big_img_ele.style.left = -(this.res.x * width_per) +"px";
this.big_img_ele.style.top = -(this.res.y * height_per) +"px";
}
//创建实例对象
new Magnifier({
small_box : ".small",
cutting_box : ".grayBox",
big_box : ".big",
big_img : ".big img"
});
这样下来,简单的放大镜效果就做好了。
嗯?有小伙伴问我为什么和效果图不一样?
好吧,完善一下,最终代码如下
结构:
<div class="small_bg">
<div class="small">
<img src="./img/1.jpg" alt="" class="active">
<span class="grayBox"></span>
</div>
<div class="list">
<img src="./img/1.jpg" alt="">
<img src="./img/2.jpg" alt="">
<img src="./img/3.jpg" alt="">
<img src="./img/4.jpg" alt="">
</div>
</div>
<div class="big">
<img src="./img/1.jpg" alt="">
</div>
样式:
*{
margin: 0;
padding: 0;
}
.small {
width: 400px;
height: 400px;
position: relative;
}
.small_bg{
width: 400px;
height: 500px;
margin-left: 200px;
margin-top: 100px;
border:4px solid #ddd;
box-shadow: 0 0 5px rgba(0,0,0,.5);
}
.list{
height: 80px;
border-top: 4px #ddd solid;
}
.list img{
float: left;
width: 100px;
height: 100px;
}
.small img{
width: 100%;
height: 100%;
display: none;
}
.small img.active{
display: block;
}
.small .wrap{
width: 100%;
height: 100%;
position: absolute;
z-index: 999;
}
.small .grayBox{
display: none;
width: 200px;
height: 200px;
background-image: url(../img/1.jpg);
background-size:400px 400px;
background-position: 0 0;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
position: absolute;
left: 0;
top: 0;
}
.big{
width: 400px;
height: 400px;
position: absolute;
left: 700px;
top: 100px;
border:4px solid #f10;
display: none;
overflow: hidden;
}
.big img{
position: absolute;
}
行为:
unction Magnifier( options ) {
this.init( options );
}
Magnifier.prototype = {
constructor : Magnifier ,
init : function( options ){
for(var attr in options){
this[attr+"_ele"] = this.$(options[attr]);
}
this.small_box_offset = {
left : this.small_box_ele.offsetLeft,
top : this.small_box_ele.offsetTop,
width : parseInt(getComputedStyle(this.small_box_ele).width),
height : parseInt(getComputedStyle(this.small_box_ele).height)
}
this.cutting_box_offset = {
width : parseInt( getComputedStyle(this.cutting_box_ele).width ),
height : parseInt( getComputedStyle(this.cutting_box_ele).height ),
}
this.bindEvent();
},
$ : function(selector){
return document.querySelector(selector);
},
bindEvent : function(){
this.small_box_ele.addEventListener( "mouseover" , function(){
this.eleToggle("show");
}.bind(this));
this.small_box_ele.addEventListener( "mouseout" , function(){
this.eleToggle("hide");
}.bind(this));
this.small_box_ele.addEventListener("mousemove" , function( ev ){
var e = ev || event;
var x = e.clientX ;
var y = e.clientY ;
this.res = this.factoryPosition( x , y )
this.eleMove( this.res.x , this.res.y );
this.getPercentage();
this.cutting_box_ele.style.backgroundPosition =(-this.res.x)+"px "+(-this.res.y)+"px";
}.bind(this));
this.small_box_ele.addEventListener("mousewheel",function( ev){
var e = ev||event;
this.cuttingChange(e.wheelDelta > 0 ? "narrow" : "enlarge");
}.bind(this));
},
eleToggle : function( type ){
this.cutting_box_ele.style.display = type === "show" ? "block" : "none";
this.big_box_ele.style.display = type === "show" ? "block" : "none";
},
eleMove : function( x , y ){
this.cutting_box_ele.style.left = x + "px";
this.cutting_box_ele.style.top = y + "px";
},
getPercentage : function(){
this.big_box_wh ={
width : parseInt(getComputedStyle(this.big_box_ele).width),
height : parseInt(getComputedStyle(this.big_box_ele).height),
}
var width_per = this.small_box_offset.width / this.cutting_box_offset.width ;
var height_per = this.small_box_offset.height / this.cutting_box_offset.height;
this.big_img_ele.style.width = this.big_box_wh.width * width_per +"px";
this.big_img_ele.style.height = this.big_box_wh.height * height_per +"px";
this.big_img_ele.style.left = -(this.res.x * width_per) +"px";
this.big_img_ele.style.top = -(this.res.y * height_per) +"px";
},
factoryPosition : function( x , y ){
var _left = x - this.small_box_offset.left - this.cutting_box_offset.width / 2;
var _top = y - this.small_box_offset.top - this.cutting_box_offset.height / 2
var left_max = this.small_box_offset.width - this.cutting_box_offset.width;
var top_max = this.small_box_offset.height - this.cutting_box_offset.height;
_left = _left <= 0 ? 0 : _left;
_top = _top <= 0 ? 0 : _top;
_left = _left >= left_max ? left_max : _left;
_top = _top >= top_max ? top_max : _top;
return {
x : _left,
y : _top
}
}
}
new Magnifier({
small_box : ".small",
cutting_box : ".grayBox",
big_box : ".big",
big_img : ".big img"
});
var btns = document.querySelectorAll(".list img");
var imgs = document.querySelectorAll(".big img,.small img");
var small = document.querySelector(".grayBox");
for( var i= 0 ; i < btns.length ; i++){
btns[i].addEventListener( "click" , function(){
var src = this.getAttribute("src");
for(var k = 0 ; k < imgs.length ; k ++){
imgs[k].src = src;
small.style.backgroundImage = "url(../img/"+ i +".jpg)";
}
})
}