在网站中需要一个图片上传裁剪的功能,借鉴这篇文章 ajax+php+jquery图片截图上传 的指点,找到了jquery.imgareaselect这个不错插件,能对图片进行自定义区域选择并给出坐标,类似微博等同类网站上传头像要求按比例裁剪的功能,正合适就自己做了个。
文件上传类经常使用到一个叫uploadify的插件,上面提到的文章也结合了uploadify插件的使用,只是现在版本的uploadify一些配置和方法已经改变了(3.2.1版本)。包括jquery.imgareaselect的一些api也有新的变化,在做的过程中通过查询文档才确认了一些功能和配置的用法。
下面是两个插件的地址
uploadify: http://www.uploadify.com/ ( 有 uploadifive(html5) 版本和 flash 版本,顾及不支持html5的浏览器,目前建议选择flash版本 )
jquery.imgareaselect: http://odyniec.net/projects/imgareaselect ( 需先有 jquery )
具体的流程都相似,客户端uploadify替代表单接收原图,异步上传到服务器,服务器端接收后存储并返回图片文件名,取得文件名后可在浏览器显示出原图,这时在图片元素上配置imgareaselect使其可划定裁剪区域,划定结束后坐标传回服务器,php脚本接收换算后的坐标对原图进行裁剪。上面提到的文章中有详细的流程图,作者非常细心。
下面说说我写的过程
上细节:首先是前端的配置
demo.php:
[javascript] view plaincopy
$field = $(input[type='file']); $field.uploadify({//配置uploadify 'buttontext': '选择图片', //选择按钮显示的字符 'swf' : '/uploadify/uploadify.swf', //swf文件的位置 'uploader' : '/receivepic.php', //上传的接收者 'cancelimg' : '/uploadify/uploadify-cancel.png', 'folder' : '/picture',//上传图片的存放地址 'auto' : false, //选择图片后是否自动上传 'multi' : false, //是否允许同时选择多个(false一次只允许选中一张图片) 'method' : 'post', 'queuesizelimit' : 1,//最多能选择加入的文件数量 'filetypeexts': '*.gif; *.jpg; *.png', //允许的后缀 'filetypedesc': 'image files', //允许的格式,详见文档 'onselect': function(file) {//选择文件后的触发事件 $(a.xtpicsubmit).show().click(function(){//自定义的按钮,显示点击执行上传 $field.uploadify('upload','*');//此处触发上传 }); $wrap.find(p.picinfo span).text(file.name);//file.name为选中的图片名称 }, 'onuploadsuccess' : function(file, data, response) { //上传成功后的触发事件 $field.uploadify('disable', true); //(上传成功后)'disable'禁止再选择图片 data = json.parse(data); //data为接收方(receivepic.php)返回的数据,稍后描述 //此时开始对取回的数据处理出需要的图片名,宽高,并计算出原图比例尺,开始设定裁剪需要的计算量 var orignw = data.width,//存储原图的宽高,用于计算 orignh = data.height, aspectratio = json.parse(picformat)[index].width/json.parse(picformat)[index].height,//提前设定的裁剪宽高比,规定随后裁剪的宽高比例 framew = 260, //原图的缩略图固定宽度,作为一个画布,限定宽度,高度自适应,保证了原图比例 frameh = 0, prevframew = 140, //预览图容器的高宽,宽度固定,高为需要裁剪的宽高比决定 prevframeh = 140/aspectratio, rangex = 1, //初始缩放比例 rangey = 1, previmgw = prevframew, //初始裁剪预览图宽高 previmgh = prevframew; $imgtar = $wrap.find(img.pic), //画布 $imgcut = $cut.find(img.cutimg);//预览图 $imgtar.attr(src,/picture/+data.filename);//显示已上传的图片,此时图片已在服务器上 frameh = math.round(framew*orignh/orignw);//根据原图宽高比和画布固定宽计算画布高,即$imgtar加载上传图后的高。此处不能简单用.height()获取,有dom加载的延迟 $cut.find(.preview).css('height',math.round(prevframeh)+px);//设置裁剪后的预览图的容器高,注意此时的高度应由裁剪宽高比决定,而非原图宽高比 //准备存放图片数据的变量,便于传回裁剪坐标 cutjson.name = data.filename; cutjson.position = {}; //准备好数据后,开始配置imgareaselect使得图片可选区 var imgarea = $imgtar.imgareaselect({ //配置imgareaselect instance: true, //配置为一个实例,使得绑定的imgareaselect对象可通过imgarea来设置 handles: true, //选区样式,四边上8个方框,设为corners 4个 fadespeed: 300, //选区阴影建立和消失的渐变 aspectratio:'1:'+(1/aspectratio), //比例尺 onselectchange: function(img,selection){//选区改变时的触发事件 /*selection包括x1,y1,x2,y2,width,height几个量,分别为选区的偏移和高宽。*/ rangex = selection.width/framew; //依据选取高宽和画布高宽换算出缩放比例 rangey = selection.height/frameh; previmgw = prevframew/rangex; //根据缩放比例和预览图容器高宽得出预览图的高宽 previmgh = prevframeh/rangey; //实时调整预览图预览裁剪后效果,可参见http://odyniec.net/projects/imgareaselect/ 的live example $imgcut.css({ 'width' : math.round(previmgw)+px, 'height' : math.round(previmgh)+px, 'margin-left':-+math.round((prevframew/selection.width)*selection.x1)+px, 'margin-top' :-+math.round((prevframeh/selection.height)*selection.y1)+px }); }, onselectend: function(img,selection){//放开选区后的触发事件 //计算实际对于原图的裁剪坐标 cutjson.position.x1 = math.round(orignw*selection.x1/framew); cutjson.position.y1 = math.round(orignh*selection.y1/frameh); cutjson.position.width = math.round(rangex*orignw); cutjson.position.height = math.round(rangey*orignh); } }); } });
此时已经取得了裁剪的坐标,只需将坐标传回服务器交给脚本处理
使用jquery的ajax方法回传数据:
[javascript] view plaincopy
$(a.getcut).click(function(){ $.ajax({ type: post, url : cutpic.php, data: { name:data.filename,position:json.stringify(cutjson.position) }, success: function(data){ $imgtar.attr('src',/picture/cut/+data); //裁剪成功传回生成的新图文件名,将结果图显示到页面 } }); });
至此前端的处理就基本完成了(代码省去了一些状态显示,元素变化的处理细节),下面是后端的两个脚本:
首先是接收上传图片的脚本,接收并存储图片后返回图片数据:
receivepic.php
[php] view plaincopy
//上传文件处理代码与往常相同,此处省去.. $arr = getimagesize('/picture/'.$file['name']); $strarr = explode(\,$arr[3]);//分析图片宽高 $data = array( 'filename'=>$file['name'], 'width'=>$strarr[1], 'height'=>$strarr[3] ); echo json_encode($data);
接下来是裁剪,其中也用到了piphp_imagecrop裁剪函数cutpic.php:
[php] view plaincopy
function piphp_imagecrop($image, $x, $y, $w, $h){ $tw = imagesx($image); $th = imagesy($image); if ($x > $tw || $y > $th || $w > $tw || $h > $th) return false; $temp = imagecreatetruecolor($w, $h); imagecopyresampled($temp, $image, 0, 0, $x, $y, $w, $h, $w, $h); return $temp; } $pic = '/picture/'.$_post['name']; $cutposition = json_decode($_post['position']); //取得上传的数据 $x1 = $cutposition->x1; $y1 = $cutposition->y1; $width = $cutposition->width; $height = $cutposition->height; $type=exif_imagetype($pic); //判断文件类型 $support_type=array(imagetype_jpeg , imagetype_png , imagetype_gif); if(!in_array($type, $support_type,true)) { echo this type of image does not support! only support jpg , gif or png; exit(); } switch($type) { case imagetype_jpeg : $image = imagecreatefromjpeg($pic); break; case imagetype_png : $image = imagecreatefrompng($pic); break; case imagetype_gif : $image = imagecreatefromgif($pic); break; default: echo load image error!; exit(); } $copy = piphp_imagecrop($image, $x1, $y1, $width, $height);//裁剪 $targetpic = '/picture/cut/'.$_post['name']; imagejpeg($copy, $targetpic); //输出新图 @unlink($pic);//删除原图节省空间 echo $_post['pic'].'?'.time(); //返回新图地址
整个过程就完成了。上个效果图:
开始的部分很多参考了ajax+php+jquery图片截图上传 此篇详细的文章,这里再次说明。
后端的交互很轻松,任务主要在于前端两个插件之间的搭建和使用,我的代码写的比较仓促,应该需要更加结构化一点。另外上传文件的各方面控制也是一个风险(类型,大小等),值得更多的考虑。
http://blog.csdn.net/as66t/article/details/11688217
----------------------------------------------------------------------------------------------------------------------
一、功能分析
用户直接上传图片,点击上传按钮之后,在图片预览图内可预览图片,然后进行图片的裁剪前预览,当点击裁剪按钮时确定裁剪图片,并在裁剪结果区域显示裁剪后的效果。
(说明:我是将上传文件保存在/uploads文件夹中,而截图结果放在/avatar文件夹里)
实现效果预览:
二、解决方案
1、插件的选择
jquery:这个是必备的一个插件可以到官网上下载
http://docs.jquery.com/downloading_jquery
imgaresselect:这个是实现客户端上图片区域选择的
http://odyniec.net/projects/imgareaselect/
uploadify:实现文件的上传的功能,支持多文件上传,且可定制性非常强。
http://www.uploadify.com/download/
上面的插件是用在客户端上,其实在我这个程序里写php时也用了一些插件。其实我之所以写图像剪裁上传的起源是因为我看了《php快速开发工具箱》想自己练习一下的。该书是有一个网址(http://www.pluginphp.com/),里面有整本书的代码,而且每个插件都相应的demo,非常不错。下面是用到的php插件:
piphp_uploadfile.php:这是一个文件上传功能的php文件
http://www.pluginphp.com/plug-in11.php
piphp_imagecrop.php:这个php文件是具有对图片进行裁剪的功能
http://www.pluginphp.com/plug-in15.php
2、客户端与服务器之间的交互图
为了便于理解,我先把交互图放在这里。其中绿色部分是客户端的主要步骤、粉红色是服务器端的主要步骤,服务器与客户端之间的交互通过ajax完成。可以发现,大部分的操作在客户端进行,服务器端与客户端之间的交流只是简单的json数据,因此这样给用户的体验是非常高的。
截图 1 客户端与服务器之间交互图
3、客户端文件
展示给用户的是html页面,为了学习并巩固css知识,就和div+css搭建了下面这样一个前台页面,见截图 2。
截图 2 前台页面
跟客户端有关的文件主要是一个index.html,而在这个文件里面会引用其他的插件文件,因此可以说,客户端方面只有一个html文件。
另外,由于这里主要讨论客户端与浏览器之间的交互,因此略过css方面的内容。这里只列出html的代码,首先是部分:
图片剪切上传
可以看得出来主要是引用一些插件的文件。上面的文件(包括css文件与js文件)都可以从我给的链接里下载到,只是样式表layout.css是我自己写的样式表,大家可以根据自己的css知识写出。
接下来是body部分,也许这么看代码比较乱,推荐使用一些带有高亮显示的工具来查看这些代码,比如dreamweaver等ide,实在不行,也可以用火狐的查看源代码来看。(火狐不仅是一个好浏览器,更是一个极棒的调试器!)
为了方便起见,我去掉诸如导航条、页脚版权声明等点缀部分,只给出必要的html代码。
图片上传
上传图片
图片预览
裁剪结果:
裁剪预览:
div id=preview>
上面我用颜色区分开主要div区,这三块分别代表上传图片区、大图展示区、截图结果区与选择预览区。其中三个粗体部分是带有id属性的空div,用来放图片用的。(到时时候动态加载图片到这些div中),因此这段代码形成的html框架如截图 2所示。(蓝色线条是block元素边界,此效果较是由火狐的插件制作而成):
截图 3 页面大体框架
基本的准备工作已经完成,待会儿再继续在这个框架上添加代码。咱们先介绍一下服务器上的php是怎么个情况。
4、服务器文件
服务器上主要用到两个php文件,一个用来处理上传图片的process.php,另外一个则是处理图片截图用的crop.php。不过,process.php文件包括插件piphp_uploadfile.php,而crop.php中包括piphp_imagecrop.php插件。(这些插件的地址我在上面已经给出了)
=======
process.php主要接收上传图片,设置限制(比如文件的大小与格式),处理一些上传错误等,最后返回给客户端json,里面包含了所上传文件的一些信息(比如路径、大小等);当在客户端点击上传按钮的时候,会用异步(ajax)的方式调用这个php文件。
=======
crop.php主要负责真正裁剪上传的图片,当在客户端返回裁剪的位置后(点击裁剪按钮后),以异步方式将数据以json的方式传递给服务器,crop.php真正裁剪图片后,将图片另存到网络的目录下,同时返回此图片的存储路径,然后再让客户端显示图片即可
三、用到的技术摘要
现在根据上面的交互图继续完善代码。因此我这节会交叉地完善html、js与php代码,并不会单独分开完善,这样在逻辑上会更好理解。
声明:新增的代码部分用粗体表示
1、uploadify上传
…
type=text/javascript src=/uploadify/jquery.uploadify.v2.1.4.min.js>
$(document).ready(function(){
//uploadify设置
$('#pic_upload').uploadify({
'uploader' : '/uploadify/uploadify.swf',
'script' : 'process.php',
'cancelimg' : '/uploadify/cancel.png',
});
}
…
上面的代码只是uploadify这一个插件的配置项而已。为了增强用户体验可以详细配置其他选项,这参考这个插件的官方文档:http://www.uploadify.com/documentation/。上面的'script'选项就是选择服务器的处理脚本,我们这里就使用process.php了。上传文件到服务器后会让服务器自动调用这个程序。那么客户端怎么知道服务器的process.php调用完了呢?如何获取process.php反馈回来的信息呢?——其实uploadify它提供了一个触发选项oncomplete,就是用来处理服务器的反馈信息的,我们稍后再写这个选项的内容,先看看process.php是返回哪些内容的呢。
2、process.php反馈上传信息
process.php的任务就是给浏览器返回json数据(至于什么是json请参考其它教程,把json想像成键/值对就可以了,它很方便数据的传输与读取)。在php里,一般是先把数据整理成数组的形式,然后使用json_encode()把数据转换成json。那process.php应该给浏览器返回什么样的数据呢?
文件是否成功上传 -> message
文件的上传状态代码 -> code
文件上传的存放路径 -> path
图片的宽度 -> width
图片的高度 -> height
图片的缩放比例 -> scale
图片的名称 -> name
其中之所以设置图片的缩放比例scale,是因为如果用户上传的图片尺寸太大(比如800x800),浏览器中的div会被撑开,布局会被打乱。因此我们限定在浏览器显示图片的时候任何一边长不能超过400px,否则在显示的时候以等比例缩放。(比如上面的800x800的图上会显示成400x400的,然后浏览器同时设置scale为0.5)。
另外,这个php文件是调用了piphp_uploadfile.php这个插件,用来将上传的文件进行鉴别与搬移。
下面是process.php的程序:
'未知上传错误',
'path'=>'',
'code'=>-4, //上传结果代码,0表示成功,-1表示失败
'width'=>100,
'height'=>100,
'scale'=>1, //比例尺
'name'=>''
);
if (!empty($_files))
{
$name='picture';
$uploadfile='uploads/';
$maxlen=9*1024*1024;
$result=piphp_uploadfile($name,$uploadfile,$maxlen);
$response['code']=$result[0];
//简单汇报成功情况
if($result[0]==0)
{
$response['message']='上传成功!';
//$response['message']=$result[2];
$response['path']=$result[1];
$response['name']=$result[2];
//获取图像的高度与宽度
$filename=iconv(utf-8,gb2312,$result[2]);
list($width,$height)=getimagesize($_server['document_root'].$uploadfile.$filename);
$response['width']=$width;
$response['height']=$height;
}
else
{
switch($result[0])
{
case -1: $response['message']=上传失败; break;
case -2: $response['message']=文件类型错误;break;
case -3: $response['message']=文件大小超过限制;break;
default: $response['message']=错误代码:$result[0];
}
}
}
else{
$response['message']=上传文件出现错误!.
;
}
$json_str=json_encode($response);
echo $json_str;
?>
其实这个程序因为有了if判断语句而显示有点大,其实逻辑还是挺简单的。无论如何,这个程序返回的我上面说的有关图片的上传信息(放在$json_str这变量里了)
3、继续改进uplodify的配置
从上面知道,process.php返回的是一个$json_str变量,它里面有图像的路径,这样我们就可以在浏览器中显示图片啦!(注意此时显示的图片已经是在服务器上了)
现在添加uploadify的'oncomplete'选项,它告诉浏览器当process.php返回数据时应该怎么做。
$('#pic_upload').uploadify({
'uploader' : '/uploadify/uploadify.swf',
'script' : 'process.php',
'cancelimg' : '/uploadify/cancel.png',
'oncomplete': function(event, id, fileobj, response, data) {
json_str=json.parse(response);
var maxsize=400;
var width=json_str.width;
var height=json_str.height;
var scale=json_str.scale;
if(json_str.code == 0)
{
$('#uploadinfo').html(json_str.message+'
平均上传速度:'+ data.speed.tofixed(2) + 'kb/s');
//对图像进行缩放
if(json_str.width > maxsize || json_str.height >maxsize){
if( json_str.width > json_str.height)
{
width = maxsize;
height = maxsize / json_str.width * json_str.height;
json_str.scale = maxsize / json_str.width;
}
else
{
height = maxsize;
width = maxsize / json_str.height * json_str.width;
json_str.scale = maxsize / json_str.height;
}
}
$('#oriimage').html('');
//同时插入预览图
$('#preview').empty().html('').css({
overflow:'hidden',
width:'150px',
height:'150px'
});
}
else{
$('#uploadinfo').html('错误代码['+json_str.code+']:'+json_str.message+'
');
}
},
});
…
这里的程序主要做两件事,首先(第一种颜色标志处)显示上传的图,不过如果图片太大的话就应该显示缩放后的图,同时将缩放的比例保存到scale变量中;然后(第二处颜色标志处)再初始化裁剪预览图(用jquery方法),注意这里只是初始化并没有动态显示裁剪预览图,动态显示部分要用imgareaselection这个插件完成。下面就开始讲这个插件吧
4、用imgareaselection获取截图点坐标
关于imgareaselection的使用说明请到官方上查看,这里就不再细讲。
由于图片是动态加载的,所以不能事先将这个插件应用到图像上。我们可以设置一个按钮,当图片上传显示后,我们点击这个加载裁剪框按钮,将这个插件绑定到图像上。所以我们先在html上添加一个按钮,我是加载那个图像预览的div里:
图片预览
加载裁剪框
$(document).ready(function(){
$('#pic_upload').uploadify({
…
});
//加载裁剪框
$('#initcrop').click(function(e){
ias=$('#oriimage img').imgareaselect({instance:true});
ias.setoptions({ aspectratio: '1:1', handle:true,
hide:false,
onselectchange:preview2,
onselectend: function (img, selection) {
json_str.x1=selection.x1;
json_str.y1=selection.y1;
json_str.cropwidth=selection.x2-selection.x1;
json_str.cropheight=selection.y2-selection.y1;
},
});
});
}
这里的onselectchange选项就是当改变裁剪框时所要调用的函数,这里使用preview2函数名作为值,这个函数我是另外写在下面的,当然你也可以使用匿名函数的。我是为了强调这个函数,所以写成实名函数:
//图像预览函数
function preview2(img, selection) {
realwidth=json_str.width * json_str.scale;
realheight=json_str.height * json_str.scale;
sizewidth=150;sizeheight=150;
var scalex = sizewidth / selection.width ;
var scaley = sizeheight / selection.height ;
$('#preview img').css({
width: math.round(scalex * realwidth) + 'px',
height: math.round(scaley * realheight) + 'px',
marginleft: - math.round(scalex * selection.x1) + 'px',
margintop: -math.round(scaley * selection.y1) + 'px'
});
};
这个函数的功能是实现动态显示截图区域图像的,这个区域大小是150x150像素的一个小框,这里它动态加载css,注意要跟上一节中的uploadify中oncomplete中预加载此截图框要联系起来。那里它是这么设置的:
//同时插入预览图
$('#preview').empty().html('').css({
overflow:'hidden',
width:'150px',
height:'150px'
});
}
注意overflow:hidden的含义是将图像里超过这150x150像素的图像隐藏起来。其实这种方法借鉴自:http://odyniec.net/projects/imgareaselect/examples-callback.html
另外的这插件中的onselectend选项配置:当鼠标离开拖选框时,将此裁剪区域的左上角坐标与裁剪区的长、宽存储到json_str变量中,到时候传送给crop.php函数。
5、将json_str数据传送给crop.php
使用jquery中的ajax()方法将json_str变量传送给服务器。先在html中添加一个裁剪按钮:
这里我使用了元素,其实没有必要,可以是任何元素,因为我们使用的是强大的jquery的ajax()方法。
当用户确定要裁剪时,按下此按钮就会调用ajax()方法。我们将处理程序写在head部分的
//加载裁剪框
$('#initcrop').click(function(e){
…
});
//裁剪动作,将数据传给服务器,同时ajax返回图片
$(#crop).click(function(e){
if(!(typeof json_str == 'undefined'))
