需求:点击用户页面的头像上传或拍照更换头像。上传图片后要求可裁剪移动旋转。
分析:经过大量的调研,我选择了 cropperjs 去实现这一功能,所以将本次解决过程和思路做一回顾。
首先,在项目中引入 cropperjs -------- npm install cropperjs --save
其次我在全局创建了一个组件名为:cropper.vue
此处是cropper.vue 组件内容,
这个div是一个蒙层效果,此处 img绑定的是我们上传的图片路径。此处container绑定的:style="{height: Global.HEIGHT(114) + 'px'}" 是我自己设置的一个全局计算移动端屏幕高度一个方法,大家可以改一下。
{{name.substr(name.length-2)}}
div类名为avatar,这块我绑定了一个imageShow,这块关键是我们的需求是如果用户没有上传图片变默认用他的昵称最后两位做他的头像,昵称从接口获取。所以这块{{name.substr(name.length-2)}}大家可以根据自己的要求改。
import Cropper from "cropperjs";
export default {
props: {
// 图像路劲
path: {
type: String,
default: () => ''
},
// 患者姓名
name: {
type: String,
default: () => ''
},
},
data () {
return {
headerImage: "",
picValue: "",
cropper: "",
newpath: this.path,
croppable: false,
panel: false,
url: "",
imgCropperData: {
accept: "image/gif, image/jpeg, image/png, image/jpg"
},
// 是否显示图片
imageShow: true
};
},
mounted () {
//初始化这个裁剪框
var self = this;
var image = document.getElementById("image");
this.cropper = new Cropper(image, {
aspectRatio: 1 / 1,
viewMode: 1,
background: false,
zoomable: true, //是否可以缩放图片 true-可以
movable: true,//是否能移动图片
cropBoxMovable: false,//是否允许拖动裁剪框
cropBoxResizable: false,//是否允许拖动 改变裁剪框大小
dragMode: 'move', // 是否可以移动图片
ready: function () {
self.croppable = true;
}
});
},
methods: {
/**
* @description: 取消上传
* @param {type}
* @Date: 2019-05-25 16:20:17
*/
cancel () {
// 关闭弹框
this.panel = false;
// 清空此时上传的文件
this.$refs.files.value = '';
},
/**
* @description: 点击头像调用手机相册
* @param {type}
* @Date: 2019-05-25 16:19:46
*/
changeImg () {
this.$refs.files.click()
},
/**
* @description: 创建url路径
* @param {type}
* @Date: 2019-05-25 16:19:10
*/
getObjectURL (file) {
var url = null;
if (window.createObjectURL != undefined) {
// basic
url = window.createObjectURL(file);
} else if (window.URL != undefined) {
// mozilla(firefox)
url = window.URL.createObjectURL(file);
} else if (window.webkitURL != undefined) {
// webkit or chrome
url = window.webkitURL.createObjectURL(file);
}
return url;
},
/**
* @description: input框change事件,获取到上传的文件
* @param {type}
* @Date: 2019-05-25 16:19:02
*/
change (e) {
let files = e.target.files || e.dataTransfer.files;
if (!files.length) return;
let type = files[0].type; //文件的类型,判断是否是图片
let size = files[0].size; //文件的大小,判断图片的大小
if (this.imgCropperData.accept.indexOf(type) == -1) {
alert("请选择我们支持的图片格式!");
return false;
}
if (size > 5242880) {
alert("请选择5M以内的图片!");
return false;
}
this.picValue = files[0];
this.url = this.getObjectURL(this.picValue);
//每次替换图片要重新得到新的url
if (this.cropper) {
this.cropper.replace(this.url);
}
this.panel = true;
},
/**
* @description: 确定提交
* @param {type}
* @Date: 2019-05-25 16:18:38
*/
commit () {
this.panel = false;
this.$refs.files.value = '';
var croppedCanvas;
var roundedCanvas;
if (!this.croppable) {
return;
}
// Crop
croppedCanvas = this.cropper.getCroppedCanvas();
// Round
roundedCanvas = this.getRoundedCanvas(croppedCanvas);
this.headerImage = roundedCanvas.toDataURL();
let a = this.dataURLtoFile(this.headerImage)
//上传图片
this.postImg(a);
},
/**
* @description: canvas画图
* @param {type}
* @Date: 2019-05-25 16:18:29
*/
getRoundedCanvas (sourceCanvas) {
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var width = sourceCanvas.width;
var height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = "destination-in";
context.beginPath();
context.arc(
width / 2,
height / 2,
Math.min(width, height) / 2,
0,
2 * Math.PI,
true
);
context.fill();
return canvas;
},
/**
* @description: 上传头像接口
* @param {type}
* @Date: 2019-05-25 16:18:13
*/
async postImg (a) {
const { fkPatientId } = storage.get('user')
let formData = new FormData();
formData.append("file", a);
formData.append("fkPatientId", fkPatientId);
try {
// 获取随访详情Api
let res = await Fetch.post('/api/patient/updatePhoto.shtml', formData, '')
this.newpath = res.rs
// 第一上传图片去除文字显示
this.imageShow = true
} catch (err) {
console.log(err)
}
},
/**
* @description: base64位图片转码文件流
* @param {type}
* @Date: 2019-05-25 09:57:22
*/
dataURLtoFile (dataurl, filename = 'file') {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
},
/**
* @param {type}
* @Description: 图片加载错误
* @Date: 2019-04-28 10:29:43
*/
imgLoadError () {
// 隐藏图片
this.imageShow = false
}
}
};
#demo {
#button,
#cancel {
position: absolute;
right: 10px;
top: 10px;
border: none;
border-radius: 5px;
width: 120px;
height: 60px;
background: #31aaff;
color: #fff;
}
#cancel {
left: 10px;
}
.container {
z-index: 99;
position: fixed;
display: flex;
align-items: center;
justify-content: center;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
}
#image {
max-width: 100%;
}
.upshow {
.upfile {
display: none;
}
}
}
.avatar {
height: 88px;
width: 88px;
border-radius: 50%;
background: none;
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 46px;
position: relative;
> div {
overflow: hidden;
height: inherit;
width: inherit;
align-items: flex-start;
display: flex;
border-radius: 50%;
> img {
width: inherit;
}
}
> span {
font-weight: bold;
letter-spacing: 4px;
white-space: nowrap;
color: white;
}
> .unread {
height: 10px;
width: 10px;
top: -8px;
}
&.shadow {
background: #8d96a6;
&:before {
content: "";
width: 100%;
height: 100%;
position: absolute;
border-radius: 50%;
box-shadow: inset 0 0 20px 0 rgba(0, 0, 0, 0.2);
z-index: 99;
left: 0px;
top: 0px;
}
}
}
在头像页面我们只需要调用此组件便可:我的父组件为 my.vue: