vue3.0 + ts H5拍照详细代码

编程入门 行业动态 更新时间:2024-10-25 18:24:37

vue3.0 + ts H5拍照详细<a href=https://www.elefans.com/category/jswz/34/1771412.html style=代码"/>

vue3.0 + ts H5拍照详细代码

pc端调用摄像头,本地实现H5拍照功能;
解决拍照模糊问题;
解决角度问题;

文件目录

-----wlCamera
-------img
----------camera.png
----------left.png
----------right.png
-----------tip,png
-------less
----------index.modules.less
-------ts
----------h5.ts
----------interface.ts
-------index.tsx

index.tsx

import { defineComponent, reactive, ref } from "vue";
import { Modal, Button } from 'view-ui-plus';
import tipPng from "./img/tip.png";
import cameraPng from "./img/camera.png";
import leftPng from "./img/left.png";
import rightPng from "./img/right.png";
import H5 from "./ts/h5"
import styles from "./less/index.module.less"
import {Props} from "./ts/interface"
export default defineComponent({emits: ["FileEvent"],props: {fileName: {type: String,required: true},ratio: {type: Object,default: () => {return {width: 1280,height: 720}}}},setup (props: Props, {emit, slots}) {let W = props.radio?.width || 1280, H = props.radio?.height || 720;let url = ref<string>(""),text = ref<string>("请点击左上角允许访问摄像头。"),popVb = ref<boolean>(false),textShow = ref<boolean>(true),popCamera = ref<boolean>(false),imgUtilSrc = ref<string>(""),videoEle: HTMLVideoElement,cameraH5 = new H5({radio: {width: W, height: H}, fileName: props.fileName});const videoRef = ref<any>(null);cameraH5.navigatorExtendH5();const onClick = () => {popCamera.value = true;cameraH5.getCaremaPermissionsH5(stream => {if (typeof stream == "string") {text.value = stream;textShow.value = true;popVb.value = stream == "系统无访问摄像头权限,配置后才能使用。";url.value = location.origin;} else {popVb.value = false;textShow.value = false;videoEle = document.getElementById("video") as HTMLVideoElement;videoEle.srcObject = stream;videoEle.onloadedmetadata = function (e) {videoEle.play();};}})}const Camera = () => {let file: File = cameraH5.createCanvas(videoEle);cameraH5.CloseVideoH5();popCamera.value = false;emit("FileEvent", file);}const rotateLeft = () => {cameraH5.rotateCount -= 1;videoRef.value.style.transform = 'translate(-50%, -50%) rotate(' + 90 * cameraH5.rotateCount +'deg)';imgUtilSrc.value = cameraH5.setImageSrc(videoEle);}const rotateRight = () => {cameraH5.rotateCount += 1;videoRef.value.style.transform = 'translate(-50%, -50%) rotate(' + 90 * cameraH5.rotateCount +'deg)';imgUtilSrc.value = cameraH5.setImageSrc(videoEle);}return () => (<><span onClick={onClick}>{slots.default?.() || <Button>拍照</Button>}</span><Modal z-index={9999} v-model={popVb.value} title="多媒体设置" width={900} footer-hide={true} draggable={true}><div id="chrome-tip-dialog" class="pd20"><p>1. 打开在浏览器地址栏中输入 chrome://flags/#unsafely-treat-insecure-origin-as-secure</p><p>2. Insecure origins treated as secure 切换成 Enabled 状态, 如图所示:</p><div class="img-camera-tip"><img src={tipPng} alt=""></img></div><p>3. 输入框中输入域名 {url.value}</p><p>4. 点击重启后生效。</p></div></Modal><Modal v-model={popCamera.value} z-index={1010} title="拍照" width={1280} styles={{top: "10px"}} footer-hide={true} draggable={true} onOnCancel={() => cameraH5.CloseVideoH5()}><div class={styles.content}><div class={styles.tip} v-show={textShow.value}>{text.value}</div><video ref={videoRef} class={styles.video} id="video"></video><canvas id="canvas" class={styles.canvas}></canvas><div v-show={!textShow.value} class={styles.toolBtns}><div class={styles.btn} onClick={rotateRight}>{slots.iconRight?.() || <img class={styles.btnImg} title="右转" src={rightPng} alt="" />}</div><div class={styles.btn} onClick={Camera}>{slots.iconCamera?.() || <img class={styles.btnCameraImg} title="拍照" src={cameraPng} alt="" />}</div><div class={styles.btn} onClick={rotateLeft}>{slots.iconLeft?.() || <img class={styles.btnImg} title="左转" src={leftPng} alt="" />}</div></div></div></Modal></>)}
})

ts

h5.ts


import {Props} from "./interface"
export default class H5 {private ratioWidth: number;private ratioHeight: number;private fileName: string;rotateCount: number;private Angle: number;MediaStreamTrack: any;constructor (cfg: Props) {this.ratioWidth = cfg.radio?.width || 1280;this.ratioHeight = cfg.radio?.height || 720;this.fileName = cfg.fileName;this.rotateCount = 0;this.Angle = 0;}navigatorExtendH5 () {if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function (constraints: MediaStreamConstraints) {var getUserMedia = navigator.getUserMedia;if (!getUserMedia) {return Promise.reject(new Error('getUserMedia is not implemented in this browser'));}return new Promise(function (resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});}}}getCaremaPermissionsH5 (cb) {window.navigator.mediaDevices.getUserMedia({ audio: false, video: {width: this.ratioWidth, height: this.ratioHeight} }).then(stream  => {this.MediaStreamTrack = stream.getTracks()[0];cb && cb(stream);}).catch(err => {var tip = '系统无访问摄像头权限,配置后才能使用。'if (err.name == 'PermissionDeniedError') {tip = '无权限访问摄像头'} else if (err.name.indexOf('NotFoundError') > -1) {tip = '摄像头未连接'}cb && cb(tip);})}transfer (stream: MediaStream, cb) { // 录制视频const mediaRecorder = new MediaRecorder(stream);let recordedBlobs: Blob[] = [];mediaRecorder.ondataavailable = (event) => {if (event.data && event.data.size > 0) {recordedBlobs.push(event.data);}};mediaRecorder.onstop = function(e) {const blob = new Blob(recordedBlobs, { type: 'video/mp4' });cb && cb(blob)}mediaRecorder.start();setTimeout(function () {mediaRecorder.stop();}, 10000);}dataURLtoFile (dataurl) {var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], this.fileName, {type: mime});}getPX (ele, property) {return Number(ele.style[property].slice(0, -2))}createCanvas (videoEle) {let fileUrl = this.setImageSrc(videoEle);return this.dataURLtoFile(fileUrl);}CloseVideoH5 () {this.rotateCount = 0;this.Angle = 0;this.MediaStreamTrack && this.MediaStreamTrack.stop();}setImageSrc (videoEle) {var canvas = document.createElement('canvas');var ctx: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;canvas.width = this.ratioWidth;canvas.height = this.ratioHeight;this.Angle = (90 * this.rotateCount) % 360if (this.Angle < 0) {this.Angle += 360}switch (this.Angle) {case 90:     // 旋转90度canvas.width = this.ratioHeight;canvas.height = this.ratioWidth;    ctx.rotate(Math.PI / 2);ctx.drawImage(videoEle, 0, -this.ratioHeight, this.ratioWidth, this.ratioHeight);break;case 180:     // 旋转180度ctx.rotate(Math.PI);    ctx.drawImage(videoEle, -this.ratioWidth, -this.ratioHeight, this.ratioWidth, this.ratioHeight);break;case 270:     // 旋转270度canvas.width = this.ratioHeight;    canvas.height = this.ratioWidth;    ctx.rotate(3 * Math.PI / 2);    ctx.drawImage(videoEle, -this.ratioWidth, 0, this.ratioWidth, this.ratioHeight);break;default:ctx.drawImage(videoEle, 0, 0, this.ratioWidth, this.ratioHeight);}return canvas.toDataURL('image/png');}
}

interface.ts

interface Radio {width: number,height: number,
}
interface Props {fileName: string,radio?: Radio
}
export {Radio, Props}

img

less

.video, .canvas {top: 50%;left: 50%;transform: translate(-50%, -50%);position: absolute;transform-origin: center center;margin: 0 auto;
}
.content {position: relative;text-align: center;border: 5px dashed #999;margin: 0 auto;overflow: hidden;height: 720px;
}
.tip {top: 50%;left: 50%;transform: translate(-50%, -50%);position: absolute;font-size: 16px;color: #E6A23C;
}
.toolBtns {position: absolute;bottom: 20px;width: 100%;height: 40px;line-height: 40px;
}
.btn {width: 40px;margin: 0 5px;cursor: pointer;display: inline-block;
}
.btnImg {width: 40px;
}
.btnCameraImg {width: 35px;
}

更多推荐

vue3.0 + ts H5拍照详细代码

本文发布于:2024-03-08 04:23:20,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1719822.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:代码   详细   ts

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!