vue 獲取攝像頭拍照并旋轉(zhuǎn)、裁剪生成新的圖片功能實現(xiàn)
描述:
vue項目中,獲取攝像頭進行拍照,并對拍攝的圖片進行旋轉(zhuǎn)、裁剪等處理
html部分
<!-- 攝像頭列表 --> <el-select v-model="autoVal" size="small" @change="change('auto', true)"> <el-option v-for="item in vidList" :key="item.deviceId" :value="item.deviceId" :label="item.label" /> </el-select> <!-- 拍照按鈕 --> <el-button size="small" type="primary" @click="getImg('auto')">拍照</el-button> <!-- 拍照畫面顯示區(qū) --> <div id="right-bottom" v-loading="loading" class="right-bottom"> <video v-show="videoFalg" id="video" ref="videoElement" autoplay :srcObject="videoSource" /> <video v-show="false" id="videoElement" ref="video" autoplay :srcObject="videoSource" /> <img id="img" src="" alt=""> <div v-show="!a3Ora4 && !isNewModel" id="videoboder" class="videoboder" /> <canvas v-show="false" id="canvas" /> <!-- <video ref="videoElement" autoplay id="video"></video> --> </div>
第一步:初始化
data() { resolutionRatio: '2592x1944', a3Ora4: false, videoSource: null, acrossOrvertical: true, // 橫版 autoVal: '', videoFalg: false, constraints: {}, val: '', loading: false, imgLoading: false, autoSelectedIndex: '', vidList: [], }, mounted() { this.getMediaInfo() }, methods: { // 第一步 getMediaInfo() { if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) { this.constraints = { video: { width: { ideal: 2592 }, height: { ideal: 1944 }, deviceId: '' } } // 初始化 this.getUserMedia(this.constraints, this.deSuccess, this.error, '0') } else { alert('不支持訪問用戶媒體') } },
第二步:getUserMedia
getUserMedia(constraints, success, error, type) { this.videoFalg = type !== '0' if (navigator.mediaDevices.getUserMedia) { // 最新的標準API navigator.mediaDevices.getUserMedia(constraints) .then(success) .catch(error) } else if (navigator.webkitGetUserMedia) { // webkit核心瀏覽器 navigator.webkitGetUserMedia(constraints, success, error) } else if (navigator.mozGetUserMedia) { // firfox瀏覽器 navigator.mozGetUserMedia(constraints, success, error) } else if (navigator.getUserMedia) { // 舊版API navigator.getUserMedia(constraints, success, error) } }, success(stream) { const video = document.getElementById('video') const el = document.getElementById('videoElement') const videoElement = this.$refs.videoElement // 兼容webkit核心瀏覽器 if (this.videoFalg) { this.videoSource = stream videoElement.srcObject = stream el.srcObject = stream } this.loading = false video.onloadedmetadata = (e) => { video.play() } }, error(err) { console.log(err) }, deSuccess(stream) { // 獲取拍照設(shè)備列表 this.getDevice() }
第三步,獲取拍照設(shè)備列表
getDevice() { if (!navigator.mediaDevices?.enumerateDevices) { console.log('不支持') } else { navigator.mediaDevices .enumerateDevices() .then((devices) => { devices.forEach((device) => { if (device.kind === 'videoinput') { this.vidList.push(device) } }) if (this.vidList.length && localStorage.getItem('videoSelectId')) { // 默認選中獲取上次選擇的設(shè)備 this.val = localStorage.getItem('videoSelectId') this.change(this.val) } }) .catch((err) => { console.error(`${err.name}: ${err.message}`) }) } },
切換攝像頭時執(zhí)行(一定要先釋放上一次使用的攝像頭,再切換新的)
async change() { // 如果 videoSource 不為空,則先釋放攝像頭?。。。。ㄖ攸c) if (this.videoSource !== null) { this.videoSource.getTracks().forEach(function (track) { track.stop() }) this.videoSource = null } // 這里用定時器,是為了保證上次的攝像頭已經(jīng)完全釋放,再切換到新的設(shè)備(重點) setTimeout(() => { const index = this.resolutionRatio.lastIndexOf('x') const srtSart = this.resolutionRatio.substring(0, index) const srtEnd = this.resolutionRatio.substring(index + 1, val.length) // 存儲選中的設(shè)備id localStorage.setItem('videoSelectId', this.autoVal) this.constraints = { video: { width: { ideal: srtSart * 1 }, height: { ideal: srtEnd * 1 }, deviceId: { exact: this.autoVal } } } } this.loading = true this.getUserMedia(this.constraints, this.success, this.error, '1') }, 1000) },
拍照,并將拍照的圖片根據(jù)需要進行旋轉(zhuǎn),獲得新的圖片
注意:以下代碼中包含多個業(yè)務(wù)邏輯,A3/A4、橫版/豎版、旋轉(zhuǎn)指定角度、自動裁剪(opencv.js)、自動裁剪識別失敗后自動彈出手動裁剪彈窗(cropperjs)等,可按需獲取, 此處只做簡單記錄
getImg(type) { if (!this.val && !this.autoVal) { this.$message.warning('請先選擇設(shè)備') return } this.imgLoading = true const el = document.getElementById('videoElement') const canvas = document.getElementById('canvas') var dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1 let base64Url = '' const context = canvas.getContext('2d') // 清空畫布 context.clearRect(0, 0, canvas.width, canvas.height) context.scale(dpr, dpr) if (this.isNewModel || !this.isNewModel && this.a3Ora4) { console.log('進入a3') console.log('容器寬高') console.log('el.videoWidth', el.videoWidth) console.log('el.videoHeight', el.videoHeight) canvas.width = el.videoWidth * dpr canvas.height = el.videoHeight * dpr console.log('容器寬高*dpr') console.log('canvas.width', canvas.width) console.log('canvas.height', canvas.height) console.log('el.videoWidth * dpr', el.videoWidth * dpr) console.log('el.videoHeight * dpr', el.videoHeight * dpr) context.drawImage(el, 0, 0, el.videoWidth, el.videoHeight, 0, 0, canvas.width, canvas.height) const imgdata = context.getImageData(0, 0, canvas.width, canvas.height) console.log('imgdata', imgdata) if (this.isNewModel) { // 新模式拍照 --- 為識別圖片后自動裁剪邏輯,此處用到了opencv.js, 可根據(jù)需要忽略 // eslint-disable-next-line no-undef const sourceMat = cv.imread('canvas') try { // eslint-disable-next-line no-undef const convertScaleAbsMat = getRect(sourceMat, 'canvas', this.currentScheme) // 有數(shù)據(jù),表示識別成功 if (convertScaleAbsMat) { // eslint-disable-next-line no-undef showImage('canvas', convertScaleAbsMat) } else { // 識別失敗,彈出圖片裁剪彈窗手動裁剪,詳見上一篇 base64Url = canvas.toDataURL('image/jpeg') this.currentType = type this.originBase64 = base64Url this.cropperVisible = true setTimeout(() => { this.imgLoading = false }, 1000) return } // 防止內(nèi)存泄漏 if (!sourceMat.isDeleted()) { sourceMat.delete() } } catch (error) { console.error('error', error, sourceMat.isDeleted()) setTimeout(() => { this.imgLoading = false }, 1000) if (!sourceMat.isDeleted()) { sourceMat.delete() } } } else { // 原模式a3拍照 const d /* 圖像的總通道*/ = imgdata.data for (var ii = 0; ii < d.length; ii += 4) { const average = d[ii] * 0.1 + d[ii + 1] * 0.5 + d[ii + 2] * 0.9 d[ii + 0] = average // 紅 d[ii + 1] = average // 綠 d[ii + 2] = average // 藍 } // 4.把處理后的像素信息放回畫布 context.clearRect(0, 0, canvas.width, canvas.height) context.putImageData(imgdata, 0, 0) } base64Url = canvas.toDataURL('image/jpeg') if (!this.acrossOrvertical && this.isNewModel) { // 新模式且是豎版 const image = new Image() image.src = base64Url const that = this // 以下為旋轉(zhuǎn)圖片邏輯 image.onload = function() { const newCanvas = document.createElement('canvas') const newContext = newCanvas.getContext('2d') newCanvas.width = image.height newCanvas.height = image.width newContext.clearRect(0, 0, newCanvas.width, newCanvas.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) // rotateVal 為旋轉(zhuǎn)的角度 newContext.rotate(that.rotateVal * Math.PI / 180) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.drawImage(image, newCanvas.width / 2 - image.width / 2, newCanvas.height / 2 - image.height / 2, image.width, image.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) // rotateVal 為旋轉(zhuǎn)的角度 newContext.rotate(-that.rotateVal * Math.PI / 180) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.restore() base64Url = newCanvas.toDataURL('image/jpeg') const blob = that.convertBase64UrlToBlob(base64Url) const url = window.URL.createObjectURL(that.convertBase64UrlToBlob(base64Url)) setTimeout(() => { that.imgLoading = false }, 1000) that.$emit('photograph', url, blob, type) return } } else { if (this.rotateVal === 180) { console.log('是A4/180') const image = new Image() image.src = base64Url const that = this image.onload = function() { // 旋轉(zhuǎn)180度 const newCanvas = document.createElement('canvas') const newContext = newCanvas.getContext('2d') newCanvas.width = image.width newCanvas.height = image.height newContext.clearRect(0, 0, newCanvas.width, newCanvas.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) newContext.rotate(Math.PI) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.drawImage(image, 0, 0, image.width, image.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) console.log('旋轉(zhuǎn)180:', that.rotateVal, -that.rotateVal * Math.PI / 180) newContext.rotate(-Math.PI) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.restore() base64Url = newCanvas.toDataURL('image/jpeg') const blob = that.convertBase64UrlToBlob(base64Url) const url = window.URL.createObjectURL(that.convertBase64UrlToBlob(base64Url)) setTimeout(() => { that.imgLoading = false }, 1000) that.$emit('photograph', url, blob, type) } } else { const blob = this.convertBase64UrlToBlob(base64Url) const url = window.URL.createObjectURL(this.convertBase64UrlToBlob(base64Url)) setTimeout(() => { this.imgLoading = false }, 1000) this.$emit('photograph', url, blob, type) } } } else if (!this.isNewModel && !this.a3Ora4) { console.log('進入a4') // 橫版 if (this.acrossOrvertical) { canvas.width = el.videoWidth * dpr canvas.height = el.videoHeight * dpr context.drawImage(el, 200, 200, el.videoWidth - 200, el.videoHeight - 200, 0, 0, (el.videoWidth + 170) * dpr, (el.videoHeight + 230) * dpr) const imgdata = context.getImageData(0, 0, canvas.width, canvas.height) const d /* 圖像的總通道*/ = imgdata.data // 2.遍歷每一個像素 for (let i = 0; i < d.length; i += 4) { const average = d[i] * 0.1 + d[i + 1] * 0.5 + d[i + 2] * 0.9 d[i + 0] = average // 紅 d[i + 1] = average // 綠 d[i + 2] = average // 藍 } // 4.把處理后的像素信息放回畫布 context.clearRect(0, 0, canvas.width, canvas.height) context.putImageData(imgdata, 0, 0) base64Url = canvas.toDataURL('image/jpeg') if (this.rotateVal === 180) { console.log('是A4/180') const image = new Image() image.src = base64Url const that = this image.onload = function() { // 旋轉(zhuǎn)180度 const newCanvas = document.createElement('canvas') const newContext = newCanvas.getContext('2d') newCanvas.width = image.width newCanvas.height = image.height newContext.clearRect(0, 0, newCanvas.width, newCanvas.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) newContext.rotate(Math.PI) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.drawImage(image, 0, 0, image.width, image.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) console.log('旋轉(zhuǎn)180:', that.rotateVal, -that.rotateVal * Math.PI / 180) newContext.rotate(-Math.PI) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.restore() base64Url = newCanvas.toDataURL('image/jpeg') const blob = that.convertBase64UrlToBlob(base64Url) const url = window.URL.createObjectURL(that.convertBase64UrlToBlob(base64Url)) setTimeout(() => { that.imgLoading = false }, 1000) that.$emit('photograph', url, blob, type) } } else { const blob = this.convertBase64UrlToBlob(base64Url) const url = window.URL.createObjectURL(this.convertBase64UrlToBlob(base64Url)) setTimeout(() => { this.imgLoading = false }, 1000) this.$emit('photograph', url, blob, type) } } else { // 豎版 canvas.width = el.videoWidth * dpr canvas.height = el.videoHeight * dpr context.drawImage(el, 200, 200, el.videoWidth - 200, el.videoHeight - 200, 0, 0, (el.videoWidth + 170) * dpr, (el.videoHeight + 230) * dpr) const imgdata = context.getImageData(0, 0, canvas.width, canvas.height) const d /* 圖像的總通道*/ = imgdata.data // 2.遍歷每一個像素 for (let i = 0; i < d.length; i += 4) { const average = d[i] * 0.1 + d[i + 1] * 0.5 + d[i + 2] * 0.9 d[i + 0] = average // 紅 d[i + 1] = average // 綠 d[i + 2] = average // 藍 } // 4.把處理后的像素信息放回畫布 context.clearRect(0, 0, canvas.width, canvas.height) context.putImageData(imgdata, 0, 0) base64Url = canvas.toDataURL('image/jpeg') const image = new Image() image.src = base64Url const that = this image.onload = function() { // 旋轉(zhuǎn)90度 const newCanvas = document.createElement('canvas') const newContext = newCanvas.getContext('2d') newCanvas.width = image.height newCanvas.height = image.width newContext.clearRect(0, 0, newCanvas.width, newCanvas.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) newContext.rotate(that.rotateVal * Math.PI / 180) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.drawImage(image, newCanvas.width / 2 - image.width / 2, newCanvas.height / 2 - image.height / 2, image.width, image.height) newContext.translate(newCanvas.width / 2, newCanvas.height / 2) newContext.rotate(-that.rotateVal * Math.PI / 180) newContext.translate(-newCanvas.width / 2, -newCanvas.height / 2) newContext.restore() base64Url = newCanvas.toDataURL('image/jpeg') const blob = that.convertBase64UrlToBlob(base64Url) const url = window.URL.createObjectURL(that.convertBase64UrlToBlob(base64Url)) setTimeout(() => { that.imgLoading = false }, 1000) that.$emit('photograph', url, blob, type) } } } },
到此這篇關(guān)于vue 獲取攝像頭拍照,并旋轉(zhuǎn)、裁剪生成新的圖片的文章就介紹到這了,更多相關(guān)vue 獲取攝像頭拍照內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue簡單封裝axios之解決post請求后端接收不到參數(shù)問題
這篇文章主要介紹了Vue簡單封裝axios之解決post請求后端接收不到參數(shù)問題,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02