这两天在想用户头像上传的问题,先试着用jQuery实现,贼麻烦,效果也不理想;后来用vue-cropper,但是没用过vue,根本不会导入组件;最后选了cropper,真香。
大致实现下面这个功能:
一看这功能就眼熟,这不和QQ编辑头像蛮像的吗?!
除去清明假期,我一口气做了两天才搞完。
这是我的最终实现效果:
下面上代码:
因为我用php写后端,所以就用了CMS框架,其实在写这个功能的时候并没有用到这个框架。
导入文件注意的点就在jquery.js一定要在js文件的最上面,以及一定要注意版本冲突问题。
我用的版本是:
Cropper v2.3.4
jQuery 3.*
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>编辑头像</title>
<link rel="stylesheet" href="{THEME_PATH}assets/layui/css/layui.css">
<link href="{HOME_THEME_PATH}web/css/register.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="{THEME_PATH}assets/global/plugins/cropper/css/cropper.css">
<!-- js(引用) -->
<script src="{THEME_PATH}assets/global/plugins/jquery.min.js" type="text/javascript"></script>
<script src="{THEME_PATH}assets/js/cms.js" type="text/javascript"></script>
<script src="{THEME_PATH}assets/layui/layui.js" type="text/javascript"></script>
<script src="{THEME_PATH}assets/global/plugins/cropper/js/cropper.js" type="text/javascript"></script>
</head>
body界面很简单
<body>
<div class="uploadPage">
<div class="photo1"></div>
<div class="photo2"></div>
<button class="uploadPhoto" type="file">
<span class="uploadPhotoText">上传头像</span>
</button>
<button class="savePhoto">
<span class="savePhotoText">保存修改</span>
</button>
</div>
</body>
css文件需要注意这个:
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
它可以改变剪裁框的形状
@charset "UTF-8";
.uploadPage {
width: 556px;
height: 425px;
}
.photo1 {
display: inline-block;
vertical-align: top;
width: 270px;
height: 270px;
margin-top: 41px;
margin-left: 40px;
background-color: rgba(0, 0, 0, 0.30);
position: relative;
overflow: hidden;
}
.photo2 {
display: inline-block;
vertical-align: top;
width: 145px;
height: 145px;
margin-top: 104px;
margin-left: 61px;
border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.30);
overflow: hidden;
}
.photo3 {
position: absolute;
cursor: move;
object-fit: contain;
}
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
.uploadPhoto {
margin-top: 30px;
margin-left: 40px;
margin-right: 196px;
display: inline-block;
vertical-align: top;
width: 136px;
height: 40px;
border: 1px solid #999999;
border-radius: 6px;
}
.uploadPhotoText {
width: 56px;
height: 20px;
font-size: 14px;
font-family: Source Han Sans CN, Source Han Sans CN-350;
font-weight: 350;
text-align: LEFT;
color: #999999;
line-height: 16px;
}
.savePhoto {
display: inline-block;
vertical-align: top;
width: 144px;
height: 40px;
background: #2fbef0;
border-radius: 6px;
border: none;
margin-top: 30px;
}
.savePhotoText {
width: 56px;
height: 20px;
font-size: 14px;
font-family: Source Han Sans CN, Source Han Sans CN-350;
font-weight: 350;
text-align: LEFT;
color: #ffffff;
line-height: 16px;
}
接下来上js代码:
我主要是用了layui的上传组件,在before方法里用了ajax来调用服务器一个专门用来上传头像的方法。(这点我还没想好怎么用更简单的方法来实现)
<script>
// 上传头像
layui.use('upload', function () {
var upload = layui.upload;
// 执行实例
var uploadInst = upload.render({
elem: '.uploadPhoto', // 绑定元素
url: 'index.php?s=Httpapi&c=upload&m=uploadPhoto', // 上传接口
auto: false,
bindAction: '.savePhoto',
choose: function (obj) {
//预读本地文件,如果是多文件,则会遍历。(不支持ie8/9)
obj.preview(function (index, file, result) {
//预览图片
$('.photo1').empty();
$('<img>').attr({
src: result,
alt: '网络不佳'
}).addClass("photo3").appendTo('.photo1');
//裁剪头像
var $image = $('.photo3');
$image.cropper({
//裁剪框的比例
aspectRatio: 1 / 1,
//视图的模式
viewMode: 3,
//被剪裁的图片可移动
dragMode: 'move',
//预览裁剪后的头像
preview: '.photo2',
//是否显示图片后的网格背景
background: false,
// 禁止移动裁剪框
cropBoxMovable: false,
// 禁止调整裁剪框大小
cropBoxResizable: false,
// 自动裁剪整个图片区域
autoCropArea: 1,
//设置裁剪框的样式
cropBoxWidth: 270,
cropBoxHeight: 270,
cropType: 'circle',
});
// 将 Cropper 实例存储在图片的 data 属性中
var cropper = $image.data('cropper');
//处理裁剪并上传图片的方法
function cropAndUpload() {
if (cropper) {
//获得裁剪框的canvas节点
var canvas = cropper.getCroppedCanvas();
//将canvas转换为图片的base64编码
var croppedImageUrl = canvas.toDataURL('image/jpeg', 0.4);
console.log(croppedImageUrl);
$.ajax({
type: "POST",
url: "index.php?s=Httpapi&c=upload&m=uploadBase64Photo", // 上传接口地址
data: {
base64Url: croppedImageUrl,
},
success: function (response) {
// 上传成功后的处理逻辑
console.log("上传成功", response);
},
error: function () {
// 上传失败后的处理逻辑
console.error('上传失败');
}
});
} else {
console.error('Cropper 实例未初始化或不存在!');
}
}
$('.savePhoto').on('click', cropAndUpload);
});
},
done: function (res) {
window.close();
},
error: function () {
// 请求异常后生成重新上传按钮
$('.uploadPhotoText').text("重新上传");
}
});
// 重载该实例,支持重载全部基础参数
uploadInst.reload({
accept: 'images', // 只允许上传图片
acceptMime: 'image/*', // 只筛选图片
size: 1024 * 10 // 限定大小
});
});
</script>
服务器PHP的代码,第一个是上传图片的代码,第二个就是专门上传头像的代码
<?php namespace Phpcmf\Controllers;
class upload extends \Phpcmf\Common
{
//上传照片接口
function uploadPhoto()
{
try {
// 配置设置
$max_photo_size = 10000000; // 上传不大于10MB的图片
$upload_required = true;
// 上传文件的存放路径
$upload_dir = 'upload_images/';
// 如果目录不存在,则创建
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true); // 最后一个参数为 true 表示如果目录树不存在,则递归创建
}
chmod($upload_dir, 0777); // 修改目录权限为最高权限(即所有用户都有读写执行权限)
// 检查上传文件
$err_msg = false;
do {
// 得到传输的文件数据
$file = $_FILES['file'];
if($file == null || $file == ""){
$err_msg = "没有传入图片";
}else{
//对文件名处理
$file['name'] = date('Y-m-d-H-i-s') . '.' . pathinfo($file['name'], PATHINFO_EXTENSION);
// 检查所有可能获取并出现的错误号
switch ($file['error']) {
case UPLOAD_ERR_INI_SIZE:
$err_msg = "上传文件的大小超过了php.ini的upload_max_file设置的值.";
break;
case UPLOAD_ERR_PARTIAL:
$err_msg = "只接收到部分内容,请再试一次";
break;
case UPLOAD_ERR_NO_FILE:
$err_msg = "上传时没有选择文件";
break;
case UPLOAD_ERR_FORM_SIZE:
$err_msg = "上传文件的大小超过特殊表单项MAX_FILE_SIZE设置的值";
break;
case UPLOAD_ERR_OK:
if ($file['size'] > $max_photo_size) {
$err_msg = "图片太大,图片不能超过 $max_photo_size 位";
} elseif (!in_array(
pathinfo($file['name'], PATHINFO_EXTENSION),
array('jpg', 'jpeg', 'gif', 'png')//定义允许上传的类型
)) {
$err_msg = "你需要上传JPG/JPEG/GIF/PNG的图片,请选择你要上传的图片";
}
break;
default:
$err_msg = "发生未知错误,请重新试一次";
break;
}
}
} while (0);
// 如果没有错误,移动文件到上传目录
$code = 1;
if (!$err_msg) {
if (!is_writable($upload_dir)) {
$err_msg = "上传目录不可写";
} elseif (!move_uploaded_file(
$file['tmp_name'],
$upload_dir . $file['name']
)) {
$err_msg = "将文件移动到目标目录时出错";
} else {
$err_msg = "成功将文件放到目标目录";
$code = 0;
$upload_dir = $upload_dir ;
}
}
echo json_encode([
"code" => $code
,"msg" => $err_msg
,"data" => [
"src" => $upload_dir.$file['name']
,"title" => $file['name']
]
]);
} catch (RuntimeException $e) {
echo json_encode(array('err_msg' => $e->getMessage()));
}
}
function uploadBase64Photo(){
// 假设base64编码的图片数据存储在名为'image_data'的POST变量中
$base64Image = $_POST['base64Url'];
if($base64Image == null || $base64Image == ""){
$err_msg = "没有传入图片";
}else{
// 去除Base64编码字符串中的头部信息
if (preg_match('/^data:image\/\w+;base64,/', $base64Image, $matches)) {
$base64Image = str_replace($matches[0], '', $base64Image);
}
// 解码Base64字符串
$decodedImage = base64_decode($base64Image);
// 上传文件的存放路径
$upload_dir = 'upload_images/';
//随机文件名
$base64ImageName = date('Y-m-d-H-i-s') . 'photo.jpg';
// 如果目录不存在,则创建
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true); // 最后一个参数为 true 表示如果目录树不存在,则递归创建
}
chmod($upload_dir, 0777); // 修改目录权限为最高权限(即所有用户都有读写执行权限)
// 指定保存图片的文件路径和文件名
$targetFile = $upload_dir . $base64ImageName;
// 将解码后的数据写入文件
if (file_put_contents($targetFile, $decodedImage)) {
$err_msg = "图片保存成功";
} else {
$err_msg = "图片保存失败";
}
}
echo $err_msg;
}
}
写出来后感觉并不难,也就是学cropper花了点时间。无论是CSDN还是B站都有很多教程,这点就不赘述了。
现在剩下的问题是截屏出来的图片不是圆形的,也许不影响用户头像的呈现效果,但是感觉总有点怪,以后有时间了再想这一块。