背景:

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

前言:

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

效果:

  附件: 2008-10-05_132709.gif

动态效果请见:http://www.pin5i.com/demo/20304/main.html


原理:

【拖放程序】

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

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

【范围限制】

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

Code
  1. if(this.Limit){
  2.     //获取超出长度
  3.     var iRight = iLeft + this._obj.offsetWidth - this.mxRight, iBottom = iTop + this._obj.offsetHeight - this.mxBottom;
  4.     //这里是先设置右边下边再设置左边上边,可能会不准确
  5.     if(iRight > 0) iLeft -= iRight;
  6.     if(iBottom > 0) iTop -= iBottom;
  7.     if(this.mxLeft > iLeft) iLeft = this.mxLeft;
  8.     if(this.mxTop > iTop) iTop = this.mxTop;
  9. }
复制代码
【释放选择】

我以前就用的方法是设置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事件代替:
  1. addEventHandler(this.Drag, "losecapture", this._fS);
  2. 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
  1. var _style = this._obj.currentStyle || document.defaultView.getComputedStyle(this._obj, null);
  2. this._xBorder = parseInt(_style.borderLeftWidth) + parseInt(_style.borderRightWidth);
  3. this._yBorder = parseInt(_style.borderTopWidth) + parseInt(_style.borderBottomWidth);
复制代码
【比例缩放】

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

Code
  1. this.SetRight(oEvent);
  2. this.repairDown(parseInt(this.style_width / this._scale));
  3. this.repairRight(parseInt(this.style_height * this._scale));
复制代码
这样的缩放是以宽度优先的,对于上下两个点以高度优先会有更好的体验,
例如对于上面的点可以:
Code
  1. this.SetUp(oEvent);
  2. this.repairRight(parseInt(this.style_height * this._scale));
  3. this.repairUp(parseInt(this.style_width / this._scale));
复制代码
对于上下左右四个点,更好的体验当然是以该点为中心缩放,
各位有兴趣的话可以把这个也做出来,写多四个修正程序对应这几个点就行。

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

【鼠标捕获补充】

setCapture解决了ie中鼠标捕获的问题,但ff下的鼠标捕获还有问题。
当层的内部没有文本内容时,ie捕获正常,但ff在拖放到浏览器外时捕获就会失效,
暂时的解决方法只有插入文本,例如:
  1. resize.innerHTML = "<font size='1px'> </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
  1. var o = this.Drag;
  2. this._cropper.style.clip = "rect(" + o.offsetTop + "px " + (o.offsetLeft + o.offsetWidth) + "px " + (o.offsetTop + o.offsetHeight) + "px " + o.offsetLeft + "px)";
复制代码
只要把SetPos()放到Drag的onMove和Resize的onResize中就行。
onMove和onResize分别是拖放和缩放时附加执行的程序。

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

【切割预览】

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

Code
  1. var o = this.Drag, h = this.viewWidth, w = h * o.offsetWidth / o.offsetHeight;
  2. if(w > this.viewHeight){ w = this.viewHeight; h = w * o.offsetHeight / o.offsetWidth; }
  3. var scale = h / o.offsetHeight
复制代码
通过这个比例就可以计算出预览图的width、height、top、left了:

Code
  1. var scale = h / o.offsetHeight, ph = this.Height * scale, pw = this.Width * scale, pt = o.offsetTop * scale, pl = o.offsetLeft * scale, styleView = this._view.style;
  2. styleView.width = pw + "px"; styleView.height = ph + "px";
  3. styleView.top = - pt + "px "; styleView.left = - pl + "px";
复制代码
最好根据这些参数切割预览图:
  1. 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
  1. this.Drag.style.overflow = "hidden";
  2. (function(style){
  3. style.width = style.height = "100%"; style.backgroundColor = "#fff"; style.filter = "alpha(opacity:0)";
  4. })(this.Drag.appendChild(document.createElement("div")).style)
复制代码
源码:

Code
  1. var $ = function (id) {
  2.     return "string" == typeof id ? document.getElementById(id) : id;
  3. };

  4. var isIE = (document.all) ? true : false;

  5. function addEventHandler(oTarget, sEventType, fnHandler) {
  6.     if (oTarget.addEventListener) {
  7.         oTarget.addEventListener(sEventType, fnHandler, false);
  8.     } else if (oTarget.attachEvent) {
  9.         oTarget.attachEvent("on" + sEventType, fnHandler);
  10.     } else {
  11.         oTarget["on" + sEventType] = fnHandler;
  12.     }
  13. };

  14. function removeEventHandler(oTarget, sEventType, fnHandler) {
  15.     if (oTarget.removeEventListener) {
  16.         oTarget.removeEventListener(sEventType, fnHandler, false);
  17.     } else if (oTarget.detachEvent) {
  18.         oTarget.detachEvent("on" + sEventType, fnHandler);
  19.     } else {
  20.         oTarget["on" + sEventType] = null;
  21.     }
  22. };


  23. var Class = {
  24.   create: function() {
  25.     return function() {
  26.       this.initialize.apply(this, arguments);
  27.     }
  28.   }
  29. }

  30. Object.extend = function(destination, source) {
  31.     for (var property in source) {
  32.         destination[property] = source[property];
  33.     }
  34.     return destination;
  35. }

  36. //拖放程序
  37. var Drag = Class.create();
  38. Drag.prototype = {
  39.   //拖放对象,触发对象
  40.   initialize: function(obj, drag, options) {
  41.     var oThis = this;
  42.    
  43.     this._obj = $(obj);//拖放对象
  44.     this.Drag = $(drag);//触发对象
  45.     this._x = this._y = 0;//记录鼠标相对拖放对象的位置
  46.     //事件对象(用于移除事件)
  47.     this._fM = function(e){ oThis.Move(window.event || e); }
  48.     this._fS = function(){ oThis.Stop(); }
  49.    
  50.     this.SetOptions(options);
  51.    
  52.     this.Limit = !!this.options.Limit;
  53.     this.mxLeft = parseInt(this.options.mxLeft);
  54.     this.mxRight = parseInt(this.options.mxRight);
  55.     this.mxTop = parseInt(this.options.mxTop);
  56.     this.mxBottom = parseInt(this.options.mxBottom);
  57.    
  58.     this.onMove = this.options.onMove;
  59.    
  60.     this._obj.style.position = "absolute";
  61.     addEventHandler(this.Drag, "mousedown", function(e){ oThis.Start(window.event || e); });
  62.   },
  63.   //设置默认属性
  64.   SetOptions: function(options) {
  65.     this.options = {//默认值
  66.         Limit:        false,//是否设置限制(为true时下面参数有用,可以是负数)
  67.         mxLeft:        0,//左边限制
  68.         mxRight:    0,//右边限制
  69.         mxTop:        0,//上边限制
  70.         mxBottom:    0,//下边限制
  71.         onMove:        function(){}//移动时执行
  72.     };
  73.     Object.extend(this.options, options || {});
  74.   },
  75.   //准备拖动
  76.   Start: function(oEvent) {
  77.     //记录鼠标相对拖放对象的位置
  78.     this._x = oEvent.clientX - this._obj.offsetLeft;
  79.     this._y = oEvent.clientY - this._obj.offsetTop;
  80.     //mousemove时移动 mouseup时停止
  81.     addEventHandler(document, "mousemove", this._fM);
  82.     addEventHandler(document, "mouseup", this._fS);
  83.     //使鼠标移到窗口外也能释放
  84.     if(isIE){
  85.         addEventHandler(this.Drag, "losecapture", this._fS);
  86.         this.Drag.setCapture();
  87.     }else{
  88.         addEventHandler(window, "blur", this._fS);
  89.     }
  90.   },
  91.   //拖动
  92.   Move: function(oEvent) {
  93.     //清除选择(ie设置捕获后默认带这个)
  94.     window.getSelection && window.getSelection().removeAllRanges();
  95.     //当前鼠标位置减去相对拖放对象的位置得到offset位置
  96.     var iLeft = oEvent.clientX - this._x, iTop = oEvent.clientY - this._y;
  97.     //设置范围限制
  98.     if(this.Limit){
  99.         //获取超出长度
  100.         var iRight = iLeft + this._obj.offsetWidth - this.mxRight, iBottom = iTop + this._obj.offsetHeight - this.mxBottom;
  101.         //这里是先设置右边下边再设置左边上边,可能会不准确
  102.         if(iRight > 0) iLeft -= iRight;
  103.         if(iBottom > 0) iTop -= iBottom;
  104.         if(this.mxLeft > iLeft) iLeft = this.mxLeft;
  105.         if(this.mxTop > iTop) iTop = this.mxTop;
  106.     }
  107.     //设置位置
  108.     this._obj.style.left = iLeft + "px";
  109.     this._obj.style.top = iTop + "px";   
  110.     //附加程序
  111.     this.onMove();
  112.   },
  113.   //停止拖动
  114.   Stop: function() {
  115.     //移除事件
  116.     removeEventHandler(document, "mousemove", this._fM);
  117.     removeEventHandler(document, "mouseup", this._fS);
  118.     if(isIE){
  119.         removeEventHandler(this.Drag, "losecapture", this._fS);
  120.         this.Drag.releaseCapture();
  121.     }else{
  122.         removeEventHandler(window, "blur", this._fS);
  123.     }
  124.   }
  125. };

  126. //缩放程序
  127. var Resize = Class.create();
  128. Resize.prototype = {
  129.   //缩放对象
  130.   initialize: function(obj, options) {
  131.     var oThis = this;
  132.    
  133.     this._obj = $(obj);//缩放对象
  134.     this._right = this._down = this._left = this._up = 0;//坐标参数
  135.     this._scale = 1;//比例参数
  136.     this._touch = null;//当前触发对象
  137.    
  138.     //用currentStyle(ff用getComputedStyle)取得最终样式
  139.     var _style = this._obj.currentStyle || document.defaultView.getComputedStyle(this._obj, null);
  140.     this._xBorder = parseInt(_style.borderLeftWidth) + parseInt(_style.borderRightWidth);
  141.     this._yBorder = parseInt(_style.borderTopWidth) + parseInt(_style.borderBottomWidth);
  142.    
  143.     //事件对象(用于移除事件)
  144.     this._fR = function(e){ oThis.Resize(e); }
  145.     this._fS = function(){ oThis.Stop(); }
  146.    
  147.     this.SetOptions(options);
  148.    
  149.     this.Limit = !!this.options.Limit;
  150.     this.mxLeft = parseInt(this.options.mxLeft);
  151.     this.mxRight = parseInt(this.options.mxRight);
  152.     this.mxTop = parseInt(this.options.mxTop);
  153.     this.mxBottom = parseInt(this.options.mxBottom);
  154.    
  155.     this.MinWidth = parseInt(this.options.MinWidth);
  156.     this.MinHeight = parseInt(this.options.MinHeight);
  157.     this.Scale = !!this.options.Scale;
  158.     this.onResize = this.options.onResize;
  159.    
  160.     this._obj.style.position = "absolute";
  161.   },
  162.   //设置默认属性
  163.   SetOptions: function(options) {
  164.     this.options = {//默认值
  165.         Limit:        false,//是否设置限制(为true时下面mx参数有用)
  166.         mxLeft:        0,//左边限制
  167.         mxRight:    0,//右边限制
  168.         mxTop:        0,//上边限制
  169.         mxBottom:    0,//下边限制
  170.         MinWidth:    50,//最小宽度
  171.         MinHeight:    50,//最小高度
  172.         Scale:        false,//是否按比例缩放
  173.         onResize:    function(){}//缩放时执行
  174.     };
  175.     Object.extend(this.options, options || {});
  176.   },
  177.   //设置触发对象
  178.   Set: function(resize, side) {
  179.     var oThis = this, resize = $(resize), _fun, _cursor;
  180.     if(!resize) return;
  181.     //根据方向设置 _fun是缩放时执行的程序 _cursor是鼠标样式
  182.     switch (side.toLowerCase()) {
  183.     case "up" :
  184.         _fun = function(e){ if(oThis.Scale){ oThis.scaleUpRight(e); }else{ oThis.SetUp(e); } };
  185.         _cursor = "n-resize";
  186.         break;
  187.     case "down" :
  188.         _fun = function(e){ if(oThis.Scale){ oThis.scaleDownRight(e); }else{ oThis.SetDown(e); } };
  189.         _cursor = "n-resize";
  190.         break;
  191.     case "left" :
  192.         _fun = function(e){ if(oThis.Scale){ oThis.scaleLeftUp(e); }else{ oThis.SetLeft(e); } };
  193.         _cursor = "e-resize";
  194.         break;
  195.     case "right" :
  196.         _fun = function(e){ if(oThis.Scale){ oThis.scaleRightDown(e); }else{ oThis.SetRight(e); } };
  197.         _cursor = "e-resize";
  198.         break;
  199.     case "left-up" :
  200.         _fun = function(e){ if(oThis.Scale){ oThis.scaleLeftUp(e); }else{ oThis.SetLeft(e); oThis.SetUp(e); } };
  201.         _cursor = "nw-resize";
  202.         break;
  203.     case "right-up" :
  204.         _fun = function(e){ if(oThis.Scale){ oThis.scaleRightUp(e); }else{ oThis.SetRight(e); oThis.SetUp(e); } };
  205.         _cursor = "ne-resize";
  206.         break;
  207.     case "left-down" :
  208.         _fun = function(e){ if(oThis.Scale){ oThis.scaleLeftDown(e); }else{ oThis.SetLeft(e); oThis.SetDown(e); } };
  209.         _cursor = "ne-resize";
  210.         break;
  211.     case "right-down" :
  212.     default :
  213.         _fun = function(e){ if(oThis.Scale){ oThis.scaleRightDown(e); }else{ oThis.SetRight(e); oThis.SetDown(e); } };
  214.         _cursor = "nw-resize";
  215.     }
  216.     //插入字符解决ff下捕获效的问题
  217.     if(!isIE){ resize.innerHTML = "<font size='1px'> </font>"; }
  218.     //设置触发对象
  219.     resize.style.cursor = _cursor;
  220.     addEventHandler(resize, "mousedown", function(e){ oThis._fun = _fun; oThis._touch = resize; oThis.Start(window.event || e); });
  221.   },
  222.   //准备缩放
  223.   Start: function(oEvent, o) {   
  224.     //防止冒泡
  225.     if(isIE){ oEvent.cancelBubble = true; } else { oEvent.stopPropagation(); }
  226.     //计算样式初始值
  227.     this.style_width = this._obj.offsetWidth;
  228.     this.style_height = this._obj.offsetHeight;
  229.     this.style_left = this._obj.offsetLeft;
  230.     this.style_top = this._obj.offsetTop;
  231.     //设置缩放比例
  232.     if(this.Scale){ this._scale = this.style_width / this.style_height; }
  233.     //计算当前边的对应另一条边的坐标 例如右边缩放时需要左边界坐标
  234.     this._left = oEvent.clientX - this.style_width;
  235.     this._right = oEvent.clientX + this.style_width;
  236.     this._up = oEvent.clientY - this.style_height;
  237.     this._down = oEvent.clientY + this.style_height;
  238.     //如果有范围 先计算好范围内最大宽度和高度
  239.     if(this.Limit){
  240.         this._mxRight = this.mxRight - this._obj.offsetLeft;
  241.         this._mxDown = this.mxBottom - this._obj.offsetTop;
  242.         this._mxLeft = this.mxLeft + this.style_width + this._obj.offsetLeft;
  243.         this._mxUp = this.mxTop + this.style_height + this._obj.offsetTop;
  244.     }
  245.     //mousemove时缩放 mouseup时停止
  246.     addEventHandler(document, "mousemove", this._fR);
  247.     addEventHandler(document, "mouseup", this._fS);
  248.    
  249.     //使鼠标移到窗口外也能释放
  250.     if(isIE){
  251.         addEventHandler(this._touch, "losecapture", this._fS);
  252.         this._touch.setCapture();
  253.     }else{
  254.         addEventHandler(window, "blur", this._fS);
  255.     }
  256.   }, 
  257.   //缩放
  258.   Resize: function(e) {
  259.     //没有触发对象的话返回
  260.     if(!this._touch) return;
  261.     //清除选择(ie设置捕获后默认带这个)
  262.     window.getSelection && window.getSelection().removeAllRanges();
  263.     //执行缩放程序
  264.     this._fun(window.event || e);
  265.     //设置样式
  266.     //因为计算用的offset是把边框算进去的所以要减去
  267.     this._obj.style.width = this.style_width - this._xBorder + "px";
  268.     this._obj.style.height = this.style_height - this._yBorder + "px";
  269.     this._obj.style.top = this.style_top + "px";
  270.     this._obj.style.left = this.style_left + "px";   
  271.     //附加程序
  272.     this.onResize();
  273.   },
  274.   //普通缩放
  275.   //右边
  276.   SetRight: function(oEvent) {
  277.     //当前坐标位置减去左边的坐标等于当前宽度
  278.     this.repairRight(oEvent.clientX - this._left);
  279.   },
  280.   //下边
  281.   SetDown: function(oEvent) {
  282.     this.repairDown(oEvent.clientY - this._up);
  283.   },
  284.   //左边
  285.   SetLeft: function(oEvent) {
  286.     //右边的坐标减去当前坐标位置等于当前宽度
  287.     this.repairLeft(this._right - oEvent.clientX);
  288.   },
  289.   //上边
  290.   SetUp: function(oEvent) {
  291.     this.repairUp(this._down - oEvent.clientY);
  292.   },
  293.   //比例缩放
  294.   //比例缩放右下
  295.   scaleRightDown: function(oEvent) {
  296.     //先计算宽度然后按比例计算高度最后根据确定的高度计算宽度(宽度优先)
  297.     this.SetRight(oEvent);
  298.     this.repairDown(parseInt(this.style_width / this._scale));
  299.     this.repairRight(parseInt(this.style_height * this._scale));
  300.   },
  301.   //比例缩放左下
  302.   scaleLeftDown: function(oEvent) {
  303.     this.SetLeft(oEvent);
  304.     this.repairDown(parseInt(this.style_width / this._scale));
  305.     this.repairLeft(parseInt(this.style_height * this._scale));
  306.   },
  307.   //比例缩放右上
  308.   scaleRightUp: function(oEvent) {
  309.     this.SetRight(oEvent);
  310.     this.repairUp(parseInt(this.style_width / this._scale));
  311.     this.repairRight(parseInt(this.style_height * this._scale));
  312.   },
  313.   //比例缩放左上
  314.   scaleLeftUp: function(oEvent) {
  315.     this.SetLeft(oEvent);
  316.     this.repairUp(parseInt(this.style_width / this._scale));
  317.     this.repairLeft(parseInt(this.style_height * this._scale));
  318.   },
  319.   //这里是高度优先用于上下两边(体验更好)
  320.   //比例缩放下右
  321.   scaleDownRight: function(oEvent) {
  322.     this.SetDown(oEvent);
  323.     this.repairRight(parseInt(this.style_height * this._scale));
  324.     this.repairDown(parseInt(this.style_width / this._scale));
  325.   },
  326.   //比例缩放上右
  327.   scaleUpRight: function(oEvent) {
  328.     this.SetUp(oEvent);
  329.     this.repairRight(parseInt(this.style_height * this._scale));
  330.     this.repairUp(parseInt(this.style_width / this._scale));
  331.   },
  332.   //修正程序
  333.   //修正右边
  334.   repairRight: function(iWidth) {
  335.     //右边和下边只要设置宽度和高度就行
  336.     //当少于最少宽度
  337.     if (iWidth < this.MinWidth){ iWidth = this.MinWidth; }
  338.     //当超过当前设定的最大宽度
  339.     if(this.Limit && iWidth > this._mxRight){ iWidth = this._mxRight; }
  340.     //修改样式
  341.     this.style_width = iWidth;
  342.   },
  343.   //修正下边
  344.   repairDown: function(iHeight) {
  345.     if (iHeight < this.MinHeight){ iHeight = this.MinHeight; }
  346.     if(this.Limit && iHeight > this._mxDown){ iHeight = this._mxDown; }
  347.     this.style_height = iHeight;
  348.   },
  349.   //修正左边
  350.   repairLeft: function(iWidth) {
  351.     //左边和上边比较麻烦 因为还要计算left和top
  352.     //当少于最少宽度
  353.     if (iWidth < this.MinWidth){ iWidth = this.MinWidth; }
  354.     //当超过当前设定的最大宽度
  355.     else if(this.Limit && iWidth > this._mxLeft){ iWidth = this._mxLeft; }
  356.     //修改样式
  357.     this.style_width = iWidth;
  358.     this.style_left = this._obj.offsetLeft + this._obj.offsetWidth - iWidth;
  359.   },
  360.   //修正上边
  361.   repairUp: function(iHeight) {
  362.     if(iHeight < this.MinHeight){ iHeight = this.MinHeight; }
  363.     else if(this.Limit && iHeight > this._mxUp){ iHeight = this._mxUp; }
  364.     this.style_height = iHeight;
  365.     this.style_top = this._obj.offsetTop + this._obj.offsetHeight - iHeight;
  366.   },
  367.   //停止缩放
  368.   Stop: function() {
  369.     //移除事件
  370.     removeEventHandler(document, "mousemove", this._fR);
  371.     removeEventHandler(document, "mouseup", this._fS);
  372.     if(isIE){
  373.         removeEventHandler(this._touch, "losecapture", this._fS);
  374.         this._touch.releaseCapture();
  375.     }else{
  376.         removeEventHandler(window, "blur", this._fS);
  377.     }
  378.     this._touch = null;
  379.   }
  380. };


  381. //图片切割
  382. var ImgCropper = Class.create();
  383. ImgCropper.prototype = {
  384.   //容器对象,拖放缩放对象,图片地址,宽度,高度
  385.   initialize: function(container, drag, url, width, height, options) {
  386.     var oThis = this;
  387.    
  388.     //容器对象
  389.     this.Container = $(container);
  390.     this.Container.style.position = "relative";
  391.     this.Container.style.overflow = "hidden";
  392.    
  393.     //拖放对象
  394.     this.Drag = $(drag);
  395.     this.Drag.style.cursor = "move";
  396.     this.Drag.style.zIndex = 200;
  397.     if(isIE){
  398.         //设置overflow解决ie6的渲染问题(缩放时填充对象高度的问题)
  399.         this.Drag.style.overflow = "hidden";
  400.         //ie下用一个透明的层填充拖放对象 不填充的话onmousedown会失效(未知原因)
  401.         (function(style){
  402.             style.width = style.height = "100%"; style.backgroundColor = "#fff"; style.filter = "alpha(opacity:0)";
  403.         })(this.Drag.appendChild(document.createElement("div")).style)
  404.     }else{
  405.         //插入字符解决ff下捕获失效的问题
  406.         this.Drag.innerHTML += "<font size='1px'> </font>";
  407.     }
  408.    
  409.     this._pic = this.Container.appendChild(document.createElement("img"));//图片对象
  410.     this._cropper = this.Container.appendChild(document.createElement("img"));//切割对象
  411.     this._pic.style.position = this._cropper.style.position = "absolute";
  412.     this._pic.style.top = this._pic.style.left = this._cropper.style.top = this._cropper.style.left = "0";//对齐
  413.     this._cropper.style.zIndex = 100;
  414.     this._cropper.onload = function(){ oThis.SetPos(); }
  415.    
  416.     this.Url = url;//图片地址
  417.     this.Width = parseInt(width);//宽度
  418.     this.Height = parseInt(height);//高度
  419.    
  420.     this.SetOptions(options);
  421.    
  422.     this.Opacity = parseInt(this.options.Opacity);
  423.     this.dragTop = parseInt(this.options.dragTop);
  424.     this.dragLeft = parseInt(this.options.dragLeft);
  425.     this.dragWidth = parseInt(this.options.dragWidth);
  426.     this.dragHeight = parseInt(this.options.dragHeight);
  427.    
  428.     //设置预览对象
  429.     this.View = $(this.options.View) || null;//预览对象
  430.     this.viewWidth = parseInt(this.options.viewWidth);
  431.     this.viewHeight = parseInt(this.options.viewHeight);
  432.     this._view = null;//预览图片对象
  433.     if(this.View){
  434.         this.View.style.position = "relative";
  435.         this.View.style.overflow = "hidden";
  436.         this._view = this.View.appendChild(document.createElement("img"));
  437.         this._view.style.position = "absolute";
  438.     }
  439.    
  440.     this.Scale = parseInt(this.options.Scale);
  441.    
  442.     //设置拖放
  443.     this._drag = new Drag(this.Drag, this.Drag, { Limit: true, onMove: function(){ oThis.SetPos(); } });
  444.     //设置缩放
  445.     this._resize = this.GetResize();
  446.    
  447.     this.Init();
  448.   },
  449.   //设置默认属性
  450.   SetOptions: function(options) {
  451.     this.options = {//默认值
  452.         Opacity:    50,//透明度(0到100)   
  453.         //拖放位置和宽高
  454.         dragTop:    0,
  455.         dragLeft:    0,
  456.         dragWidth:    100,
  457.         dragHeight:    100,
  458.         //缩放触发对象
  459.         Right:        "",
  460.         Left:        "",
  461.         Up:            "",
  462.         Down:        "",
  463.         RightDown:    "",
  464.         LeftDown:    "",
  465.         RightUp:    "",
  466.         LeftUp:        "",
  467.         Scale:        false,//是否按比例缩放
  468.         //预览对象设置
  469.         View:    "",//预览对象
  470.         viewWidth:    100,//预览宽度
  471.         viewHeight:    100//预览高度
  472.     };
  473.     Object.extend(this.options, options || {});
  474.   },
  475.   //初始化对象
  476.   Init: function() {
  477.     var oThis = this;
  478.    
  479.     //设置容器
  480.     this.Container.style.width = this.Width + "px";
  481.     this.Container.style.height = this.Height + "px";
  482.    
  483.     //设置拖放对象
  484.     this.Drag.style.top = this.dragTop + "px";
  485.     this.Drag.style.left = this.dragLeft + "px";
  486.     this.Drag.style.width = this.dragWidth + "px";
  487.     this.Drag.style.height = this.dragHeight + "px";
  488.    
  489.     //设置切割对象
  490.     this._pic.src = this._cropper.src = this.Url;
  491.     this._pic.style.width = this._cropper.style.width = this.Width + "px";
  492.     this._pic.style.height = this._cropper.style.height = this.Height + "px";
  493.     if(isIE){
  494.         this._pic.style.filter = "alpha(opacity:" + this.Opacity + ")";
  495.     } else {
  496.         this._pic.style.opacity = this.Opacity / 100;
  497.     }
  498.    
  499.     //设置预览对象
  500.     if(this.View){ this._view.src = this.Url; }
  501.    
  502.     //设置拖放
  503.     this._drag.mxRight = this.Width; this._drag.mxBottom = this.Height;
  504.     //设置缩放
  505.     if(this._resize){ this._resize.mxRight = this.Width; this._resize.mxBottom = this.Height; this._resize.Scale = this.Scale; }
  506.   },
  507.   //设置获取缩放对象
  508.   GetResize: function() {
  509.     var op = this.options;
  510.     //有触发对象时才设置
  511.     if(op.RightDown || op.LeftDown || op.RightUp || op.LeftUp || op.Right || op.Left || op.Up || op.Down ){
  512.         var oThis = this, _resize = new Resize(this.Drag, { Limit: true, onResize: function(){ oThis.SetPos(); } });
  513.        
  514.         //设置缩放触发对象
  515.         if(op.RightDown){ _resize.Set(op.RightDown, "right-down"); }
  516.         if(op.LeftDown){ _resize.Set(op.LeftDown, "left-down"); }
  517.        
  518.         if(op.RightUp){ _resize.Set(op.RightUp, "right-up"); }
  519.         if(op.LeftUp){ _resize.Set(op.LeftUp, "left-up"); }
  520.        
  521.         if(op.Right){ _resize.Set(op.Right, "right"); }
  522.         if(op.Left){ _resize.Set(op.Left, "left"); }
  523.        
  524.         if(op.Up){ _resize.Set(op.Up, "up"); }
  525.         if(op.Down){ _resize.Set(op.Down, "down"); }
  526.        
  527.         return _resize;
  528.     } else { return null; }
  529.   }, 
  530.   //设置切割
  531.   SetPos: function() {
  532.     var o = this.Drag;
  533.     //按拖放对象的参数进行切割
  534.     this._cropper.style.clip = "rect(" + o.offsetTop + "px " + (o.offsetLeft + o.offsetWidth) + "px " + (o.offsetTop + o.offsetHeight) + "px " + o.offsetLeft + "px)";
  535.     //切割预览
  536.     if(this.View) this.PreView();
  537.   }, 
  538.   //切割预览
  539.   PreView: function() {
  540.     //按比例设置宽度和高度
  541.     var o = this.Drag, h = this.viewWidth, w = h * o.offsetWidth / o.offsetHeight;
  542.     if(w > this.viewHeight){ w = this.viewHeight; h = w * o.offsetHeight / o.offsetWidth; }
  543.     //获取对应比例尺寸
  544.     var scale = h / o.offsetHeight, ph = this.Height * scale, pw = this.Width * scale, pt = o.offsetTop * scale, pl = o.offsetLeft * scale, styleView = this._view.style;
  545.     //设置样式
  546.     styleView.width = pw + "px"; styleView.height = ph + "px";
  547.     styleView.top = - pt + "px "; styleView.left = - pl + "px";
  548.     //切割预览图
  549.     styleView.clip = "rect(" + pt + "px " + (pl + w) + "px " + (pt + h) + "px " + pl + "px)";
  550.   }
  551. }
复制代码
使用说明:

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

实例化对象:

Code
  1. var ic = new ImgCropper("bgDiv", "dragDiv", "1.jpg", 400, 500, {
  2. dragTop: 50, dragLeft: 50,
  3. Right: "rRight", Left: "rLeft", Up: "rUp", Down: "rDown",
  4. RightDown: "rRightDown", LeftDown: "rLeftDown", RightUp: "rRightUp", LeftUp: "rLeftUp",
  5. View: "viewDiv", viewWidth: 200, viewHeight: 200
  6. })
复制代码
可以根据需要扩展,例如:

Code
  1. //设置图片大小
  2. function Size(w, h){
  3.     ic.Width = w;
  4.     ic.Height = h;
  5.     ic.Init();
  6. }
  7. //换图片
  8. function Pic(url){
  9.     ic.Url = url;
  10.     ic.Init();
  11. }
  12. //设置透明度
  13. function Opacity(i){
  14.     ic.Opacity = i;
  15.     ic.Init();
  16. }
  17. //是否使用比例
  18. function Scale(b){
  19.     ic.Scale = b;
  20.     ic.Init();
  21. }
复制代码
补充:
里面的Drag拖放程序和Resize缩放程序是可以独立出来用的,
ImgCropper图片切割程序只是在内部实例化了这两个对象。(文/cloudgamer

下载完整实例:
TOP