裁剪图片用的是PHP原生的方法,首先将文件上传到后台,再利用jcrop在前台裁剪图片,将裁剪的矩形的四个点的坐标传到后台,后台根据接收到的参数利用imagecopyresampled函数对上传的图片进行裁剪。
我的界面设计效果大概如下:
首先是点击选择图片按钮弹出file对话框,在选定文件后触发文件选择框的change事件,此时用ajaxFileUpload插件(异步上传文件)先将图片上传到后台。
//选择完文件后预览
$('#upload-file').on("change",function(){
ajax_file_upload();
});
function ajax_file_upload(){
if(jcrop_api != null){
jcrop_api.destroy();
}
$.ajaxFileUpload({
url: '../ajax/upload_img', //用于文件上传的服务器端请求地址
secureuri: false, //是否需要安全协议,一般设置为false
fileElementId: 'upload-file', //文件上传域的ID
dataType: 'json', //返回值类型 一般设置为json
success: function (data, status) //服务器成功响应处理函数
{
if(data.code == 1){
pic_name = data.name;
//将上传的图片显示到前台并开启裁剪插件
$("#crop-img").attr("src","../../resouces/pictures_temp/"+data.name);
$('#crop-img').Jcrop({
setSelect:[0,0,100,100],
aspectRatio: 1
},function(){
// Store the API in the jcrop_api variable
jcrop_api = this;
});
}else{
layer.msg('上传失败!',{
offset:"200px",
});
}
//重新绑定事件 事件失效原因未知
$('#upload-file').on("change",function(){
ajax_file_upload();
});
},
});
}
有时由于未知原因,在上传后会使file选择框的change事件失效,故若想修改之前上传的图片,得在上传方法执行成功后再次绑定一次change事件。
先附上upload_img方法,我的后台采用的是CI框架,故上传用的是CI框架封装好的方法:
//上传个人头像
public function upload_img(){
$id = $this->session->userdata('uid');
$config['upload_path'] = './resouces/pictures_temp/';
//如果目录不存在则创建
if(!file_exists($config['upload_path'])) {
mkdir($config['upload_path'],0700,true);
}
$config['encrypt_name'] = false;
$config['allowed_types'] = 'jpg|png';//文件类型
$config['max_size'] = '10240';
$config['file_name'] = $id.".jpg";
$file = $config['upload_path'].$config['file_name'];
if(file_exists($file)){
unlink($file);
}
$result['name'] = $config['file_name'];
$this->load->library('upload',$config);
if ($this->upload->do_upload('upload-file')) {
$result['code'] = 1;
$uploaddata = $this->upload->data();
$result['width'] = $uploaddata['image_width'];
$result['height'] = $uploaddata['image_height'];
}else{
$result['code'] = 2;
$result['error'] = $this->upload->display_errors();//输出错误原因
}
echo json_encode($result);
}
后台图片上传完后,前台对jcrop进行初始化,我这边设定了一个默认的100*100的裁剪框。效果如下:
此时可根据自身需求随意设定需要裁剪的位置和大小,当点击上传时,将获取裁剪矩形的4个点的坐标,将参数传递到后台:
//上传图片
$('#upload-img').on("click",function(){
//获取截取框4点的坐标
var x = jcrop_api.tellSelect().x;
var y = jcrop_api.tellSelect().y;
var width = jcrop_api.tellSelect().x2 - x;
var height = jcrop_api.tellSelect().y2 - y;
//将值传到php后台 php对图片进行处理
$.post('../ajax/crop_img', {
x:x,
y:y,
width:width,
height:height,
name:pic_name,
},function(data){
var imgStr = "../../resouces/pictures/"+data;
$('.show-area > img').attr("src",imgStr);
layer.msg('上传成功!',{
offset:"200px",
});
}, 'text');
});
由于jcrop提供的方法几乎都只能打印出矩形4个点的坐标,而imagecopyresampled这个方法需要裁剪后的图片的宽高,所以我在前台先做了一遍计算,再将值传递过去。
后台crop_img是我用来处理生成后的图片并入库的方法,它直接调用了get_crop_img()方法对图像进行裁剪:
//裁剪头像并入库
public function crop_img(){
$id = $this->session->userdata('uid');
$x = $this->input->get_post("x");
$y = $this->input->get_post("y");
$width = $this->input->get_post("width");
$height = $this->input->get_post("height");
$name = $this->input->get_post("name");
$filename = './resouces/pictures_temp/' . $name;
$newname = './resouces/pictures/' . $name;
get_crop_img($filename,$newname,$x,$y,$width,$height);
$conditions = array($name,$id);
$this->user_model->editPersonImgById($conditions);
echo $name;
}
//获取裁剪的头像
function get_crop_img($filename,$newname,$x,$y,$width,$height){
//如果文件存在则删除
if(file_exists($newname)){
chmod($newname,0755);
unlink($newname);
}
//获取原图的信息
$info = getImageInfo($filename);
//计算新生成的图片尺寸
$width = $width * ($info['width']/300);
$height = $height * ($info['height']/300);
$x = $x * ($info['width']/300);
$y = $y * ($info['height']/300);
$newimg = imagecreatetruecolor(100, 100); //创建新的图层
$img = imagecreatefromjpeg($filename); //把原本的图片读进来
imagecopyresampled($newimg, $img, 0, 0, $x, $y, 100, 100, $width, $height); //画图
imageinterlace($newimg,1); //隔行扫描
imagejpeg($newimg,$newname,100); //生成图片
imagedestroy($newimg);
imagedestroy($img);
}
getImageInfo()是随手写的一个获取当前图片部分信息的函数:
//获取图片的信息
function getImageInfo($img){
$imageInfo = getimagesize($img);
if( $imageInfo!== false) {
$imageType = strtolower(substr(image_type_to_extension($imageInfo[2]),1));
$info = array(
"width" =>$imageInfo[0],
"height" =>$imageInfo[1],
"type" =>$imageType,
"mime" =>$imageInfo['mime'],
);
return $info;
}else {
return false;
}
}
生成裁剪图片的流程大概如下:
1. 用imagecreatetruecolor($width,$height)方法生成一个新的空白图层,宽高由你自定义。
2. 用imagecreatefromjpeg($src)方法读取原本图片的资源,$src为图片的路径。返回的对象为一个图层对象。
3. 用imagecopyresampled ($dst_image , $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h )方法裁剪原图片,然后将新的图片画到之前创建的空白图层上。$dst_image即你裁剪后图片要画到哪个图层对象,$src_image即原图,$dst_x,$dst_y即为从新图层开始绘画的起始点坐标(即矩形左上角,因为我是新创建的图层,所以我是从(0,0)这个点开始画),int $src_x , $src_y 即为从原图上所截取图片的起始点(截图矩形的左上角点坐标)。dst_w,dst_h 即新图层上你所要画的图片的宽高,src_w,src_h 即为从原图上所截取图片的宽高。这里我对宽高都进行了一定运算,因为我前台是不限制上传图片的像素的,但固定按300*300显示,而jcrop获取的所有参数都是对300*300这个图层进行操作所获取到的,所以我要知道在原图上裁剪的比例,我必须对图片宽高和xy坐标重新计算一遍,即 (300像素图上裁剪的尺寸/300)*原图尺寸 = 原图裁剪的尺寸。
4. imagejpeg($newimg,$newname,$quality),$newimg即为刚刚绘画了的空白图层,$newname即为你要新生成的图片的路径地址(含名字),$quality即为生成图片的质量,默认为75,这里最好填100,不然生成的裁剪图片可能会有瑕疵。
5. imagedestroy($img)销毁图层,释放资源。
注:我为了简便,将上传的图片都做了处理,在后台都生成的是jpg文件,裁剪后的图片我也生成的是jpg文件,如果要生成png文件的话,php方法可能会有部分不同,但仅仅只是将适用与jpeg格式的方法换为适用于png格式的方法。
最后附上裁剪后的结果图: