页面唤起相机拍摄、保存、修改尺寸以及上传
近期有个需求,需要在微信中打开移动端页面,让用户在页面中唤起相机进行人脸拍摄,用于身份认证或满足其他业务上的需要。同时要保证上传图像不能过大,需要对图片进行裁剪再上传
过程
- 利用input的accept和capture这两个属性来限定接收类型和相机方向(前后摄像)
- 拍摄获取的图片和普通文件上传一样利用FileReader进行读取处理
- 设置到图片目标大小,再用canvas.drawImage读取图片,最后利用canvas.toDataURL转化为base64
input唤起相机
- accept限定上传文件必须是照片类型image/*
- capture设置为user,指定优先使用前置摄像头(安卓部分低版本不会生效,默认打开后置摄像头)
利用FileReader进行读取处理
function handleTakePhoto(e) {
// 读取照片
if (this.files && this.files[0]) {
const fileData = this.files[0],
fileReader = new FileReader();
// 转化为base64
fileReader.readAsDataURL(fileData);
fileReader.onload = (e) => {
// fileReader.result可以获取到base64图片链接
};
}
}
利用canvas修改图片尺寸
- new一个Image实例并加载图片,再设置图片宽高
- 设置canvas大小与目标尺寸一致,利用drawImage绘制图片,最后利用toDataURL来转化回base64
- 注:此处需等待图片加载完成才能绘制到canvas上,因此需要用promise来做异步处理
function changePhotoSize(imgData) {
const $canvas = document.querySelector(".used-for-changing-photo-size"),
context = $canvas.getContext("2d"),
image = new Image(),
// 设置目标宽度(高度随原比例变化)
targetWidth = 480;
let aspectRatio = 1;
image.src = imgData;
return new Promise((resolve, reject) => {
image.onload = function () {
aspectRatio = (this.height / this.width).toFixed(3);
this.height = aspectRatio * targetWidth;
this.width = targetWidth;
$canvas.setAttribute("width", this.width + "px");
$canvas.setAttribute("height", this.height + "px");
context.drawImage(this, 0, 0, this.width, this.height);
resolve($canvas.toDataURL());
};
image.onerror = (e) => {
reject(e);
};
});
}
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>h5摄像Demo</title>
<style>
.img-container {
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.img-container img {
max-width: 100vw;
margin-bottom: 20px;
height: auto;
}
.call-camera-container {
position: relative;
width: 50px;
margin: 0 auto;
text-align: center;
overflow: hidden;
}
.call-camera-container button {
width: 100%;
height: 100%;
line-height: 30px;
}
.call-camera-container input {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
opacity: 0;
}
canvas.used-for-changing-photo-size {
visibility: hidden;
}
</style>
</head>
<body>
<!-- 拍摄相片 -->
<div class="img-container">
<img src="http://iph.href.lu/375x375" alt="占位" />
</div>
<!-- 唤起按钮 -->
<div class="call-camera-container">
<button>📷</button>
<input type="file" accept="image/*" capture="user" />
</div>
<!-- 用于处理图像大小 -->
<canvas class="used-for-changing-photo-size"></canvas>
<script>
window.addEventListener("load", () => {
const $camera = document.querySelector("input");
// 部分机型默认设置会不生效,需要手动再赋值
$camera.setAttribute("capture", "user");
$camera.onchange = handleTakePhoto;
/**
* 处理照片数据
*/
function handleTakePhoto(e) {
// 读取照片
if (this.files && this.files[0]) {
const fileData = this.files[0],
fileReader = new FileReader();
// 转化为base64
fileReader.readAsDataURL(fileData);
fileReader.onload = (e) => {
changePhotoSize(fileReader.result).then(res => {
const $img = document.querySelector(".img-container img");
$img !== null && ($img.src = res);
}).catch(e => { })
};
}
}
/**
* 调整照片大小
* @param {string} imgData
*/
function changePhotoSize(imgData) {
const $canvas = document.querySelector(".used-for-changing-photo-size"),
context = $canvas.getContext("2d"),
image = new Image(),
// 设置目标宽度(高度随原比例变化)
targetWidth = 480;
let aspectRatio = 1;
image.src = imgData;
return new Promise((resolve, reject) => {
image.onload = function () {
aspectRatio = (this.height / this.width).toFixed(3);
this.height = aspectRatio * targetWidth;
this.width = targetWidth;
$canvas.setAttribute("width", this.width + "px");
$canvas.setAttribute("height", this.height + "px");
context.drawImage(this, 0, 0, this.width, this.height);
resolve($canvas.toDataURL());
};
image.onerror = (e) => {
reject(e);
};
});
}
});
</script>
</body>
</html>