master2012

导航

js 图片效果插件2

JavaScript 拖放效果

 

拖放效果,也叫拖拽、拖动,学名Drag-and-drop ,是最常见的js特效之一。
如果忽略很多细节,实现起来很简单,但往往细节才是难点所在。
这个程序的原型是在做图片切割效果的时候做出来的,那时参考了好几个同类的效果,跟muxrwc和BlueDestiny学习了不少东西。
虽然每次整理都觉得很好了,不过每隔一段时间又会发现得某个地方可以改善,某个地方有错误,某些需求需要实现,就像自己学习的知识那样。

这里考虑到有的人可能只需要简单的拖放,所以有一个简化版的拖放SimpleDrag,方便学习。

效果预览

ps:在maxthon下如果开启广告过滤的话很可能会被过滤掉(不知有什么方法可以避免)。

 
 

      
拖放状态:结束拖放


程序说明

【程序原理】

这里以SimpleDrag为例说一下基本原理。

首先初始化程序中要一个拖放对象:

this.Drag = $(drag);


还要两个参数在开始时记录鼠标相对拖放对象的坐标:

this._x = this._y = 0;


还有两个事件对象函数用于添加移除事件:

this._fM = BindAsEventListener(this, this.Move);
this._fS = Bind(this, this.Stop);


分别是拖动程序和停止拖动程序。
拖放对象的position必须是absolute绝对定位:

this.Drag.style.position = "absolute";


最后把Start开始拖放程序绑定到拖放对象mousedown事件:

addEventHandler(this.Drag, "mousedown", BindAsEventListener(this, this.Start));


鼠标在拖放对象按住,就会触发Start程序,主要是用来准备拖动,在这里记录鼠标相对拖放对象的坐标:

this._x = oEvent.clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;


并把_fM拖动程序和_fS停止拖动程序分别绑定到document的mousemove和mouseup事件:

addEventHandler(document, "mousemove", this._fM);
addEventHandler(document, 
"mouseup", this._fS);


注意要绑定到document才可以保证事件在整个窗口文档中都有效,如果只绑定到拖放对象就很容易出现拖太快就脱节的现象。

当鼠标在文档上移动时,就会触发Move程序了,这里就是实现拖动的程序。
通过现在鼠标的坐标值跟开始拖动时鼠标相对的坐标值的差就可以得到拖放对象应该设置的left和top了:

this.Drag.style.left = oEvent.clientX - this._x + "px";
this.Drag.style.top = oEvent.clientY - this._y + "px";


最后放开鼠标后就触发Stop程序结束拖放。
这里的主要作用是把Start程序中给document添加的事件移除:

removeEventHandler(document, "mousemove", this._fM);
removeEventHandler(document, 
"mouseup", this._fS);


这样一个简单的拖放程序就做好了,下面说说其他扩展和细节部分。

【拖放锁定】

锁定分三种,分别是:水平方向锁定(LockX)、垂直方向锁定(LockY)、完全锁定(Lock)。
这个比较简单,水平和垂直方向的锁定只要在Move判断是否锁定再设置left和top就行,如果是完全锁定就直接返回。

if(!this.LockX){ this.Drag.style.left = ; }
if(!this.LockY){ this.Drag.style.top = ; }


【触发对象】

触发对象是用来触发拖放程序的,程序中通过Handle属性设置。有的时候不需要整个拖放对象都用来触发,这时就需要触发对象了。
使用了触发对象后,进行移动的还是拖放对象,只是用触发对象来触发拖放(一般的使用是把触发对象放到拖放对象里面)。
ps:触发对象的另一个用法是通过设置相同的Handle,实现一个触发对象同时拖放多个拖放对象。

【范围限制】

要设置范围限制必须先把Limit设为true。范围限制分两种,分别是固定范围和容器范围限制,主要在Move程序中设置。
原理是当比较的值超过范围时,修正left和top要设置的值使拖放对象能保持在设置的范围内。

【固定范围限制】

容器范围限制就是指定上下左右的拖放范围。
各个属性的意思是:
上(mxTop):top限制;
下(mxBottom):top+offsetHeight限制;
左(mxLeft):left限制;
右(mxRight):left+offsetWidth限制。

如果范围设置不正确,可能导致上下或左右同时超过范围的情况,程序中有一个Repair程序用来修正范围参数的。
Repair程序会在程序初始化和Start程序中执行,在Repair程序中修正mxRight和mxBottom:

this.mxRight = Math.max(this.mxRight, this.mxLeft + this.Drag.offsetWidth);
this.mxBottom = Math.max(this.mxBottom, this.mxTop + this.Drag.offsetHeight);


其中mxLeft+offsetWidth和mxTop+offsetHeight分别是mxRight和mxBottom的最小范围值。

根据范围参数修正移动参数:

iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft);
iTop 
= Math.max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop);


对于左边上边要取更大的值,对于右边下面就要取更小的值。

【容器范围限制】

容器范围限制的意思就是把范围限制在一个容器_mxContainer内。
要注意的是拖放对象必须包含在_mxContainer中,因为程序中是使用相对定位来设置容器范围限制的(如果是在容器外就要用绝对定位,这样处理就比较麻烦了),还有就是容器空间要比拖放对象大,这个就不用说明了吧。
原理跟固定范围限制差不多,只是范围参数是根据容器的属性的设置的。

当设置了容器,在Repair程序如果容器的position不是relative或absolute,会自动把position设为relative来相对定位:

!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || CurrentStyle(this._mxContainer).position == "absolute" || (this._mxContainer.style.position = "relative");


ps:其中CurrentStyle是用来获取最终样式(详细看这里的最终样式部分)。

注意如果在程序执行之前设置过拖放对象的left和top而容器没有设置relative,在自动设置relative时会发生移位现象,所以程序在初始化时就执行一次Repair程序防止这种情况。因为offsetLeft和offsetTop要在设置relative之前获取才能正确获取值,所以在Start程序中Repair要在设置_x和_y之前执行。

由于是相对定位,对于容器范围来说范围参数上下左右的值分别是0、clientHeight、0、clientWidth。
clientWidth和clientHeight是容器可视部分的宽度和高度(详细参考这里)。
为了容器范围能兼容固定范围的参数,程序中会获取容器范围和固定范围中范围更小的值:

Code


因为设置相对定位的关系,容器_mxContainer设置过后一般不要取消或修改,否则很容易造成移位异常。

【鼠标捕获】

我在一个拖放实例中看到,即使鼠标移动到浏览器外面,拖放程序依然能够执行,仔细查看后发现是用了setCapture。
鼠标捕获(setCapture)是这个程序的重点,作用是将鼠标事件捕获到当前文档的指定的对象。这个对象会为当前应用程序或整个系统接收所有鼠标事件。
使用很简单:

this._Handle.setCapture();


setCapture捕获以下鼠标事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。
程序中主要是要捕获onmousemove和onmouseup事件。
msdn的介绍中还说到setCapture有一个bool参数,用来设置在容器内的鼠标事件是否都被容器捕获。
容器就是指调用setCapture的对象,大概意思就是:
参数为true时(默认)容器会捕获容器内所有对象的鼠标事件,即容器内的对象不会触发鼠标事件(跟容器外的对象一样);
参数为false时容器不会捕获容器内对象的鼠标事件,即容器内的对象可以正常地触发事件和取消冒泡。
而对于容器外的鼠标事件无论参数是什么都会被捕获,可以用下面这个简单的例子测试一下(ie):

Code


这里的参数是true,一开始body会捕获所有鼠标事件,即使鼠标经过div也不会触发onmousemove事件。
换成false的话,div就可以捕获鼠标事件,就能触发div的onmousemove事件了。

拖放结束后还要使用releaseCapture释放鼠标,这个可以放在Stop程序中:

this._Handle.releaseCapture();


setCapture是ie的方法,对于dom也有对应的captureEvents和releaseEvents方法,但这两个方法现在的ff已经不支持了(如果还想了解一下的话可以看w3c中dom2的Event Capture部分)。

那是不是没有鼠标捕获就不能在浏览器外执行拖动呢?并不是这样,大家测试SimpleDrag的话可以发现没有设置鼠标捕获也能在浏览器外拖动(ie和ff都可以)。
那时因为程序把mousemove事件绑定到document了,如果换成this.Drag就没有了这个效果了。
不过细心的话也能发现这个跟用setCapture设置的鼠标捕获还是有很大不同的。
看来这个效果是专门为document而设的,我想这是为了能在浏览器外也能对文档进行一些特殊操作,例如拖动鼠标选择一段文本,即使拖到浏览器外也能继续选择。

这里说说ie中有个奇怪的现象,如果在SimpleDrag的Move程序中加入document.selection.empty()来清除选择(下面会介绍),那么捕获也会失效。
这个我也想不明白,希望高人能指点。

那是不是就不需要用setCapture鼠标捕获了呢?也不是,setCapture在下面说明的焦点丢失、取消默认动作上都很有用处的,而对于ff还是需要把事件绑定到document来捕获浏览器外的拖动事件。

注意ff2下的鼠标捕获有一个bug,当拖放对象内部没有文本内容并拖放到浏览器外时捕获就会失效。
给拖放对象插入一个空文本,例如<font size='1px'>&nbsp;</font>就可以解决,不过这个bug在ff3已经修正了。

【焦点丢失】

一般情况下,鼠标捕获都能正常捕获事件,但如果浏览器窗口的焦点丢失就会导致捕获失效。
我暂时测试到会导致焦点丢失的操作包括切换窗口(包括alt+tab),alert和popup等弹出窗体。
当焦点丢失时应该同时执行Stop程序结束拖放,但当焦点丢失就不能捕获mouseup事件也就是不能触发_fS。
还好ie有onlosecapture事件会在捕获失效时触发,针对这个情况可以这样设置:

addEventHandler(this._Handle, "losecapture", this._fS);


并在Stop程序中移除:

removeEventHandler(this._Handle, "losecapture", this._fS);

但ff没有类似的方法,不过muxrwc找到一个替代losecapture的window.onblur事件,那么可以在Start程序中设置:

addEventHandler(window, "blur", this._fS);


在Stop程序中移除:

removeEventHandler(window, "blur", this._fS);

那ie也有window.onblur事件,用window.onblur代替losecapture不就可以省一段代码了吗。
接着我做了一些测试,发现基本上触发losecapture的情况都会同时触发window.onblur,看来真的可以。
于是我修改程序用window.onblur代替losecapture,但测试后就出问题了,我发现如果我用alt+tab切换到另一个窗口,拖动还可以继续,但这个时候应该是已经丢失焦点了。

于是我逐一排除测试和程序代码,结果发现如果使用了DTD,那么window.onblur会在再次获得焦点时才会触发。
大家可以用下面这段代码测试:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script>window.onblur=function(){alert(1)}</script>


在切换到其他程序后,再切换回来才会触发window.onblur,还有几个比较怪异的状况就不说了,反正ie用window.onblur是不理想的了。

【取消默认动作】

对选择状态的文本内容、连接和图片等进行拖放操作会触发系统的默认动作,例如ie中拖动图片鼠标会变成禁止操作状态,这样会导致这个拖放程序执行失败。

不过ie在设置了setCapture之后,通过用户界面用鼠标进行拖放操作和内容选择都会被禁止。
意思就是setCapture之后就不能对文档内容进行拖放和选择,注意这里的拖放是指系统的默认动作,例如ondragstart就不会被触发。
不过如果setCapture的参数是false的话,容器内的对象还是可以触发事件的(具体看鼠标捕获部分),所以setCapture的参数要设成true或保留默认值。

而ff的鼠标捕获没有这个功能,但可以用preventDefault来取消事件的默认动作来解决:

oEvent.preventDefault();


ps:据说使用preventDefault会出现mouseup丢失的情况,但我在ff3中测试没有发现,如果各位发现任何mouseup丢失的情况,务必告诉我啊。

【清除选择】

ie在设置setCapture之后内容选择都会被禁止,但也因此不会清除在设置之前就已经选择的内容,而且设置之后也能通过其他方式选择内容,
例如用ctrl+a来选择内容。
ps:onkeydown、onkeyup和onkeypress事件不会受到鼠标捕获影响。
而ff在mousedown时就能清除原来选择的内容,但拖动鼠标,ctrl+a时还是会继续选择内容。
不过在取消了系统默认动作之后,这样的选择并不会对拖放操作造成影响,这里设置主要还是为了更好的体验。

以前我用禁止拖放对象被选择的方法来达到目的,即ie中设置拖放对象的onselectstart返回false,在ff中设置样式MozUserSelect(css:-moz-user-select)为none。
但这种方法只能禁止拖放对象本身被选择,后来找到个更好的方法清除选择,不但不影响拖放对象的选择效果,还能对整个文档进行清除:
ie:document.selection.empty()
ff:window.getSelection().removeAllRanges()
为了防止在拖放过程中选择内容,所以把它放到Move程序中,下面是兼容的写法:

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();


这里说说ff下有一个比较奇怪的现象,在SimpleDrag中第一次拖放没有问题,但第二次拖放时鼠标就会显示禁止操作的样式(拖动失败)。
我发现如果在Start程序中阻止默认动作(preventDefault)或清除选择(window.getSelection().removeAllRanges())就能正常了。
所以我估计是在点击事件后,拖放对象已经默认设置成选择状态了(虽然看起来不是),再拖动时就触发了系统的默认拖动事件。
为了支持推断,我在html插入一段文本,然后在每一次拖放之后选择一下那段文本(间接取消拖放对象的选择),那就能正常了。
不过暂时还没找到官方说明,所以还不能下结论。

【margin】

还有一个情况,当拖放对象设置了margin,那么拖放的时候就会错位(给SimpleDrag的拖放对象设置margin就可以测试)。
原因是在Start程序设置_x和_y时是使用offset获取的,而这个值是包括margin的,所以在设置left和top之前要减去这个margin。
但如果在Start程序中就去掉margin那么在Move程序中设置范围限制时就会计算错误,
所以最好是在Start程序中获取值:

this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) || 0;


在Move程序中设置值:

this.Drag.style.left = iLeft - this._marginLeft + "px";
this.Drag.style.top = iTop - this._marginTop + "px";


要注意margin要在范围修正之后再设置,否则会错位。

【透明背景bug】

在ie有一个透明背景bug(不知算不算bug),可以用下面的代码测试:

Code


点击div的背景会触发不了事件(点击边框或div里面的对象是可以触发的)。
到底什么时候会出现这个bug呢,再用下面的代码测试:

Code


测试代码中我把背景颜色(包括body)设成灰色,首先可以看出在蓝色div(测试对象)内只要触发点是在灰色上面,就能触发事件;相反,在其他不是背景的地方,即使是边框、图片,也不能触发事件。
就像是把灰色的背景的补到蓝色div上来,而且仅仅是背景能这样,多奇怪的设定啊。
这里要说明的是body比较特别,不管背景是不是透明,只要触发点是直接在body上就能触发事件。
我的结论是如果触发事件的对象背景是透明的,而事件的触发点不在对象内的元素上,也不是直接在body上,而且透明背景外没有非透明背景的话,那么事件触发就会失败。
这个结论写得不太好,因为我都不知改怎么表达这奇怪的设定,希望各位能明白。
ps:这里设置图片背景跟颜色背景效果是一样的。

那最好解决的方法就是给对象设一个非透明背景,但有时需求正好是要透明的,那怎么办呢?
首先想到的是加上背景色后设置完全透明,但这样连边框,容器内的对象等都完全透明了,不够好。
如果能保证触发点直接在body或非背景上也可以,如果这个也不能保证呢?
我想到的一个解决方法是在容器里面加一个层,覆盖整个容器,并设置背景色和完全透明:

Code


ps:不要忘了设置fontSize,否则就有一个默认最小高度。
当发现程序有这个bug出现,把程序可选参数Transparent设为true就会自动插入这样一个层了。

到这里插入一个小知识吧,细心的话会发现上面的测试代码中我给html设了一个背景色。
大家可以去掉这个背景色,会发现背景色会设置到整个页面,虽然一直都是用body设置页面背景色,但现在会不会有一个疑惑,body是红色框的部分,为什么设置它的背景色就能应用到整个页面,而给html设置了背景色就又“正常”显示了呢。
这个可以从CSS21的w3c标准中关于background的部分看到原因:
For HTML documents, however, we recommend that authors specify the background for the BODY element rather than the HTML element. For HTML documents whose root HTML element has computed values of 'transparent' for 'background-color' and 'none' for 'background-image', user agents must instead use the computed value of those properties from that HTML element's first BODY element child when painting backgrounds for the canvas, and must not paint a background for that BODY element. Such backgrounds must also be anchored at the same point as they would be if they were painted only for the root element. This does not apply to XHTML documents. 

我英文很烂,就勉强翻译一下吧:
对于HTML文档,我们建议作者(这是对浏览器的制作者说的)使用BODY元素的background,而不是HTML元素的。如果HTML文档的根元素(HTML元素)的'background-color'是'transparent',同时'background-image'是'none'(这两个刚好就是默认值),那么在设置背景时就取HTML元素的第一个BODY子元素的属性值,并且不再渲染那个元素的background。
后面两个句就看不太懂了,不过已经足够解析原因了。

【iframe】

如果页面上有嵌入iframe,那就要注意了,因为鼠标捕获在iframe上会有问题。

例如在拖放容器内的一个iframe上快速移动拖放,或者鼠标拖动到容器外的一个iframe上,反正就是鼠标在iframe上(注意没有其他元素隔开),就会出问题:
首先是捕获的失效,鼠标在iframe上,就拖不动了,但并不会触发losecapture,更不用说window的blur了,这个在ie和ff都是一样;
其次ie里在iframe多摩擦几次,还可能导致ie死掉(原因不明)。

下面是我想到的几种方法:
隐藏页面的iframe,比较简单,但可能有些在iframe中重要的信息也被隐藏,或者造成页面布局的错位,用户体验不好;
鼠标移动到iframe后取消拖放,难度不大,但同样用户体验不好;
每个iframe用一个透明的层遮住,很麻烦,要计算好每个iframe的位置和大小;
用一个透明的层把整个页面遮住,比较推荐,也比较简单,下面介绍这种做法。

我在仿LightBox内容显示效果做的那个覆盖层正好能应用在这里,首先实例化一个透明的覆盖层:

var ol = new OverLay({ Opacity: 0 });


然后在onStart和onStop事件中添加ol.Show()和ol.Close()来显示和隐藏覆盖层就可以了,这样只要不是在iframe触发拖放就没有问题了。

有其他更好的方法也请各位指教。

暂时就研究到这里,想不到小小的拖放就有这么多的学问。
还有滚屏等这些都还没考虑到呢,等以后有需要了再来研究拉。


使用说明

实例化时只需要一个参数,就是拖放对象:

var drag = new Drag("idDrag")


有以下这些可选参数和属性:
属性:默认值//说明
Handle:"",//设置触发对象(不设置则使用拖放对象)
Limit:false,//是否设置范围限制(为true时下面参数有用,可以是负数)
mxLeft:0,//左边限制
mxRight:9999,//右边限制
mxTop:0,//上边限制
mxBottom:9999,//下边限制
mxContainer:"",//指定限制在容器内
LockX:false,//是否锁定水平方向拖放
LockY:false,//是否锁定垂直方向拖放
Lock:false,//是否锁定
Transparent: false,//是否透明
onStart:function(){},//开始移动时执行
onMove:function(){},//移动时执行
onStop:function(){}//结束移动时执行

还有属性Drag是拖放对象,Transparent、Handle和mxContainer初始化后就不能再设置。


程序代码 

Code



完整实例下载

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

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

如果觉得文章不错的话,欢迎点一下右下角的推荐。

程序中包含的js工具库CJL.0.1.min.js,原文在这里。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

JavaScript 图片切割效果

 

序一(08/07/21)

很久之前就在一个网站的截取相片的功能中看到这个效果,也叫图片裁剪、图片剪切(设置一下也可以做出放大镜等类似的效果)。
当时觉得很神奇,碍于水平有限,没做出来。
前些日子突然想做一个透镜效果,就突然想到了这个效果,于是找出当年“珍藏”的代码决定一尝所愿。

序二(08/12/06)

自上一个版本的图片切割效果出来后,虽然也经常看到“框架开发这个如何如何容易”之类的评论,但也受到很多人的肯定,小弟在此感谢大家的支持。
上一个版本由于是初次接触这类效果,而且是三个大功能一起开发,能力所限,所以仅仅是实现了效果就完成了。
后来我知道这个效果叫ImageCropper,找了些这类效果参考,完善了切割的部分。
近来我把其中的拖放效果和缩放效果单独出来研究,经过整理和完善,再套进切割效果,个人感觉效果已经不错了。
要说明的是这个只是一个效果,并不是真正的切割图片,要获取真正的切割图片请参考图片切割系统。

先看看效果:

 

 

       

图片地址: 

 


程序说明

这个效果主要分三个部分:层的拖放、层的缩放、图片切割(包括预览)。
其中层的拖放和层的缩放我已经在其他两篇文章中有详细说明,这里就说说图片切割这部分吧。

【图片切割】

关于图片切割的设计,有三个方法:
1.定位四个半透明层,遮住要盖住的部分,没试过,感觉比较麻烦;
2.把图片设为背景图,通过设置背景图的位置来实现,但这样的缺点是只能按图片的正常比例实现,不够灵活;
3.把图片放到切割对象里面,通过设置图片的top和left实现,这个方法是可行,但下面有更简单的方法实现;
4.通过设置图片的clip来实现。

这里介绍方法4的实现方法,这个方法是从当年“珍藏”的代码中看到的,先说说clip:
clip的作用是“检索或设置对象的可视区域。可视区域外的部分是透明的。”
依据上-右-下-左的顺序提供自对象左上角为(0,0)坐标计算的四个偏移数值来剪切。
例如:

div { position:absolute; width:60px; height:60px; clip:rect(0 20 50 10); }


注意position:absolute的设置是必须的(详细看手册)。

下面说说具体实现原理:
首先需要一个容器(_Container),容器里面会插入三个层:
底图层(_layBase):那个半透明的图片;
切割层(_layCropper):正常显示的那个部分;
控制层(_layHandle):就是控制显示的那个部分。

其中为了底图层和切割层是程序自动创建的图片,控制层是自己定义的层(程序中是一个div)。
底图层和切割层必须完全重合,程序中把这两个层都绝对定位到了左上角:

this._layBase.style.top = this._layBase.style.left = this._layCropper.style.top = this._layCropper.style.left = 0;


层叠顺序也要设置一下保证各层顺序。

下面说说各部分的作用:
容器:除了容器本身的作用,通过设置其背景色来设置透明的渐变色,由于图片本身没有背景色所以要通过容器来设置;
底图层:在容器最底部,作用是显示非选择区域的图片,透明效果就是在这层设置;
切割层:最关键的一个层,在底图层和控制层之间,在这个层通过clip设置其可视区域来实现切割图片的效果;
控制层:位于顶部,拖放(_drag)和缩放(_resize)效果就是在这个层实现,根据其拖放和缩放的结果控制切割层的切割效果。

这里要注意的是控制层的_drag拖放效果的Transparent要设为true(详细看拖放效果的透明背景bug部分)。
要使用缩放需要把Resize设为true,并设置各个拖拉对象,程序通过_resize设置缩放的比例和最少范围(详细看拖拉缩放效果)。

下面说说控制层如何控制切割效果:
控制层的拖放和缩放过程中加入了SetPos设置切割样式程序,在SetPos程序中根据控制层的样式设置切割层的可视区域范围:

var p = this.GetPos();
this._layCropper.style.clip = "rect(" + p.Top + "px " + (p.Left + p.Width) + "px " + (p.Top + p.Height) + "px " + p.Left + "px)";


其中GetPos程序,它可以把当前控制层的样式参数作为一个对象返回:

with(this._layHandle){
    
return { Top: offsetTop, Left: offsetLeft, Width: offsetWidth, Height: offsetHeight }
}


如果理解了的话就会觉得其实原理挺简单的,不过要想出来还是要一定创意才行,为想出这个方法的人致敬!

【切割预览】

预览效果需要设置Preview属性为预览容器对象,程序会自动给容器插入一个预览对象(图片)。
预览效果的关键在于如何根据控制层的数据来给预览对象定位,这个主要在SetPreview预览效果程序中处理。
首先根据控制层的高宽比置预览对象显示的宽和高(不是图片本身的宽高哦),这里可以用GetSize程序获取:

var p = this.GetPos(), s = this.GetSize(p.Width, p.Height, this.viewWidth, this.viewHeight), scale = s.Height / p.Height;


其中GetSize获取尺寸程序可以根据图片实际大小按比例缩放到要设置的大小:

Code


可以看出如果后两个参数(viewWidth和this.viewHeight)都不设置就会按原来大小显示了,

然后再按预览图跟控制层的比例设置预览图的样式参数:

var pHeight = this._layBase.height * scale, pWidth = this._layBase.width * scale, pTop = o.Top * scale, pLeft = o.Left * scale;


最后根据参数对预览对象进行样式设置和切割:

Code


这里有点烦乱,但应该不难理解就不详细说明了。

【图片大小】

容器的大小一般是固定的,但图片的大小就不是固定的,这里又可以使用SetSize程序用来设置图片大小:

Code


有了图片大小就可以用来设置拖放和缩放的范围限制了:

Code


ps:程序中的Init程序是用来把个性设置呈现出来,所以一般设置过属性之后(例如图片地址、透明度等)就执行一次Init程序就可以显示效果了。

【ie6渲染bug】

在拖放效果中说到插入一个div解决ie的透明背景bug,这里就需要修复这个bug。
跟缩放效果配合使用时,不得不说ie6的一个渲染bug,用下面的代码测试(ie6):

Code


可以对比ie7的效果,可以看出里面的div虽然高度设置了100%,但在外面的div修改高度之后,却不知什么原因没有填充了(外面的div渲染后,没有触发里面的div再渲染)。

解决这个bug的关键是使里面的div能再渲染,这里有几个方法可以解决:
1,设置控制层的overflow为hidden,好处是只需要设置一次,但这个样式会影响其他效果,当然在不影响效果的情况下这个是最好的方法;
2,在缩放的时候,通过修改控制层的某些样式,使里面的div再渲染,这样的样式包括zoom,display(应该还有其他吧);
3,在缩放的时候,通过修改插入div的某些样式,使其再渲染,这样的样式包括display(zoom也不行);
程序中是通过修改_layHandle的zoom解决的:

if(isIE6){ with(this._layHandle.style){ zoom = .9; zoom = 1; }; };


ps:这个bug的原因貌似跟ie的layout有关,但我查了一下还是没弄清楚,谁有详细的介绍或解析务必跟我分享一下啊。


使用说明

实例化时有三个必要参数:容器对象、控制层、图片地址:

var ic = new ImgCropper("bgDiv", "dragDiv", "1.jpg");


有以下这些可选参数和属性:
属性:默认值//说明
Opacity: 50,//透明度(0到100)
Color:  "",//背景色
Width:  0,//图片高度
Height:  0,//图片高度
//缩放触发对象
Resize:  false,//是否设置缩放
Right:  "",//右边缩放对象
Left:  "",//左边缩放对象
Up:   "",//上边缩放对象
Down:  "",//下边缩放对象
RightDown: "",//右下缩放对象
LeftDown: "",//左下缩放对象
RightUp: "",//右上缩放对象
LeftUp:  "",//左上缩放对象
Min:  false,//是否最小宽高限制(为true时下面min参数有用)
minWidth: 50,//最小宽度
minHeight: 50,//最小高度
Scale:  false,//是否按比例缩放
Ratio:  0,//缩放比例(宽/高)
//预览对象设置
Preview: "",//预览对象
viewWidth: 0,//预览宽度
viewHeight: 0//预览高度

 

程序代码

Code

 

 

完整实例下载

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

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

如果觉得文章不错的话,欢迎点一下右下角的推荐。

程序中包含的js工具库CJL.0.1.min.js,原文在这里。

 
分类: Javascript

posted on 2013-08-01 10:55  master2012  阅读(180)  评论(0)    收藏  举报