需求描述:
手机端用户选择的照片,肯定很大,直接上传到服务器压力很大,图片压缩,本质上后台很容易实现,现在需要前端来实现。
用户选择图片后,还能立马在界面上看到预览效果
具体的实现过程。
首先是html代码,重点只需要看<input>
标签
<div class="upload-wrap">
<!-- 图片预览 -->
<div class="xl-uploader__preview">
<div class="xl-uploader__preview-image">
<van-image height="100%" width="100%" :src="imgSrc" />
</div>
<van-icon
class="xl-uploader__clear"
color="#13b7f6"
name="close"
/>
</div>
<!-- 本地选择图片 -->
<div class="xl-uploader__upload">
<van-icon name="photo" size="0.64rem" color="#dcdee0" />
<input ref="myFileInput" class="xl-uploader__input" type="file" accept="image/jpg, image/jpeg, image/png" @change="uploadImg"></input>
</div>
<!-- 手机拍照 -->
<div class="xl-uploader__upload">
<van-icon name="photograph" size="0.64rem" color="#dcdee0" />
</div>
</div>
其次是css内容:
重点是input标签的opacity: 0;
,以及宽高都是父元素的高度。实现结果是表面上点击的是外面的div,实际上会触发input的点击事件。
.upload-wrap {
display: flex;
}
.xl-uploader__upload {
position: relative;
width: 80px;
height: 80px;
background-color: #f7f8fa;
border-radius: 8px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
margin-right: 10px;
}
.xl-uploader__input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
cursor: pointer;
opacity: 0;
}
.xl-uploader__preview {
margin-right: 10px;
position: relative;
}
.xl-uploader__preview-image {
width: 80px;
height: 80px;
border-radius: 8px;
overflow: hidden;
}
.xl-uploader__clear {
position: absolute;
top: -8px;
right: -8px;
font-size: 18px;
}
最后是js实现过程:
async uploadImg() {
try {
var that = this
const inputFiles = await this.$refs.myFileInput.files
if (inputFiles.length > 1) {
// 最多只能上传1张图片
return false
}
const img = await this.readImg(inputFiles[0])
const imgSrc = await this.compressImg(img, 1000, 1000)
console.log(imgSrc)
// 拿到转换的base64格式的图片内容了,接下去就可以去实现其他的操作。譬如作为字段内容传递给后台等,还可以拿这个imgSrc来赋值给图片的src来实现图片查看预览操作。
} catch (error) {
console.log(error)
}
},
/**
* 将图片file转换为img对象
*/
readImg(file) {
return new Promise((resolve, reject) => {
const img = new Image()
const reader = new FileReader()
reader.onload = function(e) {
img.src = e.target.result
}
reader.onerror = function(e) {
reject(e)
}
reader.readAsDataURL(file)
img.onload = function() {
resolve(img)
}
img.onerror = function(e) {
reject(e)
}
})
},
/**
* 压缩图片
* @param img 被压缩的img对象
* @param mx 触发压缩的图片最大宽度限制
* @param mh 触发压缩的图片最大高度限制
*/
compressImg(img, mx, mh) {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const { width: originWidth, height: originHeight } = img
let dataURL = ''
// 最大尺寸限制
const maxWidth = mx
const maxHeight = mh
// 目标尺寸
let targetWidth = originWidth
let targetHeight = originHeight
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > 1) {
// 宽图片
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
// 高图片
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
// 图片绘制
context.drawImage(img, 0, 0, targetWidth, targetHeight)
dataURL = canvas.toDataURL('image/jpeg') // 转换图片为dataURL
resolve(dataURL)
// 转换为bolb对象
// canvas.toBlob(function(blob) {
// resolve(blob)
// }, type || 'image/png')
})
},
我这里去掉了图片格式传参,也将转换为bolb修改为base64了。因为实际项目需求。