需求描述:
手機端用戶選擇的照片,肯定很大,直接上傳到服務器壓力很大,圖片壓縮,本質上後臺很容易實現,現在需要前端來實現。
用戶選擇圖片後,還能立馬在界面上看到預覽效果
具體的實現過程。
首先是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了。因爲實際項目需求。