基于原生JS实现图片裁剪

前端之家收集整理的这篇文章主要介绍了基于原生JS实现图片裁剪前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

下面是我自己写的图片裁剪的功能介绍:

可以利用鼠标拖拉,产生裁剪框

可以改变裁剪框大小

点击确定,返回裁剪数据

原理

完成裁剪的方法有两种:

1、利用HTML5新增拖拽事件drag drop等

2、传统方法,利用鼠标事件,mousedown、mousemove等

在这里,我们采用方法2。

裁剪区域的形成

要进行裁剪首先要形成裁剪区域,这个裁剪区域的形成我们可以与鼠标移动的距离相关联。鼠标移动多远,裁剪区域就有多大。

如下图:

而这横向与纵向移动的距离如何计算呢?当我们点下鼠标时,就能够通过event事件对象获取鼠标点击位置,而移动鼠标时,也能够通过event获取鼠标的位置,通过两次鼠标位置的改变,就能够获得鼠标的移动距离。

获取鼠标位置的属性是clientX以及clientY

阴影区域的形成

接下来就是绘制阴影区域。被裁剪图片中除裁剪区域以外的部分,都属于阴影部分,也可以不绘制该区域,绘制该区域是为了让用户更清晰的看清裁剪区域。

我将该区域分成了上下左右四个部分,见下图分布:

那么该区域如果计算呢?这时就要用到Dom元素的偏移值了,利用裁剪区域的左偏移值减去图片本身的左偏移值就是左阴影的宽,利用裁剪区域的上偏移值减去图片的上偏移值,等于上阴影的高度值。如下图:

获取到左阴影、上阴影的值后,就能够通过这两个将其他阴影的属性计算出来。

图片的偏移值有两种取法

1.利用offsetLeft 与 offsetTop 值 弊端 如果dom元素有border margin等值会将这些值计算在内

2.获取dom的css属性 弊端 预定义的css有关 如果没定义left top就无法获取

这两种方法都有各自的弊端,视不同情况来使用

裁剪越界的阻止

裁剪区域的计算是通过鼠标的移动距离来计算的,因此会出现裁剪区域越界的情况,而这情况又分成两种:

1.裁剪过程中越界

2.移动裁剪区域时越界

那么下面就来说说如何防止越界。

裁剪越界

什么是裁剪时越界?就是鼠标拖动区域超出了图片的返回,形成了越界,如下图:

对于这种越界需要判断裁剪区域的右侧相对于浏览器左侧的位置 不能够超过 图片右侧的位置相当于浏览器左侧的位置;同时裁剪区域底部相对于浏览器顶部位置 不能够超过 图片底部相对应浏览器顶部的位置。还是画图来说明:

TX >= PX 时就让TX的值强制为一固定值。

TX与PX的计算方法,假设裁剪区域为oTailor图片区域oPicture:

同理,可以按照上述方法对左侧越界,上侧越界,下侧越界进行限制,就不多赘述。

移动越界

移动越界指的是已经形成了裁剪区域了,但通过鼠标移动裁剪区域时产生了越界。这个理解比较简单,就不画图介绍了。这种越界与dom拖拽越界限制一致,通过判断鼠标移动距离是否超过了图片区域来判断。

原理与问题解决了,现在开始来完成实际功能

准备工作

在做之前,先做一些准备工作,磨刀不误砍柴功。

网页布局准备

网页布局部分关键代码如下:

Box">
Box_border1">
Box_border2">
Box_border3">
Box_border4">
Box_handle" id="Box_1">
Box_handle" id="Box_2">
Box_handle" id="Box_3">
Box_handle" id="Box_4">
Box_handle" id="Box_5">
Box_handle" id="Box_6">
Box_handle" id="Box_7">
Box_handle" id="Box_8">

<button class="confirm">确定

其中img_Box表示的是裁剪区域,outer表示阴影区域,而img_Box中的div是裁剪区域的边框

样式控制如下:

background: #454545;
}
.main {
width: 500px;
margin:50px auto;
}
.main img {
width: 500px;
position: absolute;
left: 450px;
top: 50px;
}
.img_Box {
overflow: hidden;
position: absolute;
top:0px;
left: 0px;
z-index: 2;
}
.outer {
overflow: hidden;
background: #000;
opacity: 0.4;
position: absolute;
top:0px;
left: 0px;
z-index: 0;
}
.Box_border1,.Box_border2,.Box_border3,.Box_border4 {
opacity: 0.5;
}
.Box_border1 {
background: url(./images/border-anim-v.gif) repeat-y left top;
}
.Box_border2 {
background: url(./images/border-anim-h.gif) repeat-x left top;
}
.Box_border3 {
background: url(./images/border-anim-v.gif) repeat-y right top;
}
.Box_border4 {
background: url(./images/border-anim-h.gif) repeat-x right bottom;
}
.Box_handle {
background: #fff;
border: 1px solid #000;
opacity: 0.5;
}
.confrim {
width: 80px;
height: 35px;
}

布局效果如下:

通用函数

完成图片裁剪,通过上述原理,可以知道需要大量获取标签对象以及标签的css属性等,所以可以编写通用函数,更好的获取这些值。如下:

Dom获取函数

获取 */ function $(dom) {

function getDom(dom) {

var str = dom.charAt(0);    
switch( str ) {
  case '.' :
    this.ele = document.getElementsByClassName(dom.substring(1))||null;  

    break;

  case '#' :

    this.ele = document.getElementById(dom.substring(1)) || null;

    break;

  default : 

    if(document.getElementsByTagName(dom).length) {

      this.ele = document.getElementsByTagName(dom);                

    } else if(document.getElementsByName(dom).length) {

      this.ele = document.getElementsByName(dom);                  

    } else {          
      this.ele = null;
    }
}
return this;      

};

getDom.prototype.get = function(num) {
return this.ele[num]||this.ele;
}

getDom.prototype.insert = function(value,num) {
this.ele[num].innerHTML = value;
}

return new getDom(dom);
}

Css属性获取函数

Css属性获取分成两种,一种是IE的,使用currentStyle;另一种是其他主流浏览器,使用getComputedStyle,以下是兼容版本:

获取 */ function getCss(o,key){ return o.currentStyle? o.currentStyle[key] : document.defaultView.getComputedStyle(o,false)[key]; };

赋值函数

编写时经常遇到对Dom的样式进行赋值,为方便,我专门编写了一个函数用于赋值:

函数 - @param : obj 被赋值对象 - @param : option 进行的操作 - @parma : value 赋值内容 */ function setAssign(obj,option,value) {

switch(option) {
case 'width':
obj.style.width = value;
break;
case 'height':
obj.style.height = value;
break;
case 'top':
obj.style.top = value;
break;
case 'left':
obj.style.left = value;
break;
case 'position':
obj.style.position = value;
break;
case 'cursor':
obj.style.cursor = value;
}
}

好了准备工作基本完成,现在就正式开始编写。

通过点击与移动事件完成裁剪区域绘制

图片设置mousedown以及mousemove事件监视,如下:

// 初始鼠标位置
var tempX = oEvent.clientX;
var tempY = oEvent.clientY;

// 调整裁剪区域位置
oTailor.style.left = oEvent.clientX + 'px';
oTailor.style.top = oEvent.clientY + 'px';

// 鼠标在图片上移动 绘制裁剪区域 阴影区域
document.onmousemove = function(ev) {

// 鼠标移动事件对象
var oEvent = ev || window.event;

// 当前鼠标位置减去鼠标之前的鼠标位置 等于 鼠标移动距离
var sLeft = oEvent.clientX - tempX;
var sTop = oEvent.clientY - tempY;

// 裁剪越界限制 只需限制右侧 与 下侧
if((oTailor.offsetLeft+oTailor.offsetWidth) >= (oPicture.offsetLeft+oPicture.offsetWidth)) {
  sLeft = oPicture.offsetLeft+oPicture.offsetWidth - oTailor.offsetLeft;
}  
if((oTailor.offsetTop+oTailor.offsetHeight) >= (oPicture.offsetTop+oPicture.offsetHeight)) {
  sTop = oPicture.offsetTop+oPicture.offsetHeight - oTailor.offsetTop;
}

// 裁剪区域绘制
oTailor.style.width = sLeft + 'px';
oTailor.style.height = sTop + 'px';

// 裁剪区域<a href="https://www.jb51.cc/tag/xianshi/" target="_blank" class="keywords">显示</a>
oTailor.style.display = 'block';

// 阴影区域<a href="https://www.jb51.cc/tag/xianshi/" target="_blank" class="keywords">显示</a>
for (var i = 0; i < oShadow.length; i++) {
  oShadow[i].style.display = 'block';
}

// 阴影区域绘制
shadow(oPicture,oTailor,oShadow);

// <a href="https://www.jb51.cc/tag/tianjia/" target="_blank" class="keywords">添加</a>裁剪边框
tailorBorder(oDiv,oHandle,oTailor);

// 阻止默认事件
oEvent.preventDefault();

};

// 鼠标松开 将移动事件取消
document.onmouseup = function(ev) {
var oEvent = ev || window.event;

// 移动事件取消
document.onmousemove = null;

// 阻止默认事件
oEvent.preventDefault();

};

// 阻止默认事件
oEvent.preventDefault();
}

阴影区域绘制

图片dom对象 * @param:oTailor 裁剪区域dom对象 * @param:oShadow 阴影区域dom对象 */ function shadow(oPicture,oShadow) {

// 左侧阴影区
setAssign(oShadow[0],'width',(parseInt(getCss(oTailor,'left')) - parseInt(getCss(oPicture,'left'))) + 'px');
setAssign(oShadow[0],'height',parseInt(getCss(oPicture,'height')) + 'px');
setAssign(oShadow[0],'left','left')) + 'px')
setAssign(oShadow[0],'top','top')) + 'px')

//右侧阴影区
setAssign(oShadow[2],(parseInt(getCss(oPicture,'width')) - parseInt(getCss(oTailor,'width')) - parseInt(getCss(oShadow[0],'width'))) + 'px');
setAssign(oShadow[2],'height')) + 'px');
setAssign(oShadow[2],'left')) + parseInt(getCss(oTailor,'top')) + 'px');

// 上侧阴影区
setAssign(oShadow[1],parseInt(getCss(oTailor,'width')) + 'px');
setAssign(oShadow[1],'top')) - parseInt(getCss(oPicture,'top'))) + 'px');
setAssign(oShadow[1],'left')) + parseInt(getCss(oShadow[0],'width'))) + 'px');
setAssign(oShadow[1],'top')) + 'px');

// 下侧阴影区
setAssign(oShadow[3],'width')) + 'px');
setAssign(oShadow[3],'height')) - parseInt(getCss(oTailor,'height')) - parseInt(getCss(oShadow[1],'height'))) + 'px');
setAssign(oShadow[3],'left' )) + parseInt(getCss(oShadow[0],'width'))) + 'px');
setAssign(oShadow[3],'top' )) + parseInt(getCss(oTailor,'height'))) + 'px');
}

注意在网页实际运用中,如果布局中图片css中没有left或top属性,那么上面代码会产生错误。应该使用offsetLeft与offsetTop代替之。

添加裁剪边框

在放出的布局图中,可以看见裁剪的边沿,四角及四边各有一个小正方形的形状,添加不仅是为了区分裁剪区与非裁剪区,还为下一步添加拉伸裁剪区域提供方便。下面开始编写代码

/ 点状边沿绘制 /
// 四角点状边沿绘制
for (var i = 0; i < 4; i++) {

// 点状绘制
setAssign(oHandle[i],'absolute');
setAssign(oHandle[i],'5px');
setAssign(oHandle[i],'5px');

// 0 2 表示左侧点状
if(i % 2 == 0) {
  setAssign(oHandle[i],'0px');

  setAssign(oHandle[i],(i == 0?'0px' : (parseInt(getCss(oTailor,'height')) - 8) + 'px'));      

} else {
  // 右侧点状
  setAssign(oHandle[i],( parseInt(getCss(oTailor,'width')) - 6 ) + 'px');

  setAssign(oHandle[i],(i == 1?'0px' : parseInt(getCss(oTailor,'height')) - 8 ) + 'px');          
}                              

}

// 四边点状边框
for (var i = 4; i < oHandle.length; i++) {
setAssign(oHandle[i],'5px');

// 4 6 表示上 下 点状边框
if(i % 2 == 0) {

  setAssign(oHandle[i],'width')) / 2 + 'px');

  setAssign(oHandle[i],(i == 4 ? '0px' : (parseInt(getCss(oTailor,'height')) - 8) + 'px'));

} else {

  // 左右点状
  setAssign(oHandle[i],'height')) / 2 + 'px');

  setAssign(oHandle[i],(i == 5 ? '0px' : parseInt(getCss(oTailor,'width')) - 8 ) + 'px');

}

}
}

布局中,裁剪区域类名为Box_handle的div前四个代表四角的点状,后四个表示边沿中间的点状,都按照顺时针分布。完成后效果如下:

监视阴影区域

裁剪区域与阴影区域绘制完成,现在添加一个小功能,当鼠标点击到非裁剪区时(即阴影区),取消裁剪区域。

oTailor.style.display = 'none'; oTailor.style.width = '0px'; oTailor.style.hegiht = '0px'; for (var i = 0; i < oShadow.length; i++) { oShadow[i].style.display = 'none'; oShadow[i].style.left = '0px'; oShadow[i].style.top = '0px'; }

}
}

监视鼠标移动位置

接下来添加裁剪区域拉伸的功能,当鼠标移动到边沿的点状边框时呈现不同的效果

添加鼠标显示效果

Box_1': // 左上
  setAssign(oTailor,'cursor','nw-resize');        

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_2':              // 右上 

  setAssign(oTailor,'ne-resize');        

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_3':              // 左下

  setAssign(oTailor,'sw-resize');

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_4':              // 右下
  setAssign(oTailor,'se-resize');

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_5':              // 上      
  setAssign(oTailor,'n-resize');

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_6':              // 左
  setAssign(oTailor,'w-resize');

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_7':              // 下      
  setAssign(oTailor,'s-resize');

  break;
case '<a href="https://www.jb51.cc/tag/Box/" target="_blank" class="keywords">Box</a>_8':              // 右      
  setAssign(oTailor,'e-resize');

  break;
default :                // 裁剪区域 <a href="https://www.jb51.cc/tag/xianshi/" target="_blank" class="keywords">显示</a>可移动<a href="https://www.jb51.cc/tag/tishi/" target="_blank" class="keywords">提示</a>
  setAssign(oTailor,'move');
  break;

}
}

由于监视的div较多,因此采用事件委托的方式添加效果不方便演示,有兴趣的同学可以自己测试,

添加拉伸效果

代码

获取cursor状态 var oCur = getCss(oTailor,'cursor'); // 鼠标初始位置 var sTmpX = oEvent.clientX; var sTmpY = oEvent.clientY;

// 获取裁剪区域的属性 用一个对象保存起来方便调用
oAttrs.left = getCss(oTailor,'left');
oAttrs.top = getCss(oTailor,'top');
oAttrs.width = getCss(oTailor,'width');
oAttrs.height = getCss(oTailor,'height');

document.onmousemove = function(ev) {
// 移动事件对象
var oEvent = ev || window.event;
// 当前鼠标位置减去初始鼠标位置 等于 鼠标移动距离
var sLeftT = oEvent.clientX - sTmpX;
var sTopT = oEvent.clientY - sTmpY ;

// 表示鼠标移动的距离
var oTmpHeight = '';
var oTmpTop = '';
var oTmpWidth = '';
var oTmpLeft = '';

switch(oCur) {
  case 'nw-resize' :       // 左上

    oTmpWidth = parseInt(oAttrs.width) - sLeftT ; 
    oTmpHeight = parseInt(oAttrs.height) - sTopT ; 
    oTmpLeft = parseInt(oAttrs.left) + sLeftT ; 
    oTmpTop = parseInt(oAttrs.top) + sTopT ;                   

    break;
  case 'ne-resize' :       // 右上
    // 此时width不能减去鼠标移动距离 因为此时移动距离为正值
    oTmpWidth = parseInt(oAttrs.width) + sLeftT ; 
    oTmpHeight = parseInt(oAttrs.height) - sTopT ; 
    // 右上角移动不需要left值 因为默认响右移动
    oTmpTop = parseInt(oAttrs.top) + sTopT ;                                     

    break;
  case 'sw-resize' :       // 左下
    // 同右上 height 必须是<a href="https://www.jb51.cc/tag/jiashang/" target="_blank" class="keywords">加上</a>鼠标移动距离
    oTmpWidth = parseInt(oAttrs.width) - sLeftT ; 
    oTmpHeight = parseInt(oAttrs.height) + sTopT ; 
    oTmpLeft = parseInt(oAttrs.left) + sLeftT ;                   

    break;
  case 'se-resize' :       // 右下
    // 左下与右上的结合 同时<a href="https://www.jb51.cc/tag/quchu/" target="_blank" class="keywords">去除</a>left与top
    oTmpWidth = parseInt(oAttrs.width) + sLeftT ; 
    oTmpHeight = parseInt(oAttrs.height) + sTopT ;           

    break;
  case 'n-resize' :       // 上

    oTmpHeight = parseInt(oAttrs.height) - sTopT;
    oTmpTop = parseInt(oAttrs.top) + sTopT;        

    break;
  case 'w-resize' :       // 左

    oTmpWidth = parseInt(oAttrs.width) - sLeftT ;
    oTmpLeft = parseInt(oAttrs.left) + sLeftT;   

    break;
  case 's-resize' :     // 下

    oTmpHeight = parseInt(oAttrs.height) + sTopT;

    break;
  case 'e-resize' :       // 右

    var oTmpWidth = parseInt(oAttrs.width) + sLeftT;

    break;
  default :                      
    // 否则是移动裁剪区域
    tailorMove(oEvent,oPicture,oShadow);

    break;
}  



// 向上拉到边界
if(parseInt(getCss(oTailor,'top')) <= oPicture.offsetTop) {
  oTmpHeight = parseInt(getCss(oPicture,'height')) - (oPicture.offsetTop+parseInt(getCss(oPicture,'height'))-parseInt(getCss(oTailor,'top'))-parseInt(getCss(oTailor,'height')));
  oTmpTop = oPicture.offsetTop;
}else if(oPicture.offsetTop+parseInt(getCss(oPicture,'height')) <= (parseInt(getCss(oTailor,'top'))+parseInt(getCss(oTailor,'height')))){
  // 向下拉到边界
  oTmpHeight = oPicture.offsetTop+parseInt(getCss(oPicture,'top'));
}
// 向左拉到边界
if((parseInt(getCss(oTailor,'left'))) <= oPicture.offsetLeft) {
  oTmpWidth = parseInt(getCss(oPicture,'width')) - (oPicture.offsetLeft+parseInt(getCss(oPicture),'width')-parseInt(getCss(oTailor,'left'))-parseInt(getCss(oTailor,'width')))
  oTmpLeft = oPicture.offsetLeft;
} else if(parseInt(getCss(oTailor,'width')) + parseInt(getCss(oTailor,'left')) >= (oPicture.offsetLeft+oPicture.offsetWidth)) {
  // 向右拉到边界    
  oTmpWidth = oPicture.offsetLeft+oPicture.offsetWidth - parseInt(getCss(oTailor,'left'));            
}

// 赋值
if(oTmpWidth){
  setAssign(oTailor,oTmpWidth + 'px');
}
if(oTmpHeight) {
  setAssign(oTailor,oTmpHeight + 'px');
}
if (oTmpLeft) {
  setAssign(oTailor,oTmpLeft + 'px');
}
if (oTmpTop) {
  setAssign(oTailor,oTmpTop + 'px');        
}      

// 阴影区域绘制
shadow(oPicture,oTailor);

};

// 当松开鼠标时注意取消移动事件
document.onmouseup = function(ev) {
// event事件对象
var oEvent = ev || window.event;

document.onmousemove = null;      
oEvent.preventDefault();

}

oEvent.preventDefault();
};

拉伸时注意移动距离的计算,特别是向上及向左移动时,要注意同时改变裁剪区域的left、top值,否则它只会向下、向右增大。来具体说一下如何计算:

原理

以鼠标向左上角拉伸为例,鼠标的移动距离与上面所讲的一致,但此时注意计算出的值是一个负数,所以在计算裁剪区域的增加值时,要用原裁剪区的宽度或高度减去该值,同时,增加多少宽度,裁剪区的左偏移值就要减去多少,否则显示效果是裁剪区域向右增大,如下图:

上图中,绿色区域是拉伸时增加宽、高后的裁剪区域,如果没进行偏移调整后的效果既是这样,黄色区域是进行偏移跳转后的裁剪区域,两个的叠加区就是原来的裁剪区了。

这是左上角拉伸,左下角拉伸即其他与之类似,可依照向上套。

而另一关键,拉伸越界在上面已经说过,就不再叙述了。

裁剪区域的移动

现在来说最后一个功能,裁剪区域的移动。当鼠标移动到裁剪区域内部时,就会触发移动事件,此时可以移动裁剪区域,代码如下:

var oTmpx = oEvent.clientX - oTailor.offsetLeft;
var oTmpy = oEvent.clientY - oTailor.offsetTop;

document.onmousemove = function(ev) {
var oEvent = ev || window.event;

oLeft = oEvent.clientX - oTmpx;
oTop = oEvent.clientY - oTmpy;  


if(oLeft < oPicture.offsetLeft ) {
  oLeft = oPicture.offsetLeft ;  
} else if(oLeft > (oPicture.offsetLeft + oPicture.offsetWidth - oTailor.offsetWidth)) {
  oLeft = oPicture.offsetLeft + oPicture.offsetWidth - oTailor.offsetWidth;
}      
if(oTop < oPicture.offsetTop) {
  oTop = oPicture.offsetTop;  
} else if (oTop > (oPicture.offsetTop + oPicture.offsetHeight - oTailor.offsetHeight)) {
  oTop = oPicture.offsetTop + oPicture.offsetHeight - oTailor.offsetHeight;
}        

oTailor.style.left = ( oLeft)+ 'px';
oTailor.style.top = (oTop) + 'px';    
shadow(oPicture,oShadow);

}
}

获取裁剪的位置

裁剪效果功能基本完成,那么就要获取裁剪的位置,首先要知道需要获取那些属性。根据PHPGD库操作,进行图片裁剪需要知道,裁剪的起点坐标以及裁剪的宽高。我用一个函数获取这些数据,并将其封装后返回:

Box').get(0);

oAttrs.LeftX = (parseInt(getCss(oTailor,'left')) - oPicture.offsetLeft);
oAttrs.LeftY = (parseInt(getCss(oTailor,'top')) - oPicture.offsetTop);
oAttrs.Twidth = (parseInt(getCss(oTailor,'width')));
oAttrs.Theight = (parseInt(getCss(oTailor,'height')));
return oAttrs;
}

还有一个问题,如果网页上的图片是使用css压缩后的图片,那么在此获得的位置与裁剪大小会与你想像的有区别,可能裁剪后的图片范围会变大(原图较大),也有可能会变小(原图较小)。

如果能够获得原图的大小,可以根据压缩图与原图的比例来进行裁剪,这样可以获得正确的裁剪图。

好了,一个简单的图片裁剪功能就完成了,可以利用ajax传递到后台进行处理了。

本文内容到此就结束了,有问题的话欢迎大家留言讨论,希望本文对大家学习javascript有所帮助。

原文链接:https://www.f2er.com/js/46928.html

猜你在找的JavaScript相关文章