vue 获取摄像头拍照并旋转、裁剪生成新的图片功能实现
作者:叶子_o
本文给大家介绍vue 获取摄像头拍照并旋转、裁剪生成新的图片功能实现,主要步骤包括初始化、获取摄像头权限、切换摄像头、拍照以及对图片进行旋转和裁剪,同时提到了使用opencv.js和cropperjs进行自动裁剪和手动裁剪的实现,感兴趣的朋友跟随小编一起看看吧
描述:
vue项目中,获取摄像头进行拍照,并对拍摄的图片进行旋转、裁剪等处理
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> <!-- 拍照画面显示区 --> <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) { // 获取拍照设备列表 this.getDevice() }
第三步,获取拍照设备列表
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')) { // 默认选中获取上次选择的设备 this.val = localStorage.getItem('videoSelectId') this.change(this.val) } }) .catch((err) => { console.error(`${err.name}: ${err.message}`) }) } },
切换摄像头时执行(一定要先释放上一次使用的摄像头,再切换新的)
async change() { // 如果 videoSource 不为空,则先释放摄像头!!!!(重点) if (this.videoSource !== null) { this.videoSource.getTracks().forEach(function (track) { track.stop() }) this.videoSource = null } // 这里用定时器,是为了保证上次的摄像头已经完全释放,再切换到新的设备(重点) setTimeout(() => { const index = this.resolutionRatio.lastIndexOf('x') const srtSart = this.resolutionRatio.substring(0, index) const srtEnd = this.resolutionRatio.substring(index + 1, val.length) // 存储选中的设备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) },
拍照,并将拍照的图片根据需要进行旋转,获得新的图片
注意:以下代码中包含多个业务逻辑,A3/A4、横版/竖版、旋转指定角度、自动裁剪(opencv.js)、自动裁剪识别失败后自动弹出手动裁剪弹窗(cropperjs)等,可按需获取, 此处只做简单记录
getImg(type) { if (!this.val && !this.autoVal) { this.$message.warning('请先选择设备') 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, 可根据需要忽略 // 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) // 有数据,表示识别成功 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 } // 防止内存泄漏 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 // 以下为旋转图片逻辑 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 为旋转的角度 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 为旋转的角度 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() { // 旋转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('旋转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() { // 旋转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('旋转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() { // 旋转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) } } } },
到此这篇关于vue 获取摄像头拍照,并旋转、裁剪生成新的图片的文章就介绍到这了,更多相关vue 获取摄像头拍照内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!