html部分
<template>
<div class="views">
<video style="width: 100vw; height: calc(100vh - 18vh)" object-fit="fill"></video>
<!-- <img style="width: 100vw; height: calc(100vh - 18vh)" :src="str" alt="" srcset=""> -->
<div class="picture" @click="pictureClick">
<i class="iconfont icon-picture"></i>
</div>
<div @click="handlePhotographClick" class="action"></div>
<div class="folder" @click="folderClick">
<i class="iconfont icon-folder"></i>
</div>
<div class="bac">
<div>
<div class="img_box" v-for="(img, index) in srcList" :key="img.src + index">
<i class="iconfont icon-guanbi" @click="delImg(img.name, index)"></i>
<img src="../../../../public/img/files1.png" v-if="img.name === 'files'" />
<img :src="img.src" v-else />
<span>{{ index + 1 }}</span>
</div>
</div>
<div>
<button class="btn" @click="upload">确认</button>
</div>
</div>
</div>
</template>
js部分
<script>
export default {
data() {
return {
imageUrl: '',
// 媒体流,用于关闭摄像头
mediaStreamTrack: null,
fileName: '', // 上传文件名
fileList: [], // 上传文件列表
isCamera: true, // 是否是摄像头
imgBase64: '', // 图片base64
photoList: [], // 图片列表
localHeight: 0, // 本地视频高度
srcList: [], // 图片路径列表
}
},
mounted() {
this.invokingCamera()
},
destroyed() {
this.handlePhotographCloseClick()
},
methods: {
// 调用摄像头
invokingCamera() {
const self = this
// 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。
// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先,如果有getUserMedia的话,就获得它
const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia
// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否则,为老的navigator.getUserMedia方法包裹一个Promise
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
const constraints = {
audio: false,
video: {
// 前置摄像头
facingMode: { exact: 'environment' },
// 手机端相当于高
width: Math.max(window.innerWidth, window.innerHeight),
// 手机端相当于宽
height: Math.min(window.innerWidth, window.innerHeight),
},
}
navigator.mediaDevices
.getUserMedia(constraints)
.then(function (stream) {
self.mediaStreamTrack = stream
const video = document.querySelector('video')
// 旧的浏览器可能没有srcObject
if ('srcObject' in video) {
video.srcObject = stream
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
video.src = window.URL.createObjectURL(stream)
}
video.onloadedmetadata = function (e) {
video.play()
}
})
.catch(function (err) {
console.log(err.name + ': ' + err.message)
})
},
// 关闭摄像头
handlePhotographCloseClick() {
if (this.mediaStreamTrack) {
// 关闭摄像头
this.mediaStreamTrack.getTracks().forEach(function (track) {
track.stop()
})
this.mediaStreamTrack = null
}
},
// 拍照
handlePhotographClick() {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const video = document.querySelector('video')
canvas.width = Math.min(video.videoWidth, video.videoHeight)
canvas.height = Math.max(video.videoWidth, video.videoHeight)
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
// 将图片转为base64
const base64Image = canvas.toDataURL('image/png')
// const str = base64Image.replace('data:image/png;base64,', '')
// // 文件对象数组
// this.photoList.push(this.convertBlobToFile(this.convertBase64ToBlob(str), new Date().getTime()))
// 图片路径数组
this.srcList.push({
src: base64Image,
// src: this.str,
name: new Date().getTime() + '.png',
})
},
folderClick() {
// 选择文件
var input = document.createElement('input')
input.type = 'file'
input.accept = '.doc,.docx,.txt,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf'
input.addEventListener('change', (e) => {
// console.log(e.target.files, 'eeeeee')
// 读取选择的文件
this.fileList.push(e.target.files)
this.srcList.push({
src: null,
name: 'files',
})
})
// 触发点击事件,打开文件
input.click()
},
pictureClick() {
const self = this
// 选择图片
var input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.multiple = true
// 点击事件处理函数
input.addEventListener('change', function () {
if (this.files && this.files[0]) {
// 读取选择的图片文件
var reader = new FileReader()
reader.onload = (e) => {
// 图片加载完成后,将图片URL赋值给input的src属性,即可显示图片
// console.log(e.target.result, 'e.target.result')
const type = this.files[0].type.split('/')[1]
self.srcList.push({
src: e.target.result,
name: new Date().getTime() + '.' + type,
})
}
reader.readAsDataURL(this.files[0])
}
})
// 触发点击事件,打开图库
input.click()
},
// 转换为blob格式
convertBase64ToBlob(base64Str) {
// 将base64字符串转为二进制数据
const byteCharacters = atob(base64Str)
// 创建Blob对象
const blob = new Blob([byteCharacters], { type: 'application/octet-stream' })
return blob
},
// 将blob转为file对象
convertBlobToFile(blob, fileName) {
// 创建File对象
const type = fileName.split('.')[1]
const file = new File([blob], fileName, { type: `image/${type}` })
return file
},
delImg(name, index) {
// 删除图片
if (name === 'files') {
this.fileList.splice(index, 1)
}
this.srcList.splice(index, 1)
},
upload() {
// 上传按钮
if (this.srcList.length > 0) {
for (let i = 0; i < this.srcList.length; i++) {
// 文件对象数组
if (this.srcList[i].src === null) {
continue
}
const type = this.srcList[i].name.split('.')[1]
this.photoList.push(this.convertBlobToFile(this.convertBase64ToBlob(this.srcList[i].src.replace(`data:image/${type};base64,`, '')), new Date().getTime() + `.${type}`))
}
}
const fileObj = []
for (let i = 0; i < this.fileList.length; i++) {
fileObj.push(this.fileList[i][0])
}
for (let i = 0; i < this.photoList.length; i++) {
fileObj.push(this.photoList[i])
}
console.log(fileObj, 'fileObj')
},
},
}
</script>
css部分
<style scoped lang="stylus">
.views{
width: 100vw;
height: 100vh;
background-color: #ccc;
position: relative
.picture{
position: absolute;
left: 1rem;
z-index: 9;
bottom: 4rem;
.icon-picture{
font-size: 1rem;
font-weight: bold;
}
}
.folder{
position: absolute;
right: 1rem;
z-index: 9;
bottom: 4rem;
.icon-folder{
font-size: 1rem;
font-weight: bold;
}
}
.action{
position: absolute;
bottom: 20vh;
left: 50%;
margin-left: -35px;
border-radius: 50px;
border: 10px solid #ccc;
box-shadow: 0 0 10px black;
background-color: #fff;
width: 70px;
height: 70px;
display: flex;
justify-content: center;
z-index :99
}
.bac {
overflow-x: scroll;
white-space: nowrap; /* 横向内容不换行 */
align-items: flex-end; /* 图片索引在左下角 */
}
.img_box {
margin-top: 10px;
position: relative;
display: inline-block; /* img_box横向排列 */
margin-left: 8px;
margin-bottom: 8px;
}
.icon-guanbi {
position: absolute;
top: -10px;
right: -10px;
width: 20px;
height: 20px;
font-size : 20px;
color: #fff;
color:red;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
img {
// width: 100px;
height:15vh;
margin-left: 8px
}
span {
position: absolute;
bottom: 0;
left: 0;
margin-left: 4px;
margin-bottom: 4px;
color: #fff;
background-color: #64e8ff;
padding: 4px;
font-size: 20px;
border-radius: 2px 2px 2px 6px;
}
.btn{
color: #fff;
border: none;
background: #029afc;
height: 1rem;
width: 1.5rem;
position: absolute;
right: 0;
bottom: 1rem;
z-index: 99;
border-radius: 10px;
line-height: 1rem;
}
}
</style>
以上代码可以直接复制使用,留个关注吧。