欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

cloudgamer出品ImageZoom 图片放大效果

程序员文章站 2022-04-12 11:18:24
一般用于放大查看商品图片,在凡客,京东商城,阿里巴巴等都有类似的效果。 好处是能在原图附近对图片进行局部放大查看,而且可以通过鼠标控制查看的部位。 前一阵子看到sohigh...
一般用于放大查看商品图片,在凡客,京东商城,阿里巴巴等都有类似的效果。
好处是能在原图附近对图片进行局部放大查看,而且可以通过鼠标控制查看的部位。
前一阵子看到sohighthesky的图片放大效果,心血来潮自己也写一个看看。
这个程序有以下特点:
1,支持使用原图放大或新图片设置大图;
2,大图完成载入前使用原图放大代替,减少操作等待时间;
3,支持鼠标滚动缩放大图;
4,可以通过设置显示范围或显示框大小设置显示尺寸;
5,可以设置是否自动隐藏显示框;
6,支持插件形式的扩展来实现更多的功能(下一篇再详细介绍)。
兼容:ie6/7/8, firefox 3.6.2, opera 10.51, safari 4.0.4, chrome 4.1
复制代码 代码如下:

var styles;
if ( !viewer.clientwidth ) {
var style = viewer.style;
styles = {
display: style.display,
position: style.position,
visibility: style.visibility
};
$$d.setstyle( viewer, {
display: "block", position: "absolute", visibility: "hidden"
});
}
this._viewerwidth = viewer.clientwidth;
this._viewerheight = viewer.clientheight;
if ( styles ) { $$d.setstyle( viewer, styles ); }
rangewidth = math.ceil( this._viewerwidth / scale );
rangeheight = math.ceil( this._viewerheight / scale );

注意,显示范围是通过clientwidth/clientheight来获取的。
如果显示框是display为none的隐藏状态,就不能直接获取clientwidth/clientheight。
这种情况下,程序用以下方法获取:
1,记录display/position/visibility的原始值;
2,分别设为"block"/"absolute"/"hidden",这是既能隐藏也能占位的状态;
3,获取参数;
4,重新设回原始值,恢复原来的状态。
得到显示范围后,再配合比例就能得到范围参数了。
ps:这是通用的获取不占位元素尺寸参数的方法,jquery的css也是用这个方法获取width/height的。
比例计算后可能会得到小数,而尺寸大小只能是整数,程序一律使用math.ceil来取整。
【放大效果】
所有东西都设置好后,就可以执行start设置触发程序了。
程序会自动执行start方法,里面主要是给原图对象的mouseover/mousemove绑定_start程序:
var image = this._image, start = this._start;
$$e.addevent( image, "mouseover", start );
$$e.addevent( image, "mousemove", start );
分别对应移入原图对象和在原图对象上移动的情况。
ps:如果使用attachevent的话还要注意重复绑定同一函数的问题,这里的addevent就没有这个问题。
绑定的_start程序,主要是进行一些事件的解绑和绑定:
复制代码 代码如下:

$$e.removeevent( image, "mouseover", this._start );
$$e.removeevent( image, "mousemove", this._start );
$$e.addevent( document, "mousemove", this._move );
$$e.addevent( document, "mouseout", this._out );

为了在移出窗口时能结束放大效果,给document的mouseout绑定了_out程序:
复制代码 代码如下:

this._out = $$f.bindaseventlistener( function(e){
if ( !e.relatedtarget ) this._end();
}, this );

当鼠标移出document会触发mouseout,如果当前relatedtarget是null的话,就延时执行_end结束程序:
var othis = this, end = function(){ othis._end(); };
this._end = function(){ othis._timer = settimeout( end, othis.delay ); };
在_end程序中,会先执行stop方法,在里面移除所有可能绑定的事件,再执行start方法继续等待触发。
而mousemove绑定的_move移动程序,主要用来实现鼠标移动到哪里就放大哪里的功能。
为适应更多的情况(例如扩展篇的其他模式),把它绑定到document上,但也因此不能用mouseout事件来触发移出程序。
程序通过鼠标和原图的坐标比较,来判断鼠标是否移出原图对象范围:
复制代码 代码如下:

var x = e.pagex, y = e.pagey, rect = this._rect;
if ( x < rect.left || x > rect.right || y < rect.top || y > rect.bottom ) {
this._end();
} else {
...
}

如果鼠标移出原图对象的话,就执行_end结束放大效果。
如果鼠标在原图对象上移动,就计算坐标,并通过_repair程序把坐标转化成定位需要的值。
最后设置大图的left/top定位使显示框显示要放大的部位。
ps:我尝试过用scrollleft/scrolltop来做定位,但发现这样在ie中会像锯齿那样移动,放得越大越明显,所以放弃。
【鼠标滚动缩放】
如果设置mouse属性为true,就会开启鼠标滚动缩放功能。
在执行放大效果期间,可以通过滚动鼠标滚轮对大图进行缩放处理。
其实就是根据滚轮动参数的变化来修改放大比例。
关于鼠标滚动事件,在slider中也提过,不过那时只分析了ie和ff的区别,这里再分析一下。
首先ie是用mousewheel绑定事件的,使用event的wheeldelta来获取滚动参数。
其他浏览器用以下代码测试:
复制代码 代码如下:

<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml">
<body style="height:1000px;">
<script>
function test(e){ alert(e.type+":"+e.detail+"_"+e.wheeldelta) }
document.addeventlistener( "dommousescroll", test, false );
document.addeventlistener( "mousewheel", test, false );
</script>
</body>
</html>

向下滚动一下,可以得到以下结果:
ff:dommousescroll:3_undefined
opera:mousewheel:3_-120
chrome/safari:mousewheel:0_-120
可以看到事件的绑定,ff只支持dommousescroll,其他就只支持mousewheel。
而滚动参数的获取,ff只支持detail,opera两种都支持,chrome/safari就支持wheeldelta。
ps:不明白chrome/safari的detail为什么是0,有其他用途?
而dommousescroll跟mousewheel还有一个不同是前者不能直接绑定元素,后者可以。
即可以elem.onmousewheel,但不能elem.ondommousescroll。
根据以上分析,在_start程序里是这样把_mouse程序绑定到document的滚动事件中:
this.mouse && $$e.addevent( document, $$b.firefox ? "dommousescroll" : "mousewheel", this._mouse );
在_mouse程序里面根据滚动参数和自定义的rate缩放比率得到新的放大比例:
this._scale += ( e.wheeldelta ? e.wheeldelta / (-120) : (e.detail || 0) / 3 ) * this.rate;
修改比例时,程序参数也需要重新计算。
由于_rangewidth/_rangeheight会影响计算的过程,要重新恢复成自定义的默认值:
var opt = this.options;
this._rangewidth = opt.rangewidth;
this._rangeheight = opt.rangeheight;
然后执行_initsize和_initdata重新设置尺寸和参数,再执行_move重新定位。
最后记得用preventdefault防止触发页面滚动。
使用技巧
【图片设置】
程序支持大图使用原图放大或用新大图。
如果用新大图而且图片比较大的话,强烈建议设置放大比例,这样程序会自动在大图载入前先使用原图放大,这样就不用等待大图加载完成。
还要注意新大图本身的宽高比例要跟原图一致,否则就对不准坐标了,使用原图放大就没有这个问题。
【显示框设置】
有两个方法可以设置显示框的尺寸:
要固定显示范围的话,先设置rangewidth/rangeheight,程序会根据显示范围和放大比例计算显示框的尺寸;
要用显示框当前的尺寸来显示的话,只要不设置rangewidth/rangeheight或设为0就可以了。
【reset】
由于各个属性和对象之间有很多的关联,很多属性不能直接修改。
程序设置了一个reset方法专门用来修改这类属性的。
如果程序加载完成后又修改了影响程序计算的样式,例如原图大小,显示框大小等,也要执行一次reset来重新设置参数和属性。
【浮动定位】
程序没有设置显示框浮动定位的功能,需要的话可以自行添加。
简单的定位可以参考实例的方法,还要小心覆盖select的问题。
如果要更复杂的浮动定位,可以参考“浮动定位提示效果”。
【opera的bug】
测试中发现opera 10.10有两个bug。
分别是img元素设置透明时会看到背景图,用js修改鼠标样式会有问题。
不过这两个问题在10.50都已经修复了,还没升级的赶快升啦。
【maxthon的bug】
用maxthon 2.5.1测试时发现一个问题,测试以下代码:
复制代码 代码如下:

<div id="t" style="width:50px;"></div>
<script>
var t=document.getelementbyid("t");
t.clientwidth;
t.style.display="none";
alert(t.clientwidth);
</script>

一般来说用display隐藏后,clientwidth应该是0的,但maxthon貌似没有处理这个情况。
这会影响到程序中clientwidth的判断,不过对一般使用没什么影响。
我已经提交了这个问题,不知会不会处理。
使用说明
实例化时,必须有一个img元素作为原图对象,和一个容器作为显示框:
var iz = new imagezoom( "idimage", "idviewer" );
可选参数用来设置系统的默认属性,包括:
属性: 默认值//说明
mode: "simple",//模式
scale: 0,//比例(大图/原图)
max: 10,//最大比例
min: 1.5,//最小比例
originpic: "",//原图地址
zoompic: "",//大图地址
rangewidth: 0,//显示范围宽度
rangeheight:0,//显示范围高度
delay: 20,//延迟结束时间
autohide: true,//是否自动隐藏
mouse: false,//鼠标缩放
rate: .2,//鼠标缩放比率
onload: $$.emptyfunction,//加载完成时执行
onstart: $$.emptyfunction,//开始放大时执行
onmove: $$.emptyfunction,//放大移动时执行
onend: $$.emptyfunction//放大结束时执行
其中模式的使用在下一篇扩展篇再说明。
初始化后,scale、max、min、originpic、zoompic、rangewidth、rangeheight这些属性需要用reset方法来修改。
还提供了以下方法:
start:开始放大程序(程序会自动执行);
stop:停止放大程序;
reset:修改设置;
dispose:销毁程序。
程序源码
复制代码 代码如下:

var imagezoom = function(image, viewer, options) {
this._initialize( image, viewer, options );
this._initmode( this.options.mode );
this._oninit();
this._initload();
};
imagezoom.prototype = {
//初始化程序
_initialize: function(image, viewer, options) {
this._image = $$(image);//原图
this._zoom = document.createelement("img");//显示图
this._viewer = $$(viewer);//显示框
this._viewerwidth = 0;//显示框宽
this._viewerheight = 0;//显示框高
this._preload = new image();//预载对象
this._rect = null;//原图坐标
this._repairleft = 0;//显示图x坐标修正
this._repairtop = 0;//显示图y坐标修正
this._rangewidth = 0;//显示范围宽度
this._rangeheight = 0;//显示范围高度
this._timer = null;//计时器
this._loaded = false;//是否加载
this._substitute = false;//是否替换
var opt = this._setoptions(options);
this._scale = opt.scale;
this._max = opt.max;
this._min = opt.min;
this._originpic = opt.originpic;
this._zoompic = opt.zoompic;
this._rangewidth = opt.rangewidth;
this._rangeheight = opt.rangeheight;
this.delay = opt.delay;
this.autohide = opt.autohide;
this.mouse = opt.mouse;
this.rate = opt.rate;
this.onload = opt.onload;
this.onstart = opt.onstart;
this.onmove = opt.onmove;
this.onend = opt.onend;
var othis = this, end = function(){ othis._end(); };
this._end = function(){ othis._timer = settimeout( end, othis.delay ); };
this._start = $$f.bindaseventlistener( this._start, this );
this._move = $$f.bindaseventlistener( this._move, this );
this._mouse = $$f.bindaseventlistener( this._mouse, this );
this._out = $$f.bindaseventlistener( function(e){
if ( !e.relatedtarget ) this._end();
}, this );
},
//设置默认属性
_setoptions: function(options) {
this.options = {//默认值
mode: "simple",//模式
scale: 0,//比例(大图/原图)
max: 10,//最大比例
min: 1.5,//最小比例
originpic: "",//原图地址
zoompic: "",//大图地址
rangewidth: 0,//显示范围宽度
rangeheight:0,//显示范围高度
delay: 20,//延迟结束时间
autohide: true,//是否自动隐藏
mouse: false,//鼠标缩放
rate: .2,//鼠标缩放比率
onload: $$.emptyfunction,//加载完成时执行
onstart: $$.emptyfunction,//开始放大时执行
onmove: $$.emptyfunction,//放大移动时执行
onend: $$.emptyfunction//放大结束时执行
};
return $$.extend(this.options, options || {});
},
//根据模式初始化函数属性
_initmode: function(mode) {
mode = $$.extend({
options:{},
init: $$.emptyfunction,
load: $$.emptyfunction,
start: $$.emptyfunction,
end: $$.emptyfunction,
move: $$.emptyfunction,
dispose:$$.emptyfunction
}, (imagezoom._mode || {})[ mode.tolowercase() ] || {} );
this.options = $$.extend( mode.options, this.options );
this._oninit = mode.init;
this._onload = mode.load;
this._onstart = mode.start;
this._onend = mode.end;
this._onmove = mode.move;
this._ondispose = mode.dispose;
},
//初始化加载
_initload: function() {
var image = this._image, originpic = this._originpic,
useorigin = !this._zoompic && this._scale,
loadimage = $$f.bind( useorigin ? this._loadoriginimage : this._loadimage, this );
//设置自动隐藏
if ( this.autohide ) { this._viewer.style.display = "none"; }
//先加载原图
if ( originpic && originpic != image.src ) {//使用自定义地址
image.onload = loadimage;
image.src = originpic;
} else if ( image.src ) {//使用元素地址
if ( !image.complete ) {//未载入完
image.onload = loadimage;
} else {//已经载入
loadimage();
}
} else {
return;//没有原图地址
}
//加载大图
if ( !useorigin ) {
var preload = this._preload, zoompic = this._zoompic || image.src,
loadpreload = $$f.bind( this._loadpreload, this );
if ( zoompic != preload.src ) {//新地址重新加载
preload.onload = loadpreload;
preload.src = zoompic;
} else {//正在加载
if ( !preload.complete ) {//未载入完
preload.onload = loadpreload;
} else {//已经载入
this._loadpreload();
}
}
}
},
//原图放大加载程序
_loadoriginimage: function() {
this._image.onload = null;
this._zoom.src = this._image.src;
this._initloaded();
},
//原图加载程序
_loadimage: function() {
this._image.onload = null;
if ( this._loaded ) {//大图已经加载
this._initloaded();
} else {
this._loaded = true;
if ( this._scale ) {//有自定义比例才用原图放大替换大图
this._substitute = true;
this._zoom.src = this._image.src;
this._initloaded();
}
}
},
//大图预载程序
_loadpreload: function() {
this._preload.onload = null;
this._zoom.src = this._preload.src;
if ( this._loaded ) {//原图已经加载
//没有使用替换
if ( !this._substitute ) { this._initloaded(); }
} else {
this._loaded = true;
}
},
//初始化加载设置
_initloaded: function(src) {
//初始化显示图
this._initsize();
//初始化显示框
this._initviewer();
//初始化数据
this._initdata();
//开始执行
this._onload();
this.onload();
this.start();
},
//初始化显示图尺寸
_initsize: function() {
var zoom = this._zoom, image = this._image, scale = this._scale;
if ( !scale ) { scale = this._preload.width / image.width; }
this._scale = scale = math.min( math.max( this._min, scale ), this._max );
//按比例设置显示图大小
zoom.width = math.ceil( image.width * scale );
zoom.height = math.ceil( image.height * scale );
},
//初始化显示框
_initviewer: function() {
var zoom = this._zoom, viewer = this._viewer;
//设置样式
var styles = { padding: 0, overflow: "hidden" }, p = $$d.getstyle( viewer, "position" );
if ( p != "relative" && p != "absolute" ){ styles.position = "relative"; };
$$d.setstyle( viewer, styles );
zoom.style.position = "absolute";
//插入显示图
if ( !$$d.contains( viewer, zoom ) ){ viewer.appendchild( zoom ); }
},
//初始化数据
_initdata: function() {
var zoom = this._zoom, image = this._image, viewer = this._viewer,
scale = this._scale, rangewidth = this._rangewidth, rangeheight = this._rangeheight;
//原图坐标
this._rect = $$d.rect( image );
//修正参数
this._repairleft = image.clientleft + parseint($$d.getstyle( image, "padding-left" ));
this._repairtop = image.clienttop + parseint($$d.getstyle( image, "padding-top" ));
//设置范围参数和显示框大小
if ( rangewidth > 0 && rangeheight > 0 ) {
rangewidth = math.ceil( rangewidth );
rangeheight = math.ceil( rangeheight );
this._viewerwidth = math.ceil( rangewidth * scale );
this._viewerheight = math.ceil( rangeheight * scale );
$$d.setstyle( viewer, {
width: this._viewerwidth + "px",
height: this._viewerheight + "px"
});
} else {
var styles;
if ( !viewer.clientwidth ) {//隐藏
var style = viewer.style;
styles = {
display: style.display,
position: style.position,
visibility: style.visibility
};
$$d.setstyle( viewer, {
display: "block", position: "absolute", visibility: "hidden"
});
}
this._viewerwidth = viewer.clientwidth;
this._viewerheight = viewer.clientheight;
if ( styles ) { $$d.setstyle( viewer, styles ); }
rangewidth = math.ceil( this._viewerwidth / scale );
rangeheight = math.ceil( this._viewerheight / scale );
}
this._rangewidth = rangewidth;
this._rangeheight = rangeheight;
},
//开始
_start: function() {
cleartimeout( this._timer );
var viewer = this._viewer, image = this._image, scale = this._scale;
viewer.style.display = "block";
this._onstart();
this.onstart();
$$e.removeevent( image, "mouseover", this._start );
$$e.removeevent( image, "mousemove", this._start );
$$e.addevent( document, "mousemove", this._move );
$$e.addevent( document, "mouseout", this._out );
this.mouse && $$e.addevent( document, $$b.firefox ? "dommousescroll" : "mousewheel", this._mouse );
},
//移动
_move: function(e) {
cleartimeout( this._timer );
var x = e.pagex, y = e.pagey, rect = this._rect;
if ( x < rect.left || x > rect.right || y < rect.top || y > rect.bottom ) {
this._end();//移出原图范围
} else {
var zoom = this._zoom,
pos = this._repair(
x - rect.left - this._repairleft,
y - rect.top - this._repairtop
);
this._onmove( e, pos );
//设置定位
zoom.style.left = pos.left + "px";
zoom.style.top = pos.top + "px";
this.onmove();
}
},
//修正坐标
_repair: function(x, y) {
var scale = this._scale, zoom = this._zoom,
viewerwidth = this._viewerwidth,
viewerheight = this._viewerheight;
//修正坐标
x = math.ceil( viewerwidth / 2 - x * scale );
y = math.ceil( viewerheight / 2 - y * scale );
//范围限制
x = math.min( math.max( x, viewerwidth - zoom.width ), 0 );
y = math.min( math.max( y, viewerheight - zoom.height ), 0 );
return { left: x, top: y };
},
//结束
_end: function() {
this._onend();
this.onend();
if ( this.autohide ) { this._viewer.style.display = "none"; }
this.stop();
this.start();
},
//鼠标缩放
_mouse: function(e) {
this._scale += ( e.wheeldelta ? e.wheeldelta / (-120) : (e.detail || 0) / 3 ) * this.rate;
var opt = this.options;
this._rangewidth = opt.rangewidth;
this._rangeheight = opt.rangeheight;
this._initsize();
this._initdata();
this._move(e);
e.preventdefault();
},
//开始
start: function() {
if ( this._viewerwidth && this._viewerheight ) {
var image = this._image, start = this._start;
$$e.addevent( image, "mouseover", start );
$$e.addevent( image, "mousemove", start );
}
},
//停止
stop: function() {
cleartimeout( this._timer );
$$e.removeevent( this._image, "mouseover", this._start );
$$e.removeevent( this._image, "mousemove", this._start );
$$e.removeevent( document, "mousemove", this._move );
$$e.removeevent( document, "mouseout", this._out );
$$e.removeevent( document, $$b.firefox ? "dommousescroll" : "mousewheel", this._mouse );
},
//修改设置
reset: function(options) {
this.stop();
var viewer = this._viewer, zoom = this._zoom;
if ( $$d.contains( viewer, zoom ) ) { viewer.removechild( zoom ); }
var opt = $$.extend( this.options, options || {} );
this._scale = opt.scale;
this._max = opt.max;
this._min = opt.min;
this._originpic = opt.originpic;
this._zoompic = opt.zoompic;
this._rangewidth = opt.rangewidth;
this._rangeheight = opt.rangeheight;
//重置属性
this._loaded = this._substitute = false;
this._rect = null;
this._repairleft = this._repairtop =
this._viewerwidth = this._viewerheight = 0;
this._initload();
},
//销毁程序
dispose: function() {
this._ondispose();
this.stop();
if ( $$d.contains( this._viewer, this._zoom ) ) {
this._viewer.removechild( this._zoom );
}
this._image.onload = this._preload.onload =
this._image = this._preload = this._zoom = this._viewer =
this.onload = this.onstart = this.onmove = this.onend =
this._start = this._move = this._end = this._out = null
}
}

转载请注明出处:http://www.cnblogs.com/cloudgamer/

如有任何建议或疑问,欢迎留言讨论。