JavaScript 拖拉缩放效果

拖拉缩放效果,实现通过鼠标拖动来调整层的面积(宽高)大小。例如选框效果。

这里的拖拉缩放比一般的选框复杂一点,能设置八个方位(方向)的固定触发点,能设置最小范围,最大范围和比例缩放。

拖放效果一样,程序的原型也是在做图片切割效果的时候做出来的。但这个效果的参考少的多,基本上靠自己摸索,走了不少弯路,现在总算把自己想要的效果做出来了,程序跟上一个版本比较也已经“面目全非”,还是觉得有很多需要改进的地方,就像永远没有最完美的土耳其地毯。

这里也有一个简化版的SimpleResize,方便学习。

效果预览

程序说明

其中用到的鼠标捕获、清除选择等,在拖放效果中有说明的这里就略过。下面以SimpleResize为例说一下基本原理。

【程序原理】

程序需要用Set来添加触发对象(就是用来拖拉的对象,详细看使用说明),主要是设置mousedown事件来触发Start程序开始缩放。

Start程序主要用来设置缩放程序_fun和缩放需要的参数,最后设置mousemove事件触发Resize程序进行缩放,mouseup事件中执行取消缩放Stop程序。

Resize程序的任务是执行缩放程序_fun和设置整理后的样式,这里为了简化程序样式是全部一起设置的,这样程序的注意流程就完成了。
ps:设置样式的值必须是大于0的数,否则ie会报错。

下面说说缩放的原理,先以右边拖拉为例,右边拖拉一般是以左边为固定点,右边进行缩放。
首先记录左边定位参数_sideLeft:

this._sideLeft = e.clientX - this._styleWidth;


在拖拉时,就可以根据这个参照值计算拖拉后要设置的样式参数_styleWidth:

this._styleWidth = Math.max(e.clientX - this._sideLeft, 0);


上面的程序能保证样式是大于等于0的数。

至于左边就麻烦一点,因为左边拖拉是以右边为固定点,这就必须在设置宽度的同时设置left才能,保证右边固定。
首先记录右边定位参数_sideRight:

this._sideRight = e.clientX + this._styleWidth;


还有left的定位参数_fixLeft:

this._fixLeft = this._styleWidth + this._styleLeft;


在拖拉时,计算_styleWidth:

this._styleWidth = Math.max(this._sideRight - e.clientX, 0);


在根据_styleWidth设置_styleLeft:

this._styleLeft = this._fixLeft - this._styleWidth;


上下同理,至于斜角的四个方向只是同时执行两个方向,例如右下就是同时执行右边和下边:

this.Right(e); this.Down(e);


【程序结构】

在更详细的程序说明之前,先了解一下程序结构。

当点击触发点,就会根据设置给缩放程序_fun设置为八个方向的缩放程序的其中一个。
八个缩放程序分别是:Up(上)、Down(下)、Right(右)、Left(左)、RightDown(右下)、RightUp(右上)、LeftDown(左下)和LeftUp(左上)。

在这些缩放程序,首先会进行宽和高的设置,由于宽和高的设置还需要经过范围限制和比例缩放的修正,而这些会在修正程序中处理。
修正程序包括几个部分:
RepairX:水平方向修正(左右方向);
RepairY:垂直方向修正(上下方向);
RepairAngle:对角方向修正(右下、右上、左下、左上);
RepairTop:top修正(用于以右边为固定点定位);
RepairLeft:left修正(用于以下边为固定点定位);
RepairHeight:高度修正;
RepairWidth:宽度修正。

如果没有设置最小范围限制,当缩放超过定位边时就会自动转向,例如右边缩放,左边定位,当拖动到左边定位的左边时,就会切换成左边缩放,右边定位,而这个切换是在转向程序中进行的。
转向程序包括几个部分:
TurnRight:右转程序;
TurnLeft:左转程序;
TurnUp:上转程序;
TurnDown:下转程序。

基本结构了解后,下面就开始介绍程序细节。


【最小范围限制】

最小范围限制就是限制缩放的宽和高,程序中把Min设为true,就可以通过minWidth和minHeight属性进行限制。
单是限制很简单,只要超过限制就把值设成限制值就行了,这个主要是在修正程序RepairHeight和RepairWidth中修正:
例如RepairWidth中:

iWidth = Math.max(this.Min ? this.minWidth : iWidth, iWidth, 0);


注意这里带了个0,保证最小值大于等于0。

【最大范围限制】

最大范围限制,复杂一点,是固定一个(矩形)范围,然后把缩放限制在范围内。
这个范围限制跟拖放效果的类似,有四个范围属性:上(mxTop)、下(mxBottom)、左(mxLeft)、右(mxRight)。
程序中把Max设为true就可以设置。
然后根据这四个范围属性设置四个范围参数,_mxRightWidth、_mxDownHeight、_mxUpHeight和_mxLeftWidth。
这四个围参数代表的是相对于定位边的最大宽度或高度,例如_mxRightWidth就是当右边缩放时(左边固定),宽度可以设置的最大值:

this._mxRightWidth = Math.max(mxRight - this._styleLeft - this._borderX, this.Min ? this.minWidth : 0);


这里要小心的是不要把边框忽略了。

然后在Right缩放程序中,把这个参数传递给RepairX,而RepairX把参数传递给RepairWidth并在里面修正宽度:

iWidth = Math.min(this.Max ? mxWidth : iWidth, iWidth);


还有容器范围限制,这个跟拖放效果的差不多,这里就不重复了。此外在Start程序中还会对异常的范围参数进行修正,不过这里考虑的不多,估计并不很完善,最好还是不要设一些奇怪的参数。

【比例缩放】

比例缩放就是缩放的时候同时设置宽和高,使用宽和高按照一定的比例显示。
程序中把Scale设为true就可以启用,并且Ratio可以设置比例大小(宽/高),如果不设置的话就会自动取当前的宽/高比例。

对于对角方向,在比例缩放的情况下宽和高就不能同时设置,而必须有一个优先另一个参照比例设置了。
这个要注意,否则很容易进入死胡同。RepairAngle修正程序中就是宽度优先的,高度再按比例修正(参考代码)。

而左右上下四个方向,是以缩放对象对称轴为中心缩放的。
以左右方向为例,要实现这个效果,首先在Start程序中设置中心定位坐标_scaleTop:

this._scaleTop = this._styleTop + this._styleHeight / 2;


当修正好高之后,再用这个坐标设置_styleTop值:

this._styleTop = this._scaleTop - iHeight / 2;


其实就是设置高之后再修正top,使缩放之后的缩放对象中心的水平坐标保持不变,就做出以缩放对象的水平对称轴为中心的缩放了。

还有两个比例设置程序RepairScaleHeight和RepairScaleWidth,在这两个程序分别按比例设置高和宽。
这里必须留意一个问题,程序在计算样式参数的时候,是不计算边框的,但比例计算时应该把边框算进去。
例如RepairScaleHeight程序中:

return Math.max(Math.round((iWidth + this._borderX) / this.Ratio - this._borderY), 0);


注意,因为这样计算的结果可能会小于0,所以用Math.max保证结果大于0(上面已经说了样式值必须大于0)。

【范围限制下的比例缩放】

一般的比例缩放很简单,在宽或高取值之后,按比例设置另一个值就行了。
但如果有了范围限制有可能按比例缩放后,就超过范围限制了。
如果只考虑最大范围限制的话,可以再修正,每次修正的范围会越来越小,没有问题。
但加上最小范围限制,就可能这边已经到了最小范围了,但另一边还在最大范围限制之外了。
这个时候就必须小心细心处理了,当两个范围限制发生冲突时,要放弃其中一个,程序中是优先考虑最大范围限制,放弃最小范围限制,这个看起来没什么但如果思想转不过来,就很容易钻入死胡同去了(经验教训T_T)。
例如用宽度和RepairScaleHeight程序已经获得了高的值iHeight,可以这样设置宽和高:

Code


说明一下这段代码:
首先判断iHeight是否超过最大值,是的话就根据最大值设置宽和高,由于优先考虑最大范围所以宽是否超过最小范围就不用再考虑了;
如果没有超过最大值,再判断是否小于最小值,是的话用高度最小值和RepairScaleWidth程序取要设置的宽赋给一个临时变量tWidth,然后判断tWidth是否超过最大范围,不是的话就可以进行赋值,否则就放弃修改。

【自动转向】

如果没有设置最小范围限制,当超过改方向能设置宽高的范围就会自动转向。
转向程序中需要一个参数表示转向后要执行的缩放程序,并重新设置几个属性。
以左转程序TurnLeft为例:
_fun:设置为转向后要执行的缩放程序;
_sideRigh:设置为当前的_sideLeft,即以把右边定位左边设置成原来的左边定位坐标(形象点说就是原来是左边不动,改成右边不动);
_fixLeft:左转后的定位需要_fixLeft,设置为_styleLeft,本来是left加width的,由于左转时width是0,所以只要left就够了。
如果设置了最大范围限制,还需要设置一下范围参数,为了方便,程序使用了一个_mxSet方法重新设置范围参数。
程序如下:

Code


如果发生了转向就返回true,这个主要是用在对角方向的转向。
对于对角方向,可能会转向两个方向,但同一时间最多只能设置一个转向(同时转两个可能会造成混乱),
而且在按比例缩放时,程序规定只进行水平方向的转向(比例缩放中已说明)。
例如对于RightDown转向,可以这样满足这两个需求:

this.TurnLeft(this.LeftDown) || this.Scale || this.TurnUp(this.RightUp);


【样式修正】

由于offset获取的值跟style设置的值并不是一样的,例如offsetWidth包括padding、border和width。
所以在获取和设置时必须做一些修正,例如用offsetWidth获取宽度,要设置width时必须减去padding和border等等。
程序中有_borderX和_borderY属性分别是缩放对象的左右和上下边框宽度:

Code


程序中主要是修正了border,对于padding、margin都没有考虑,如果设置了这些属性的要注意一下哦。

【样式设置】

首先缩放对象必须是绝对定位,如果有范围限制容器就必须把容器设置成相对定位:

Code


推荐根据拖拉的方向设置拖拉对象的鼠标样式,其中右下和左上是nw-resize,左下和右上是ne-resize,上和下是n-resize,左和右是e-resiz。
至于拖拉对象的定位就有技巧一点,绝对定位到四个角比较简单,适当设置top,left,right和height到为0就行了,例如右上角是right和top为0。
四个边就难一点,参考这里的居中显示效果,利用定位样式和margin就能做到居中了。
例如右边设置top为50%,margin-top为高度的负的一半就能在右边上下居中了。

程序说明就到这里了,还有一些结构上的东西以我的能力还是比较难写出来,还是看代码来领会吧。
程序有很多相似的结构,总感觉可以整理得更好,等以后自己的编写水平高点的时候再来看拉。


使用说明

首先实例化一个拖拉缩放对象:

var rs = new Resize("dragDiv");

有以下这些可选参数和属性:
Max:  false,//是否设置范围限制(为true时下面mx参数有用)
mxContainer:"",//指定限制在容器内
mxLeft:  0,//左边限制
mxRight: 9999,//右边限制
mxTop:  0,//上边限制
mxBottom: 9999,//下边限制
Min:  false,//是否最小宽高限制(为true时下面min参数有用)
minWidth: 50,//最小宽度
minHeight: 50,//最小高度
Scale:  false,//是否按比例缩放
Ratio:  0,//缩放比例(宽/高)
onResize: function(){}//缩放时执行

然后使用Set程序添加拖拉对象,Set程序需要两个参数,第一格是拖拉对象,第二个是缩放参数。
其中缩放参数可以是"right-down"、"left-down"、"right-up"、"left-up"、"right"、"left"、"up"和"down"其中之一。
像这样添加就行了:

rs.Set("rDown""down");

ps:如果跟跟拖放效果配合使用时,要禁止冒泡,否则一点拖拉对象就冒泡到拖放了。


程序代码 

Code

 

完整测试代码下载

ps:实例中包含了拖放效果,不过两个效果是完全独立的,删掉拖放的部分也能正常缩放。

转载请注明出处:http://www.cnblogs.com/cloudgamer/
posted @ 2008-12-03 09:07 cloudgamer 阅读(12193) 评论(55)  编辑 收藏 网摘 所属分类: Javascript

  回复  引用  查看    
#1楼2008-12-03 09:13 | iIMax      
js高手,学习
  回复  引用    
#2楼2008-12-03 09:15 | 过路人[未注册用户]
有意思:)
  回复  引用  查看    
#3楼2008-12-03 09:25 | 随风逝去(叶进)      
收下了
  回复  引用    
#4楼2008-12-03 09:42 | zzzzz[未注册用户]
这个效果使用jQuery可以很简单的就实现了。 :)
  回复  引用    
#5楼2008-12-03 09:42 | 至强1[未注册用户]
真是不错..

但你那个"图片切割"能不能让底图也能调整呀..这个才能真正切出缩略图.

  回复  引用  查看    
#6楼2008-12-03 09:56 | KenBlove      
demo做的很帅~
  回复  引用    
#7楼2008-12-03 10:08 | nonew[未注册用户]
Extjs里有现成的方法~~
  回复  引用  查看    
#8楼[楼主]2008-12-03 10:18 | cloudgamer      
@iIMax
@过路人
@随风逝去(叶进)
@zzzzz
@至强1
@KenBlove
@nonew
谢谢各位支持

框架当然能做出来
但如果没有这些原理
写框架的人能做的出来吗?
框架只是一个工具,这里我是给大家js的知识原理而不是教大家使用工具

  回复  引用  查看    
#9楼2008-12-03 10:26 | 阿一(杨正祎)      
--引用--------------------------------------------------
nonew: Extjs里有现成的方法~~
--------------------------------------------------------
使用框架,用于受制于人。
用于只能模仿,不能超越。不利于自己技术的提高。

  回复  引用    
#10楼2008-12-03 10:27 | SherryTop[未注册用户]
Thanks for sharing!
  回复  引用    
#11楼2008-12-03 10:38 | chenjun[未注册用户]
提个建议,控件在获取焦点的时候应该现在拖拽边框,失去焦点时,应该去掉!
  回复  引用  查看    
#12楼2008-12-03 10:41 | Baozi      
谢谢分享
  回复  引用  查看    
#13楼2008-12-03 10:43 | MythYsJh      
顶楼主,佩服的五体投地!
  回复  引用  查看    
#14楼[楼主]2008-12-03 10:44 | cloudgamer      
@阿一(杨正祎)
@MythYsJh
@SherryTop
@chenjun
@Baozi
谢谢支持

chenjun这个属于扩展内容
跟拖拉缩放本身没什么关系的
而且也不难可以自己动手试试

  回复  引用  查看    
#15楼2008-12-03 13:12 | net1234      
谢谢分享,楼主很厉害
  回复  引用    
#16楼2008-12-03 13:56 | Eric Pan1987[未注册用户]
this demo is so nice. Thank your for your sharing!
  回复  引用    
#17楼2008-12-03 15:37 | 吃请客[未注册用户]
不错的东西,已经收录了、

http://www.scriptlover.com/controls/?url=/controls/resize

  回复  引用    
#18楼2008-12-03 16:07 | nonew[未注册用户]
--引用--------------------------------------------------
bravo yang: 有一个小问题,在空白区域mousedown然后随机移动鼠标会出现蓝色背景.是lz有意而为之么?
--------------------------------------------------------
晕 ~那是选择 类似 ctrl+a

  回复  引用  查看    
#19楼[楼主]2008-12-03 16:42 | cloudgamer      
@net1234
@Eric Pan1987
@吃请客
@bravo yang
@nonew
谢谢支持

  回复  引用  查看    
#20楼2008-12-03 18:35 | works guo      
很帅哦
  回复  引用  查看    
#21楼2008-12-03 18:50 | airwolf2026      
支持楼主的奉献精神.另外bs那些说框架可以做出来的人.嘎嘎
  回复  引用  查看    
#22楼[楼主]2008-12-03 19:18 | cloudgamer      
@airwolf2026
@works guo
@bravo yang
谢谢支持
一起学习

  回复  引用  查看    
#23楼2008-12-03 21:07 | 菜菜渣囧      
授人以渔的LZ,顶一个~
  回复  引用  查看    
#24楼2008-12-03 21:21 | DiryBoy      
收藏学习了。
对了,我也是顺德人,顺便问下,楼主是在顺德工作吗?

  回复  引用    
#25楼2008-12-03 21:39 | myhanbaobao[未注册用户]
学习中。。。。心服、口服、佩服。。。。。兄长请受小妹一拜
  回复  引用  查看    
#26楼2008-12-04 09:10 | 小宇儿      
好东西,一定要顶
  回复  引用  查看    
#27楼[楼主]2008-12-04 11:17 | cloudgamer      
@菜菜渣囧
@myhanbaobao
@小宇儿
谢谢支持

@DiryBoy
我是在顺德工作哦
呵呵

  回复  引用    
#28楼2008-12-04 11:19 | papa[未注册用户]
不能不顶了~
  回复  引用    
#29楼2008-12-05 00:45 | GaryW[未注册用户]
可以考虑加个handle,就像window的窗口一样. Thans.
  回复  引用  查看    
#30楼[楼主]2008-12-05 01:11 | cloudgamer      
@papa
谢谢支持

@GaryW
你说的跟这个效果关系不大
你可以自己试试

  回复  引用  查看    
#31楼2008-12-05 09:09 | 四喜      
太强悍了,学习下。
  回复  引用  查看    
#32楼2008-12-08 14:02 | 寒@鹏      
8错 不过好像没什么实用价值 就在 交友网站看见过此类效果
不知道还能用哪里 O(∩_∩)O哈哈~ 不过还是高手楼主

  回复  引用  查看    
#33楼[楼主]2008-12-08 14:39 | cloudgamer      
@四喜
谢谢支持

@寒@鹏
价值还是有的
有很多模拟弹出窗口的拖拉控制窗口大小就可以用到

  回复  引用    
#34楼2008-12-09 16:50 | 小a不a点a[未注册用户]
高人请帮我看看这个问题

http://zhidao.baidu.com/question/78447819.html

  回复  引用    
#35楼2008-12-14 22:14 | oatn[未注册用户]
高人,吐血佩服中!
  回复  引用  查看    
#36楼2008-12-15 07:28 | xland      

  回复  引用    
#37楼2008-12-16 13:06 | *Iron*[未注册用户]
博主真乃神人也!!!!
  回复  引用  查看    
#38楼2008-12-31 12:23 | Damon King      
楼主说的没错,这些都是很好的js学习资料,谢谢楼主的无私分享!
  回复  引用  查看    
#39楼[楼主]2009-01-09 14:07 | cloudgamer      
@小a不a点a
@oatn
@xland
@*Iron*
@Damon King
谢谢支持

  回复  引用    
#40楼2009-01-30 12:45 | 过路呵呵[未注册用户]
效果还可以,但是不太兼容ff,原因是“没有禁止他元素看见该事件和禁止网页中的默认动作”,因此有楼上的“在空白区域mousedown然后随机移动鼠标会出现蓝色背景”现象。要做些小的修改哦。
  回复  引用  查看    
#41楼[楼主]2009-02-01 21:48 | cloudgamer      
@过路呵呵
那是选择内容而已

  回复  引用  查看    
#42楼2009-03-05 12:05 | lhgstudio      
老大我有个问题,我照你这个试验了一下,如果把控制点改成小图片的话,把小图片做为控制点的背景图片,然后在点击和拖动时在IE6下鼠标的指针都会变成默认的样式,这个怎么解决呢?
  回复  引用  查看    
#43楼[楼主]2009-03-05 13:57 | cloudgamer      
@lhgstudio
是背景图还是直接图片
好像没有那样的问题
最好是给我个页面我看看

  回复  引用  查看    
#44楼2009-03-28 12:23 | yahoo      
太强悍了,
在这里学习比看什么书都要强得多!~

援人以鱼,不如援人以渔,
BS说框能实现的那些人


  回复  引用  查看    
#45楼2009-04-30 16:33 | 四喜      
请问楼主,如果我想添加多个可以拖动并缩放的div并且在拖放和改变大小的动作结束的时候,把该DIV(div已编号)的左上角坐标和高、宽保存到数据库中(js请求webservice这块儿我可以搞定,),该怎么做?
  回复  引用  查看    
#46楼2009-04-30 16:34 | 四喜      
能给个思路吗?谢谢。
  回复  引用  查看    
#47楼[楼主]2009-04-30 16:43 | cloudgamer      
@yahoo
谢谢支持
@四喜
//停止缩放
Stop
里面执行你那个操作就行了
至于那些相关参数你可以通过this._obj来获取相关属性

  回复  引用  查看    
#48楼2009-04-30 16:52 | 四喜      
@cloudgamer
谢谢!
//停止缩放
Stop: function() {
removeEventHandler(document, "mousemove", this._fR);
removeEventHandler(document, "mouseup", this._fS);
document.title="已经停止缩放!";
if(isIE){
removeEventHandler(this._obj, "losecapture", this._fS);
this._obj.releaseCapture();
}else{
removeEventHandler(window, "blur", this._fS);
}
}

我再研究下多个层拖动的问题。再次感谢。

  回复  引用    
#49楼2009-05-05 16:08 | jiyuanjie[未注册用户]
请问博主,我设置DIV属性contentEditable = "true",使div中的文本可编辑。但是调用Drag.js后可编辑这个功能就失效了,请问怎么解决。
  回复  引用  查看    
#50楼[楼主]2009-05-05 16:20 | cloudgamer      
@jiyuanjie
这个没什么办法
因为要编辑就要点击
这个点击会跟拖动程序冲突
或者你看看怎么处理一下这两个事件的关系咯

  回复  引用    
#51楼2009-05-19 15:42 | xiongzhijian[未注册用户]
学习了....非常佩服.
  回复  引用  查看    
#52楼2009-06-01 11:05 | wtcsy      
拜读了很久
好东西 Up
有些不明白 会何可以想出这样的构造方式
我现在写程序怎么老是直线式写程序

  回复  引用  查看    
#53楼[楼主]2009-06-01 11:20 | cloudgamer      
@wtcsy
你的意思是initialize?
这个我也是参考prototype.js的

@xiongzhijian
谢谢支持

  回复  引用  查看    
#54楼2009-06-01 11:30 | wtcsy      
这个 我没说清楚
我是说计算位置时候的方法 RepairX ,RepairTop等等之间的关系
是如何想到这么去构造的了 一点都不直线
Ps:其实计算大小位置的过程 看的也不大懂!~

  回复  引用  查看    
#55楼[楼主]2009-06-01 11:37 | cloudgamer      
@wtcsy
这个其实也不是一下子写出来的
一开始肯定是先实现效果,这时程序代码可能会很混乱
接着才开始整理程序架构,一点一点改进的

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1346386




相关文章:

相关链接: