(转)JavaScript 图片切割效果(带拖放、缩放效果)

原贴:http://www.cnblogs.com/cloudgamer/archive/2008/07/21/1247267.html

背景:

很久之前就在marry5.com看到这个效果,当时觉得很神奇,碍于水平有限,虽然对这个效果催之若鹜也没想过做出来。
前些日子突然想做一个透镜效果,就突然想到了这个效果,于是找出当年“珍藏”的代码决定一尝所愿。

前言:

这个程序主要分三部分:层的拖放、层的缩放、图片切割(包括预览)。
其中层的拖放是很常见的效果,层的缩放有点难度,图片切割看着炫其实原理也很简单。
不过在实现的过程中也学习到很多以前不知道的东西,下面都会说明,希望大家从中也能学到东西。

效果:






 

原理:

【拖放程序】

基本原理很简单,不知道的看代码就明白,其中参考了越兔BlueDestiny的相关文章。

下面说一下比较有用的地方:

【范围限制】

首先当然是有拖放范围参数,分别是mxLeft(左边的left最小值)、mxRight(右边的left最大值)、mxTop(上边的top最小值)、mxBottom(下边的top最大值)。
然后在拖动程序Move()中看有没有超过,超过的话设回极限值就行:

Code

【释放选择】

我以前就用的方法是设置ie的onselectstart和ff的MozUserSelect,
但BlueDestiny说“用user-select会相当于event.preventDefault。阻止默认动作就会在某些操作的时候导致mouseup丢失。”,
最好的方法是ie用document.selection.empty(),ff用window.getSelection().removeAllRanges()。
所以可以在Move()中加入:

window.getSelection && window.getSelection().removeAllRanges();
这种写法是从越兔的程序中学到的。
因为ie的鼠标捕获默认(下面会说)带这个,所以ie就不用了。

【鼠标捕获】

以前不知道js有这个东西,使用很简单:
设置捕获:this.Drag.setCapture();
取消捕获:this.Drag.releaseCapture()。
它的作用是:将鼠标捕获设置到指定的对象。这个对象会为当前应用程序或整个系统接收所有鼠标输入。
还不明白的话,试试拖放的时候把鼠标拖放到浏览器外面,会发现拖动还在继续,
如果没有这个鼠标捕获就会失效了。
但在浏览器外是触发不了mouseup的,不过还可以用losecapture事件代替:

addEventHandler(this.Drag, "losecapture"this._fS);
this.Drag.setCapture();

程序中给ff的window添加blur时停止的事件,越兔说是为了可以检测到alt+tab造成的mouseup丢失,完美一点,也加上去了。

这样一个拖放程序就完成了。


【缩放程序】

原理也很简单,根据鼠标的坐标来设置缩放对象样式。
除了设置width和height外,对于上边和左边的缩放还要设置left和top,详细可参考代码。

程序更重要的是结构设计,因为有八个方向又分普通和比例缩放,
要尽量抓出能重用的部分,不然程序的复杂度可想而知。
为了提高程序内函数重用度减低复杂度我做了以下设计:
1.设一个_fun程序存放缩放过程中要执行的程序(有点像委托);
2.计算四个样式初始值,缩放函数修改这些初始值,最后重新设置全部样式(为了减低复杂度不是按需修改);
3.对于普通缩放只需要四个方向的程序就够,像右下方向可以用执行右边和下边程序代替;
4.根据比例缩放程序和普通缩放程序可重用的部分抽出了四个修正程序(用了部分程序效率来换取);

下面是程序中比较有用的部分:

【边宽获取】

由于涉及到高度和宽度的修改,边框宽度的获取必不可少。
因为用offset取得的宽度或高度是包括了边框宽度的,style中的宽度和高度是不包括边框宽度的,
所以设置样式的时候必须在offset取得的宽度或高度的基础上减去边框宽度。

那怎么取得边框宽度呢?
直观的方法是通过parseInt(object.style.borderBottomWidth)来获取,但这是错误的,
因为这样只能获取style中设置的样式,而不能获取class中设置的样式。

要取得最终样式(实际的样式),在ie中可使用currentStyle取得,在ff中使用document.defaultView.getComputedStyle(object, null),
那么用下面的程序就可以获取边框宽度了:

Code

【比例缩放】

比例缩放原理也很简单,在原有缩放的基础上,再按比例设置一次高和宽。
例如右下的比例缩放是先设置一次右边的普通缩放取得宽度,
根据比例取得高度后执行一次下边的修正程序,
由于此时高度经过修正可能已经改变了,最后需要再执行一次右边的修正程序。

Code

这样的缩放是以宽度优先的,对于上下两个点以高度优先会有更好的体验,
例如对于上面的点可以:
Code

对于上下左右四个点,更好的体验当然是以该点为中心缩放,
各位有兴趣的话可以把这个也做出来,写多四个修正程序对应这几个点就行。

而最小限制,范围限制可参照修正程序中的代码。
程序中也使用了跟拖放差不多的释放选择和鼠标捕获。

【鼠标捕获补充】

setCapture解决了ie中鼠标捕获的问题,但ff下的鼠标捕获还有问题。
当层的内部没有文本内容时,ie捕获正常,但ff在拖放到浏览器外时捕获就会失效,
暂时的解决方法只有插入文本,例如:

resize.innerHTML = "<font size='1px'>&nbsp;</font>";

各位如果有更好解决方案的话记得通知我啊。

 

【图片切割】

关于图片切割的设计,有三个方法:
1.把图片设为背景图,通过设置背景图的位置来实现,但这样的缺点是只能按图片的正常比例实现,不够灵活;
2.把图片放到切割对象里面,通过设置图片的top和left实现,这个方法是可行,但下面有更简单的方法实现;
3.通过设置图片的clip来实现。

这个方法是从当年“珍藏”的代码中看到的,虽然以前接触过clip,但都忘了。
clip的作用是“检索或设置对象的可视区域。可视区域外的部分是透明的。”
依据上-右-下-左的顺序提供自对象左上角为(0,0)坐标计算的四个偏移数值来剪切。
例如:

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

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

下面说说具体实现原理:
首先需要一个容器,拖放对象,图片地址,显示宽度和高度,
还要插入三个层:
底图层:那个半透明的图片,
显示层:拖放对象中正常显示的那个部分,
拖动层:就是拖放对象,
其中为了底图层和显示层能完美的重叠,我两个层都用了绝对定位,定在左上角。
zIndex也要设置一下,保证三个层的顺序。

下面是很简单但最重要设置切割函数SetPos(),按拖放对象的参数进行切割:

Code

只要把SetPos()放到Drag的onMove和Resize的onResize中就行。
onMove和onResize分别是拖放和缩放时附加执行的程序。

程序中有一个Init()函数,它的作用是初始化一些设置,
特别分出来的原因是为了在用户修改了属性后执行一次,就可以根据修改过的属性重新初始化一次。
更好的做法是根据不同的属性修改修改需要修改的设置,这里我是偷懒了。

【切割预览】

至于预览效果也不难,根据预览高度宽度和拖放对象(显示层)的参数,计算出图片和预览图的比例scale:

Code

通过这个比例就可以计算出预览图的width、height、top、left了:

Code

最好根据这些参数切割预览图:

styleView.clip = "rect(" + pt + "px " + (pl + w) + "px " + (pt + h) + "px " + pl + "px)";

预览图效果就做好了。

【拖放补充】

在ie中,如果对象的position为absolute,并且背景是透明的话,会触发不了鼠标事件(真的是透明了)。
我的解决方法是在对象里面加一个透明的width和height都是100%的层,这样就能解决了。
但ie6中还有问题,在js修改对象的高度后,ie6并居然不会自动填充,
还好BlueDestiny告诉我解决的方法,设置对象的overflow为hidden就可以解决,
BlueDestiny说“出现这个问题的原因是因为IE6渲染的问题,通过某些CSS的属性可以让DOM改变之后再次渲染。”
虽然不太明白,但总算解决:

Code

源码:

Code

 

使用说明:

首先需要5个参数,分别是:容器对象、拖放对象、图片地址、图片宽度、图片高度。
可选设置:
Opacity:透明度(0到100),
dragTop:拖放对象top,
dragLeft:拖放对象left,
dragWidth:拖放对象宽度,
dragHeight:拖放对象高度,
缩放触发对象:
Right,Left,Up,Down,RightDown,LeftDown,RightUp,LeftUp,
Scale:是否按比例缩放,
View:预览对象,
viewWidth:预览宽度,
viewHeight:预览高度,

实例化对象:

Code

可以根据需要扩展,例如:

Code

补充:

里面的Drag拖放程序和Resize缩放程序是可以独立出来用的,
ImgCropper图片切割程序只是在内部实例化了这两个对象。

下载完整实例

posted on 2008-08-07 08:47  上校  阅读(678)  评论(1编辑  收藏  举报