天天看点

ImageZoom 图片放大效果(扩展篇)

上一篇imagezoom 已经对图片放大效果做了详细的分析,这次在imagezoom的基础上进行扩展,实现更多的效果。

主要扩展了原图和显示框的展示模式,有以下几种模式:

"follow" 跟随模式:显示框能跟随鼠标移动的效果;

"handle" 拖柄模式:原图上有一个拖柄来标记显示范围;

"cropper" 切割模式:原图用不透明的来标记显示范围,其他部分用半透明显示;

"handle-cropper" 拖柄切割模式:拖柄模式和切割模式的混合版,同时用透明度和拖柄来标记显示范围。

当然更多的扩展等待你的想象力来发掘。

兼容:ie6/7/8, firefox 3.6.2, opera 10.51, safari 4.0.4, chrome 4.1

效果预览

 模式选择:

一般模式 跟随模式 手柄模式 切割模式 手柄切割模式 

ImageZoom 图片放大效果(扩展篇)

仿凡客诚品(vancl)商品图片放大效果

ImageZoom 图片放大效果(扩展篇)
ImageZoom 图片放大效果(扩展篇)
ImageZoom 图片放大效果(扩展篇)

程序说明

【扩展模式】

上次imageslazyload 使用了继承做扩展,这次用插件的形式来做扩展。

先看看基础模式,这些模式是保存在imagezoom._mode中的,类似这样的结构:

ImageZoom 图片放大效果(扩展篇)

imagezoom._mode = {  

    模式名: {  

        options: {  

            ...  

        },  

        methods: {  

            init: function() {  

                ...  

            },  

        }  

    },  

    ...  

}  

其中模式名就是基础模式的名字,options是可选参数扩展,methods是程序结构的扩展。

基础模式包含"follow", "handle"和"cropper"模式,后面再详细介绍。

methods包含要扩展的钩子程序,是扩展的主要部分。

ps:这里说的模式不是“设计模式”里面的模式。

扩展需要在程序初始化时进行,要放在_initialize程序之前执行。

为了不影响原程序的结构,这里用织入法在_initialize之前插入一段程序:

ImageZoom 图片放大效果(扩展篇)

imagezoom.prototype._initialize = (function(){  

    var init = imagezoom.prototype._initialize,  

        ...;  

    return function(){  

        ...  

        init.apply( this, arguments );  

    }  

})();  

原理就是先保存原来的函数,插入一段程序组成新函数,然后重新替换原来的函数。

考虑到组合基础模式的情况,使用了一个对象保存真正使用的模式:

ImageZoom 图片放大效果(扩展篇)

mode = imagezoom._mode,  

modes = {  

    "follow": [ mode.follow ],  

    "handle": [ mode.handle ],  

    "cropper": [ mode.cropper ],  

    "handle-cropper": [ mode.handle, mode.cropper ]  

};  

可以看到"handle-cropper"模式其实就是"handle"和"cropper"的组合模式。

插入的程序的主要任务是根据设定好的基础模式,进行扩展:

ImageZoom 图片放大效果(扩展篇)

var options = arguments[2];  

if ( options && options.mode && modes[ options.mode ] ) {  

    $$a.foreach( modes[ options.mode ], function( mode ){  

        $$.extend( options, mode.options, false );  

        $$a.foreach( mode.methods, function( method, name ){  

            $$ce.addevent( this, name, method );  

        }, this );  

    }, this );  

}   

首先扩展options可选参数对象,由于可选参数是第三个参数,所以用arguments[2]获取。

extend的第三个参数设为false,说明不重写相同属性,即保留自定义的属性值。

然后把methods里面的方法作为钩子函数逐个添加到程序中。

methods可以包含init, load, start, repair, move, end, dispose这几个方法,分别对应imagezoom中初始化、加载、开始、修正、移动、结束和销毁事件。

在扩展时,不同的事件执行不同的任务:

init初始化函数:用来设置扩展需要的属性,注意这些属性不要跟imagezoom本身的属性冲突了,即重名。

load加载函数:图片加载完成,相关参数也设置完成,主要做执行放大效果前的准备工作。

start开始函数:触发放大效果时执行的。

repair修正函数:用于修正大图定位的坐标值。

move移动函数:触发放大效果后,鼠标移动时执行的。

end结束函数就:结束放大效果时执行的。

dispose销毁函数:在移除程序时清理程序的。

ps:具体位置参考imagezoom中使用$$ce.fireevent的部分。

可以看到这里用了织入法(weave)和钩子法(hook)对程序做扩展。

织入法是一种aop,可以在不改变原程序的基础上进行扩展,但只能在函数前面或后面加入程序。

而钩子法必须在原程序中设置好对应钩子才能配合使用,但位置相对灵活。

【跟随模式】

在"follow"跟随模式中,进行放大时,显示框会跟随鼠标移动,就像拿着放大镜看的效果。

首先显示框要绝对定位,要实现显示框跟随鼠标移动,只要在move中设置对应的left/top就行了:

ImageZoom 图片放大效果(扩展篇)

var style = this._viewer.style;  

style.left = e.pagex - this._repairfollowleft + "px";  

style.top = e.pagey - this._repairfollowtop + "px";  

其中pagex/pagey是鼠标当前的坐标,_repairfollowleft/_repairfollowtop是坐标的修正参数。

修正参数是在load中设置的,如果显示框隐藏的话,用上一篇获取显示范围的方法获取参数:

ImageZoom 图片放大效果(扩展篇)

if ( !viewer.offsetwidth ) {  

    styles = { display: style.display, visibility: style.visibility };  

    $$d.setstyle( viewer, { display: "block", visibility: "hidden" });  

...  

if ( styles ) { $$d.setstyle( viewer, styles ); }  

 为了跟随时,让鼠标固定在显示框中心位置,先根据显示框的offsetwidth/offsetheight修正参数:

ImageZoom 图片放大效果(扩展篇)

this._repairfollowleft = viewer.offsetwidth / 2;  

this._repairfollowtop = viewer.offsetheight / 2;  

如果显示框的offsetparent不是body,还需要根据offsetparent修正坐标:

ImageZoom 图片放大效果(扩展篇)

if ( !/body|html/.test( viewer.offsetparent.nodename ) ) {  

    var parent = viewer.offsetparent, rect = $$d.rect( parent );  

    this._repairfollowleft += rect.left + parent.clientleft;  

    this._repairfollowtop += rect.top + parent.clienttop;  

}    

ps:在maxthon测试时发现body子元素的offsetparent不是body而是html。

为了在移除程序后,能恢复显示框的样式,在load程序中做了样式的备份:

ImageZoom 图片放大效果(扩展篇)

var viewer = this._viewer, style = viewer.style, styles;  

this._stylesfollow = {  

    left: style.left, top: style.top, position: style.position  

 并在dispose中恢复:

ImageZoom 图片放大效果(扩展篇)

$$d.setstyle( this._viewer, this._stylesfollow );  

 现在已经达到了基本的效果,但由于大图移动范围的限制,当鼠标移动到接近边界时,大图就卡住不会动了。

为了实现在鼠标移动时,大图会持续变化的效果,在repair中修正了移动坐标:

ImageZoom 图片放大效果(扩展篇)

pos.left = ( viewerwidth / 2 - pos.left ) * ( viewerwidth / zoom.width - 1 );  

pos.top = ( viewerheight / 2 - pos.top ) * ( viewerheight / zoom.height - 1 );  

 原理稍有些复杂,以水平坐标为例,先看下图:

ImageZoom 图片放大效果(扩展篇)

大方框代表大图对象,小方框代表显示框。

当前位置是根据鼠标坐标得到的实际显示的位置,目标位置想要实现效果的位置。

有一些物理或几何知识应该明白这个等式:x / y = m / n

可以推出:y = x * n / m = x * ( zoom.width -  viewerwidth ) / zoom.height

x当前坐标可以通过pos.left来得到:x = viewerwidth / 2 - pos.left

最后得到:left = -y = ( viewerwidth / 2 - pos.left ) * ( viewerwidth / zoom.width - 1 )

垂直坐标也差不多。

【拖柄模式】

拖柄是一个层,在原图上面,用来表示显示范围在原图的位置和范围。

显示范围可以根据_rangewidth/_rangeheight获取。

至于位置的指定可以根据鼠标坐标或大图定位坐标来设置。

如果鼠标坐标的话还必须做其他处理,例如范围控制,而根据大图定位坐标相对就方便准确得多,程序也是用后面一个方法。

首先在init定义一个_handle拖柄对象:

ImageZoom 图片放大效果(扩展篇)

if ( !handle ) {  

    var body = document.body;  

    handle = body.insertbefore(this._viewer.clonenode(false), body.childnodes[0]);  

    handle.id = "";  

    handle["_createbyhandle"] = true;  

} else {  

    var style = handle.style;  

    this._styleshandle = {  

        left: style.left, top: style.top, position: style.position,  

        display: style.display, visibility: style.visibility,  

        padding: style.padding, margin: style.margin,  

        width: style.width, height: style.height  

    };  

如果有自定义拖柄对象,就像跟随模式那样备份拖柄的样式。

否则就复制显示框作为拖柄对象,并添加"_createbyhandle"属性作标记,方便在dispose中移除。

在load时,设置拖柄样式:

ImageZoom 图片放大效果(扩展篇)

$$d.setstyle( handle, {  

    position: "absolute",  

    width: this._rangewidth + "px",  

    height: this._rangeheight + "px",  

    display: "block",  

    visibility: "hidden"  

});  

绝对定位是必须的,并根据_rangewidth/_rangeheight设置尺寸。

设置display和visibility是为了下面获取参数。

先根据原图坐标获取修正参数:

ImageZoom 图片放大效果(扩展篇)

this._repairhandleleft = rect.left + this._repairleft - handle.clientleft;  

this._repairhandletop = rect.top + this._repairtop - handle.clienttop;  

 和跟随模式类似,也要做offsetparent定位的修正:

ImageZoom 图片放大效果(扩展篇)

if ( handle.offsetparent.nodename.touppercase() != "body" ) {  

    var parent = handle.offsetparent, rect = $$d.rect( parent );  

    this._repairhandleleft -= rect.left + parent.clientleft;  

    this._repairhandletop -= rect.top + parent.clienttop;  

 然后重新隐藏:

ImageZoom 图片放大效果(扩展篇)

$$d.setstyle( handle, { display: "none", visibility: "visible" });  

 在start时,显示拖柄对象。 在move时,根据大图定位坐标设置拖柄定位:

ImageZoom 图片放大效果(扩展篇)

var style = this._handle.style, scale = this._scale;  

style.left = math.ceil( this._repairhandleleft - x / scale ) + "px";  

style.top = math.ceil( this._repairhandletop - y / scale )  + "px";  

 在end时,隐藏拖柄对象。

【切割模式】

“切割”就是选择的部分全透明,其他部分半透明的效果。

为了实现切割效果,需要在init中新建一个_cropper切割层:

ImageZoom 图片放大效果(扩展篇)

var body = document.body,  

    cropper = body.insertbefore(document.createelement("img"), body.childnodes[0]);  

cropper.style.display = "none";  

 并在load中设置这个切割层:

ImageZoom 图片放大效果(扩展篇)

cropper.src = image.src;  

cropper.width = image.width;  

cropper.height = image.height;  

$$d.setstyle( cropper, {  

    left: rect.left + this._repairleft + "px",  

    top: rect.top + this._repairtop + "px"  

差不多是复制一个原图对象,并且绝对定位到原图对象上面。

在start时,显示切割层,并根据透明度设置原图为半透明状态。

在move时,根据大图移动的坐标设置切割层要clip的范围:

ImageZoom 图片放大效果(扩展篇)

var w = this._rangewidth, h = this._rangeheight, scale = this._scale;  

x = math.ceil( -x / scale ); y = math.ceil( -y / scale );  

this._cropper.style.clip = "rect(" + y + "px " + (x + w) + "px " + (y + h) + "px " + x + "px)";  

在end时,隐藏切割层,并重新设置原图为不透明,来恢复原来的状态。

还要记得在dispose中移除切割层。

使用技巧

需要扩展的效果时才需要添加这个扩展程序。

可自行对imagezoom._mode进行扩展,扩展后记得在modes添加对应模式。

可以组合多个基础模式同时使用,具体参考"handle-cropper"模式。

使用说明

使用方法跟imagezoom差不多,只是多了一个可选参考mode设置显示模式。

使用"handle"模式时,可选参数的"handle"属性可以设置拖柄对象。

使用"cropper"模式时,可选参数的"opacity"属性可以设置透明度。

使用"handle-cropper"模式时,以上两个参数都可以使用。