展示
布局样式
<script src="https://cdn.bootcss.com/vConsole/3.3.4/vconsole.min.js"></script>
<style>canvas{
width: 200px;
height: 200px;
opacity: 0;
}
.upload{
width: 200px;
height: 200px;
display: inline-block;
border-radius: 10px;
border: 1px dashed rgb(122, 201, 221);
background-color: rgb(188, 224, 223);
overflow: hidden;
position: relative;
display: inline-block;
}
#upload-img{
width: 100%;
height: 100%;
object-fit: cover;
}
input[type='file']{
position: absolute;
top: 0;
left: 0;
font-size: 1000%;
opacity: 0;
}</style>
<div>
<div class="upload">
<img id="upload-img" src="" alt="">
<input id="file" type="file" capture="camera" accept="image/*" onchange="handleFileChange(this)">
</div>
<canvas id="canvas" width="200" height="200"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/exif-js"></script>
逻辑
var vConsole = new VConsole();
/**
* 监听调用摄像头
*/
async function handleFileChange(element) {
var file = element.files[0];
const or = await getImageTag(file);
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = function () {
const result = this.result;
const img = new Image();
img.src = result;
img.onload = function () {
const data = getRotateImg(img, or);
const f = dataURLtoFile(data);
/*
// 模拟上传文件后的回显
const reader1 = new FileReader();
reader1.readAsDataURL(f);
reader1.onloadend = function () {
const result1 = this.result;
document.getElementById("upload-img").src = result1;
}
*/
};
};
}
/**
* @desc 获取图片旋转方向
*/
const getImageTag = (file) => {
if (!file) return 0;
return new Promise((resolve, reject) => {
EXIF.getData(file, function () {
const o = EXIF.getTag(this, 'Orientation');
resolve(o); // 6 1 3 8
});
});
};
/**
* @desc 获取旋转后的图片
* @param {Object} img 图片文件
* @param {Number} or 旋转信息
*/
function getRotateImg(img, or) {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 原始图片
const width = img.width;
const height = img.height;
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
// 旋转后图片
switch (or) {
case 6: // 顺时针旋转90度
return rotateImg(img, 'right', canvas);
break;
case 8: // 逆时针旋转90度
return rotateImg(img, 'left', canvas);
break;
case 3: // 顺时针旋转180度
return rotateImg(img, 'right', canvas, 2);
break;
default:
break;
}
}
/**
* @desc 旋转canvas,会对源数据canvas进行修改
* @param {Object} img 图片文件
* @param {String} dir 方向 left逆时针|right顺时针
* @param {Object} canvas 画布
* @param {Number} s 向指定方向旋转几步,1步为90度
*/
const rotateImg = (img, dir = 'right', canvas, s = 1) => {
const MIN_STEP = 0;
const MAX_STEP = 3;
const width = canvas.width || img.width;
const height = canvas.height || img.height;
let step = 0;
if (dir === 'right') {
step += s;
step > MAX_STEP && (step = MIN_STEP);
} else {
step -= s;
step < MIN_STEP && (step = MAX_STEP);
}
const degree = step * 90 * Math.PI / 180;
const ctx = canvas.getContext('2d');
switch (step) {
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height, width, height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height, width, height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0, width, height);
break;
default:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
break;
}
return canvas.toDataURL('image/jpg');
};
/**
* @desc 将base64的图片转为文件流
* @param {String} dataUrl base64数据
* @return {Object} 文件流
*/
const dataURLtoFile = (dataUrl) => {
const filename = `img${Date.now()}`;
const arr = dataUrl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {
type: mime
});
};