vue如何調(diào)用攝像頭實現(xiàn)拍照上傳圖片、本地上傳圖片
今天項目上遇到一個功能,人臉錄入,要求用戶可以自己調(diào)用攝像頭拍照上傳圖片,還可以上傳本地圖片,這個功能并沒有那么復(fù)雜,今天就封裝一下這個功能,首先咱們看一下效果。
下面是封裝好的效果,因為封裝的時候做了頁面銷毀關(guān)閉攝像頭,所以加了一個打開攝像頭的功能,我們可以自己拍照,也有本地上傳按鈕
下面是拍照效果,左邊部分是你點擊拍照上傳服務(wù)器返回來的地址,然后顯示效果,右邊就是咱們上面說的封裝好的組件
下面看一下組件實現(xiàn)的代碼
主要功能邏輯就是調(diào)用是攝像頭權(quán)限,把實時畫面顯示到video里面,點擊拍照的時候繪制vanvas,然后把canvas轉(zhuǎn)base64圖片,base64圖片轉(zhuǎn)文件進行上傳,上傳圖片就不多介紹了,就是Element文件上傳,配置action,headers 就行。下面直接上代碼吧。
<template> <div class="camera_outer"> <video id="videoCamera" :width="videoWidth" :height="videoHeight" autoplay></video> <canvas id="canvasCamera" style="display:none;" :width="videoWidth" :height="videoHeight"></canvas> <div style="display: flex;justify-content: space-between;align-items: center;"> <el-button type="primary" @click="setImage()">拍照</el-button> <el-upload style="display: flex;align-items: center;" :headers="headers" :show-file-list="false" :on-success="handleAvatarSuccess" class="avatar-uploader" action="/admin/sys-file/upload"> <el-button type="primary">上傳照片</el-button> </el-upload> <el-button @click="getCompetence()">打開攝像頭</el-button> </div> </div> </template> <script> import store from '@/store' import { uploadImg } from '@/api/visitinfo' export default { data() { return { videoWidth: 300, videoHeight: 300, imgSrc: '', thisCancas: null, thisContext: null, thisVideo: null, headers: { Authorization: 'Bearer ' + store.getters.access_token } } }, mounted() { this.getCompetence() }, destroyed() { this.stopNavigator() }, methods: { handleAvatarSuccess(res, file) { this.$emit('refreshDataList', res.data.url) }, // 調(diào)用權(quán)限(打開攝像頭功能) getCompetence() { var _this = this this.thisCancas = document.getElementById('canvasCamera') this.thisContext = this.thisCancas.getContext('2d') this.thisVideo = document.getElementById('videoCamera') // 舊版本瀏覽器可能根本不支持mediaDevices,我們首先設(shè)置一個空對象 if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {} } // 一些瀏覽器實現(xiàn)了部分mediaDevices,我們不能只分配一個對象 // 使用getUserMedia,因為它會覆蓋現(xiàn)有的屬性。 // 這里,如果缺少getUserMedia屬性,就添加它。 if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function (constraints) { // 首先獲取現(xiàn)存的getUserMedia(如果存在) var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia // 有些瀏覽器不支持,會返回錯誤信息 // 保持接口一致 if (!getUserMedia) { return Promise.reject(new Error('getUserMedia is not implemented in this browser')) } // 否則,使用Promise將調(diào)用包裝到舊的navigator.getUserMedia return new Promise(function (resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject) }) } } var constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } } navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { // 舊的瀏覽器可能沒有srcObject if ('srcObject' in _this.thisVideo) { _this.thisVideo.srcObject = stream } else { // 避免在新的瀏覽器中使用它,因為它正在被棄用。 _this.thisVideo.src = window.URL.createObjectURL(stream) } _this.thisVideo.onloadedmetadata = function (e) { _this.thisVideo.play() } }).catch(err => { console.log(err) }) }, // 繪制圖片(拍照功能) setImage() { var _this = this // 點擊,canvas畫圖 _this.thisContext.drawImage(_this.thisVideo, 0, 0, _this.videoWidth, _this.videoHeight) // 獲取圖片base64鏈接 var image = this.thisCancas.toDataURL('image/png') _this.imgSrc = image const file = image const time = (new Date()).valueOf() const name = time + '.png' const conversions = this.base64ToFile(file, name) const data = new FormData() data.append('file', conversions) uploadImg(data).then(res => { if (res.data.code == 0) { this.$emit('refreshDataList', res.data.data.url) } }) }, // base64圖片轉(zhuǎn)file的方法(base64圖片, 設(shè)置生成file的文件名) base64ToFile(base64, fileName) { // 將base64按照 , 進行分割 將前綴 與后續(xù)內(nèi)容分隔開 const data = base64.split(',') // 利用正則表達式 從前綴中獲取圖片的類型信息(image/png、image/jpeg、image/webp等) const type = data[0].match(/:(.*?);/)[1] // 從圖片的類型信息中 獲取具體的文件格式后綴(png、jpeg、webp) const suffix = type.split('/')[1] // 使用atob()對base64數(shù)據(jù)進行解碼 結(jié)果是一個文件數(shù)據(jù)流 以字符串的格式輸出 const bstr = window.atob(data[1]) // 獲取解碼結(jié)果字符串的長度 let n = bstr.length // 根據(jù)解碼結(jié)果字符串的長度創(chuàng)建一個等長的整形數(shù)字數(shù)組 // 但在創(chuàng)建時 所有元素初始值都為 0 const u8arr = new Uint8Array(n) // 將整形數(shù)組的每個元素填充為解碼結(jié)果字符串對應(yīng)位置字符的UTF-16 編碼單元 while (n--) { // charCodeAt():獲取給定索引處字符對應(yīng)的 UTF-16 代碼單元 u8arr[n] = bstr.charCodeAt(n) } // 利用構(gòu)造函數(shù)創(chuàng)建File文件對象 // new File(bits, name, options) const file = new File([u8arr], `${fileName}.${suffix}`, { type: type }) // 將File文件對象返回給方法的調(diào)用者 return file }, // 關(guān)閉攝像頭 stopNavigator() { this.thisVideo.srcObject.getTracks()[0].stop() } } } </script> <style lang="less" scoped> .camera_outer { position: relative; overflow: hidden; // background: url("../../assets/img/user_0608_04.png") no-repeat center; background-size: 100%; video, canvas, .tx_img { -moz-transform: scaleX(-1); -webkit-transform: scaleX(-1); -o-transform: scaleX(-1); transform: scaleX(-1); } .btn_camera { position: absolute; bottom: 4px; left: 0; right: 0; height: 50px; background-color: rgba(0, 0, 0, 0.3); line-height: 50px; text-align: center; color: #ffffff; } .bg_r_img { position: absolute; bottom: 0; left: 0; right: 0; top: 0; } .img_bg_camera { position: absolute; bottom: 0; left: 0; right: 0; top: 0; img { width: 300px; height: 300px; } .img_btn_camera { position: absolute; bottom: 0; left: 0; right: 0; height: 50px; line-height: 50px; text-align: center; background-color: rgba(0, 0, 0, 0.3); color: #ffffff; .loding_img { width: 50px; height: 50px; } } } } </style>
封裝好以后就是調(diào)用了
<template> <div> <TakePhotos ref="TakePhotos" @refreshDataList="refreshDataList" /> </div> </template> <script> export default { methods: { refreshDataList(imgSrc) { // 這里返回服務(wù)器圖片的地址 console.log(imgSrc) }, } </script>
上面代碼注釋已經(jīng)都寫了,相信大家都能看懂
總結(jié)
到此這篇關(guān)于vue如何調(diào)用攝像頭實現(xiàn)拍照上傳圖片、本地上傳圖片的文章就介紹到這了,更多相關(guān)vue調(diào)用攝像頭拍照上傳圖片內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實現(xiàn)前臺列表數(shù)據(jù)過濾搜索、分頁效果
這篇文章主要為大家詳細介紹了vue實現(xiàn)前臺列表數(shù)據(jù)過濾搜索、分頁效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-05-05Vue報錯:TypeError:?Cannot?create?property?‘xxxx‘?on的解決
這篇文章主要介紹了Vue報錯:TypeError:?Cannot?create?property?‘xxxx‘?on的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06vue如何解決sass-loader的版本過高導(dǎo)致的編譯錯誤
這篇文章主要介紹了vue如何解決sass-loader的版本過高導(dǎo)致的編譯錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06