vue 獲取攝像頭拍照并旋轉(zhuǎn)、裁剪生成新的圖片功能實(shí)現(xiàn)
描述:
vue項(xiàng)目中,獲取攝像頭進(jìn)行拍照,并對拍攝的圖片進(jìn)行旋轉(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) {
// 最新的標(biāo)準(zhǔn)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')) {
// 默認(rèn)選中獲取上次選擇的設(shè)備
this.val = localStorage.getItem('videoSelectId')
this.change(this.val)
}
})
.catch((err) => {
console.error(`${err.name}: ${err.message}`)
})
}
},切換攝像頭時執(zhí)行(一定要先釋放上一次使用的攝像頭,再切換新的)
async change() {
// 如果 videoSource 不為空,則先釋放攝像頭?。。。。ㄖ攸c(diǎn))
if (this.videoSource !== null) {
this.videoSource.getTracks().forEach(function (track) {
track.stop()
})
this.videoSource = null
}
// 這里用定時器,是為了保證上次的攝像頭已經(jīng)完全釋放,再切換到新的設(shè)備(重點(diǎn))
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ù)需要進(jìn)行旋轉(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('進(jìn)入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 // 藍(lán)
}
// 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('進(jìn)入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 // 藍(lán)
}
// 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 // 藍(lán)
}
// 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)文章希望大家以后多多支持腳本之家!
- vue3開啟攝像頭并進(jìn)行拍照的實(shí)現(xiàn)示例
- vue如何調(diào)用攝像頭實(shí)現(xiàn)拍照上傳圖片、本地上傳圖片
- vue調(diào)用攝像頭進(jìn)行拍照并能保存到本地的方法
- Vue實(shí)現(xiàn)調(diào)用PC端攝像頭實(shí)時拍照
- Vue調(diào)用PC攝像頭實(shí)現(xiàn)拍照功能
- vue調(diào)取電腦攝像頭實(shí)現(xiàn)拍照功能
- vue調(diào)用本地攝像頭實(shí)現(xiàn)拍照功能
- Vue2.0實(shí)現(xiàn)調(diào)用攝像頭進(jìn)行拍照功能 exif.js實(shí)現(xiàn)圖片上傳功能
相關(guān)文章
vue項(xiàng)目下,如何用命令直接修復(fù)ESLint報錯
這篇文章主要介紹了vue項(xiàng)目下,如何用命令直接修復(fù)ESLint報錯,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04
Vue簡單封裝axios之解決post請求后端接收不到參數(shù)問題
這篇文章主要介紹了Vue簡單封裝axios之解決post請求后端接收不到參數(shù)問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02
vue3實(shí)現(xiàn)問卷調(diào)查的示例代碼
本文主要介紹了vue3實(shí)現(xiàn)問卷調(diào)查的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05

