<template>
<div>
<div style="display: flex; align-items: center">
<el-button type="primary" @click="callCamera">点击打开摄像头</el-button>
<el-button size="mini" type="primary" @click="photograph">拍照</el-button>
<el-button size="mini" type="primary" @click="upDataPhotoApi">
上传照片
</el-button>
</div>
<br />
<div style="display: flex">
<div style="margin-right: 20px; border: 1px solid #ccc">
<!--图片展示-->
<video ref="video" autoplay height="480" width="640"></video>
<!--确认-->
</div>
<!--canvas截取流-->
<canvas v-show="false" id="canvas" ref="canvas" height="480" width="640"></canvas>
<Crop v-if="headImgSrc" ref="cROPLL" :image-src="headImgSrc" @updateimagesrc="updateImageSrc" />
</div>
<br />
</div>
</template>
<script>
//接口和base64接口
import { upDataPhoto,uploadBase64Images } from '@/api/village/DoorCheckInLogManagement'
//裁切
import Crop from './crop'
export default defineComponent({
name: 'PhotoComponents',
components: {
Crop,
},
// props: ["data", "url"],
props: {
type: {
type: Number,
default: '',
},
data: {
type: String,
default: '',
},
url: {
type: String,
default: '',
},
},
emits: ['getbyid',"getphonedata"],
setup(props, { emit }) {
const state = reactive({
cROPLL: null,
headImgSrc: '',
video: null,
canvas: null,
})
const $baseMessage = inject('$baseMessage')
// 调用摄像头
const callCamera = () => {
// H5调用电脑摄像头API
//navigator.mediaDevices.getUserMedia 调用后 会提示用户给予使用媒体输入的许可.返回一个 Promise 对象,
//成功后会resolve回调一个 MediaStream 对象。
//在移动设备上面调用摄像头也可以通过此种方式,如下的例子表示优先使用前置摄像头
//{ audio: true, video: { facingMode: "user" } }前摄像头
//{ audio: true, video: { facingMode: { exact: "environment" } } }强制使用后置摄像头
//设置获取接近 1280x720 的相机分辨率
//{ audio: true, video: { width: 1280, height: 720 } };
navigator.mediaDevices
.getUserMedia({
// audio: true,//调用音频
video: true, //说明请求的媒体类型。调用视频
})
.then((success) => {
// 摄像头开启成功
state.video.srcObject = success //src 是播多媒体文件的;srcobj 是实时流
// 实时拍照效果
state.video.play()
})
.catch(() => {
//用户拒绝
console.error('摄像头开启失败,请检查摄像头是否可用!')
})
}
const photograph = () => {
const ctx = state.canvas.getContext('2d')
// 把当前视频帧内容渲染到canvas上
ctx.drawImage(state.video, 0, 0, 640, 480)
//canvas图片 转base64格式、图片格式转换、图片质量压缩
//语法 canvas.toDataURL(type, encoderOptions); 设置转换的图片格式、图片质量压缩
const imgBase64 = state.canvas.toDataURL('image/jpeg', 1)
// let str = imgBase64.replace("data:image/jpeg;base64,", "");
// let strLength = str.length;
// let fileLength = parseInt(strLength - (strLength / 8) * 2); //
// let size = (fileLength / 1024).toFixed(2);// 由字节转换为KB 判断大小
// console.log(size);
function useImg(data) {
state.headImgSrc = ''
setTimeout(() => {
state.headImgSrc = data
})
}
dealImage(imgBase64, 640, useImg)
}
const dealImage = (base64, w, callback) => {
const newImage = new Image()
let quality = 0.6 //压缩系数0-1之间
newImage.src = base64
newImage.setAttribute('crossOrigin', 'Anonymous') //url为外域时需要
let imgWidth, imgHeight
newImage.onload = function () {
imgWidth = 640
imgHeight = 480
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (Math.max(imgWidth, imgHeight) > w) {
if (imgWidth > imgHeight) {
canvas.width = w
canvas.height = (w * imgHeight) / imgWidth
} else {
canvas.height = w
canvas.width = (w * imgWidth) / imgHeight
}
} else {
canvas.width = imgWidth
canvas.height = imgHeight
quality = 0.6
}
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
const base64 = canvas.toDataURL('image/jpeg', quality) //压缩语句
// 如想确保图片压缩到自己想要的尺寸,如要求在100-200kb之间,请加以下语句,quality初始值根据情况自定
// while (base64.length / 1024 > 200) {
// quality -= 0.01;
// base64 = canvas.toDataURL("image/jpeg", quality);
// }
// // 防止最后一次压缩低于最低尺寸,只要quality递减合理,无需考虑
// while (base64.length / 1024 < 100) {
// quality += 0.001;
// base64 = canvas.toDataURL("image/jpeg", quality);
// }
callback(base64) //必须通过回调函数返回,否则无法及时拿到该值,return拿不到
}
}
// 上传照片
const upDataPhotoApi = async () => {
if (state.headImgSrc) {
if (!props.type) {
//接口生成图片
const { msg } = await upDataPhoto({
villageId: props.data.villageId,
id: props.data.personId,
doorId: props.data.doorId,
picBase64Str: state.headImgSrc,
})
$baseMessage(msg, 'success', 'vab-hey-message-success')
state.headImgSrc = ''
emit('getbyid')
const c = document.getElementById('canvas')
const h = c.height
c.height = h
closeCamera()
}else if(props.type == 1){
const { msg,data } = await uploadBase64Images({
base64Img: state.headImgSrc,
})
$baseMessage(msg, 'success', 'vab-hey-message-success')
emit('getphonedata',data)
state.headImgSrc = ''
const c = document.getElementById('canvas')
const h = c.height
c.height = h
closeCamera()
}
} else {
$baseMessage('请先拍照', 'error', 'vab-hey-message-error')
}
}
// 关闭摄像头
const closeCamera = () => {
if (!state.video.srcObject) {
return
}
//MediaStream 接口的getTracks() 方法会返回一个包含媒体流中所有媒体对象
const stream = state.video.srcObject
const tracks = stream.getTracks()
tracks.forEach((track) => {
track.stop()
})
state.video.srcObject = null
}
const updateImageSrc = (data) => {
console.log(data, '-----------')
state.headImgSrc = data
}
onMounted(() => {
console.log(props)
})
return {
...toRefs(state),
photograph,
callCamera,
closeCamera,
upDataPhotoApi,
dealImage,
updateImageSrc,
}
},
})
</script>
crop.vue
//cropperImg.vue
<template>
<div>
<!--使用ref属性给图片元素命名为imageRef-->
<el-button size="mini" type="primary" @click="cropImage">
裁剪图片
</el-button>
<img ref="imageRef" :src="imageSrc" style="width: 640px; height: 480px" />
</div>
</template>
<script setup>
import {
ref,
onMounted,
onBeforeUnmount,
defineProps,
defineEmits,
} from 'vue'
import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'
const props = defineProps({
//图片地址
imageSrc: {
type: String,
required: true,
},
//aspectRatio:置裁剪框为固定的宽高比
aspectRatio: {
type: Number,
default: 0.6,
},
//viewMode: 视图控制
viewMode: {
type: Number,
default: 0.1,
},
//autoCropArea: 设置裁剪区域占图片的大小 值为 0-1 默认 0.8 表示 80%的区域
autoCropArea: {
type: Number,
default: 0.1,
},
})
//绑定图片的dom对象
const imageRef = ref(null)
let cropper = null
//使用Cropper构造函数创建裁剪器实例,并将图片元素和一些裁剪选项传入
onMounted(() => {
cropper = new Cropper(imageRef.value, {
aspectRatio: props.aspectRatio,
viewMode: props.viewMode,
autoCropArea: props.autoCropArea,
})
})
//定义方法
const emit = defineEmits(['updateimagesrc'])
//在cropImage函数中,获取裁剪后的图片数据URL,并使用emit方法触发updateImageSrc事件,
// 将裁剪后的图片数据URL传递给父组件。
const cropImage = () => {
const canvas = cropper.getCroppedCanvas()
const croppedImage = canvas.toDataURL()
emit('updateimagesrc', croppedImage)
}
const create = () => {
cropper.destroy()
}
//销毁
onBeforeUnmount(() => {
create()
})
</script>