vue制作拍照、录像功能

简介: vue制作拍照、录像功能

使用vue制作出来相机的功能:照相、录视频

废话不多说,直接上代码!

布局:

<template>
  <div>
    <video ref="video" autoplay></video>
    <canvas ref="canvas" width="64" height="48"></canvas>
    <div class="footer">
      <div class="font"><b>{{ content }}</b></div>
      <div class="but">
        <input type="file" id="file">
        <label for="file"><img src="../assets/img.png" alt=""></label>
        <!-- <button type="primary">拍照</button> -->
        <div @touchstart="gtouchstart()" @touchmove="gtouchmove()" @touchend="showDeleteButton()" class="lick">
          <p class="li"></p>
        </div>
        <!-- <button style="margin-right: 10px;">切换摄像头</button> -->
        <img @click='changeDevice' src="../assets/look.png" alt="">
      </div>
    </div>
    <div>
      <button @click="getCamera" style="margin-right: 10px;">开启摄像头</button>
      <button @click="closeCamera">关闭摄像头</button>
    </div>
    <a id="downLoadLink" style="display: none;"></a>
    <!-- <p>{{ content }}</p> -->
  </div>
</template>

CSS样式:

<style scoped>
video {
  width: 100%;
  height: 60vh;
  background-color: black;
}
 
#file {
  display: none;
}
 
.font {
  width: 100%;
  text-align: center;
  font-size: 15px;
  color: #667E6E;
  margin-bottom: 20px;
}
 
.footer {
  width: 90%;
  height: 23vh;
  margin-left: 5%;
  position: fixed;
  bottom: 0;
  z-index: 10;
  background-color: white;
}
 
.but {
  display: flex;
  justify-content: space-around;
}
 
.lick {
  width: 70px;
  height: 70px;
  line-height: 70px;
  border-radius: 50%;
  background-color: #E6E6E6;
  /* text-align: center; */
}
 
.li {
  width: 30px;
  height: 30px;
  line-height: 30px;
  background-color: #B7B7B7;
  border-radius: 50%;
  margin: 20px 0 0 20px;
}
 
input {
  width: 50px;
}
 
img {
  width: 40px;
  height: 40px;
  margin-top: 15px;
}
</style>

脚本:

<script>
export default {
  data() {
    return {
      videoArr: [],//所有的摄像头,也可以加入音频设备
      modelSel: '',//当前使用的摄像头
      myInterval: null,
      mediaStreamTrack: {}, // 退出时关闭摄像头
      video_stream: '', // 视频stream
      recordedBlobs: [], // 视频音频 blobs
      isRecord: false, // 视频是否正在录制
      content: '按住拍摄,点击拍照'
    }
  },
  created() {
    this.changeDevice();
  },
  mounted() {
    this.getCamera();
  },
  methods: {
    getCamera() {
      // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
      if (navigator.mediaDevices === undefined) {
        navigator.mediaDevices = {};
      }
      navigator.mediaDevices
        .getUserMedia({
          video: true,
        })
        .then((stream) => {
          // 摄像头开启成功
          this.mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[0];
          this.video_stream = stream;
          this.$refs.video.srcObject = stream;
          this.$refs.video.play();
        })
        .catch(err => {
          console.log(err);
        });
    },
    //长按事件(起始) 返回按钮--------------------------------------------------------------------------------------------
    gtouchstart() {
      var self = this;
      this.timeOutEvent = setTimeout(function () {
        self.longPress();
      }, 500); //这里设置定时器,定义长按500毫秒触发长按事件
      return false;
    },
    //手释放,如果在500毫秒内就释放,则取消长按事件,此时可以执行onclick应该执行的事件
    showDeleteButton() {
      clearTimeout(this.timeOutEvent); //清除定时器
      if (this.timeOutEvent != 0) {    //这里写要执行的内容(如onclick事件)点击未长按
        this.returns = true
        setTimeout(() => {
          this.uploadImg()
        }, 100);
      } else { //长按后松开要执行的内容
        this.returns = false
        this.stop()
        this.content = '按住拍摄,点击拍照'
      }
      return false;
    },
    //如果手指有移动,则取消所有事件,此时说明用户只是要移动而不是长按
    gtouchmove() {
      clearTimeout(this.timeOutEvent); //清除定时器
      this.timeOutEvent = 0;
      console.log("移动");
    },
    //真正长按后应该执行的内容
    longPress() { //执行长按要执行的内容,如弹出菜单
      this.timeOutEvent = 0;
      this.returns = true
      this.record();
      this.content = '视频录制中'
      console.log("长按");
    },
    // 拍照--------------------------------------------------------------------------------------------------------------------
    uploadImg() {
      let ctx = this.$refs['canvas'].getContext('2d');
      ctx.drawImage(this.$refs['video'], 0, 0, 64, 48);
      // let imgBase64 = this.$refs['canvas'].toDataURL('image/jpeg', 0.7);
      console.log(this.$refs['canvas']);
    },
    closeCamera() {
      if (!this.$refs['video'].srcObject) return;
      let stream = this.$refs['video'].srcObject;
      let tracks = stream.getTracks();
      tracks.forEach(track => {
        track.stop();
      });
      this.$refs['video'].srcObject = null;
    },
    changeDevice() {
      navigator.mediaDevices.enumerateDevices().then((devices) => {
        this.videoArr = [];
        devices.forEach((device) => {
          //音频是audioautput  摄像头videoinput
          if (device.kind == 'videoinput') {
            this.videoArr.push({
              'label': device.label,
              'id': device.deviceId
            })
          }
        });
      })
    },
    setCurrentDevice(val) {
      const videoConstraints = {};
      if (val === '') {
        videoConstraints.facingMode = 'environment';
      } else {
        videoConstraints.deviceId = { exact: val };
      }
      var constraints = {
        video: videoConstraints,
      };
      this.getUserMedia(constraints);
    },
    getUserMedia(constraints, success, error) {
      if (navigator.mediaDevices.getUserMedia) {
        //最新的标准API
        navigator.mediaDevices.getUserMedia(constraints).then(success => {
          // 摄像头开启成功
          this.$refs['video'].srcObject = success
          // 实时拍照效果
          this.$refs['video'].play()
        }).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);
      }
    },
    // 视频录制----------------------------------------------------------------------------------------------------------------------
    record() {
      console.log('record');
      this.isRecord = !this.isRecord;
      let mediaRecorder;
      let options;
      this.recordedBlobs = [];
      if (typeof MediaRecorder.isTypeSupported === 'function') {
        // 根据浏览器来设置编码参数
        if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) {
          options = {
            MimeType: 'video/webm;codecs=h264',
          };
        } else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) {
          options = {
            MimeType: 'video/webm;codecs=h264',
          };
        } else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8')) {
          options = {
            MimeType: 'video/webm;codecs=vp8',
          };
        }
        mediaRecorder = new MediaRecorder(this.video_stream, options);
      } else {
        // console.log('isTypeSupported is not supported, using default codecs for browser');
        console.log('当前不支持isTypeSupported,使用浏览器的默认编解码器');
        mediaRecorder = new MediaRecorder(this.video_stream);
      }
      mediaRecorder.start();
      // 视频录制监听事件
      mediaRecorder.ondataavailable = e => {
        console.log(e);
        // 录制的视频数据有效
        if (e.data && e.data.size > 0) {
          this.recordedBlobs.push(e.data);
        }
      };
      // 停止录像后增加下载视频功能,将视频流转为mp4格式
      mediaRecorder.onstop = () => {
        const blob = new Blob(this.recordedBlobs, { type: 'video/mp4' });
        this.recordedBlobs = [];
        // 将视频链接转换完可以用于在浏览器上预览的本地视频
        const videoUrl = window.URL.createObjectURL(blob);
        // 设置下载链接
        document.getElementById('downLoadLink').href = videoUrl;
        // 设置下载mp4格式视频
        document.getElementById('downLoadLink').download = 'media.mp4';
        document.getElementById('downLoadLink').innerHTML = 'DownLoad video file';
        // 生成随机数字
        const rand = Math.floor((Math.random() * 1000000));
        // 生成视频名
        const name = `video${rand}.mp4`;
 
        // setAttribute() 方法添加指定的属性,并为其赋指定的值
        document.getElementById('downLoadLink').setAttribute('download', name);
        document.getElementById('downLoadLink').setAttribute('name', name);
 
        // 0.5s后自动下载视频
        setTimeout(() => {
          document.getElementById('downLoadLink').click();
        }, 500);
      };
    },
    // 停止录制----------------------------------------------------------------------------------------------------------------------
    stop() {
      this.isRecord = !this.isRecord;
      if (!this.$refs.video.srcObject) return;
      const stream = this.$refs.video.srcObject;
      const tracks = stream.getTracks();
      // 关闭摄像头和音频
      tracks.forEach(track => {
        track.stop();
      });
    },
  }
}
</script>
相关文章
|
8天前
|
JavaScript 前端开发 开发者
Vue中的class和style绑定
在 Vue 中,class 和 style 绑定是基于数据驱动视图的强大功能。通过 class 绑定,可以动态更新元素的 class 属性,支持对象和数组语法,适用于普通元素和组件。style 绑定则允许以对象或数组形式动态设置内联样式,Vue 会根据数据变化自动更新 DOM。
|
8天前
|
移动开发 JavaScript API
Vue Router 核心原理
Vue Router 是 Vue.js 的官方路由管理器,用于实现单页面应用(SPA)的路由功能。其核心原理包括路由配置、监听浏览器事件和组件渲染等。通过定义路径与组件的映射关系,Vue Router 将用户访问的路径与对应的组件关联,支持哈希和历史模式监听 URL 变化,确保页面导航时正确渲染组件。
|
8天前
|
JavaScript 前端开发 数据安全/隐私保护
Vue Router 简介
Vue Router 是 Vue.js 官方的路由管理库,用于构建单页面应用(SPA)。它将不同页面映射到对应组件,支持嵌套路由、路由参数和导航守卫等功能,简化复杂前端应用的开发。主要特性包括路由映射、嵌套路由、路由参数、导航守卫和路由懒加载,提升性能和开发效率。安装命令:`npm install vue-router`。
|
12天前
|
监控 JavaScript 前端开发
ry-vue-flowable-xg:震撼来袭!这款基于 Vue 和 Flowable 的企业级工程项目管理项目,你绝不能错过
基于 Vue 和 Flowable 的企业级工程项目管理平台,免费开源且高度定制化。它覆盖投标管理、进度控制、财务核算等全流程需求,提供流程设计、部署、监控和任务管理等功能,适用于企业办公、生产制造、金融服务等多个场景,助力企业提升效率与竞争力。
66 12
|
29天前
|
JavaScript 安全 API
iframe嵌入页面实现免登录思路(以vue为例)
通过上述步骤,可以在Vue.js项目中通过 `iframe`实现不同应用间的免登录功能。利用Token传递和消息传递机制,可以确保安全、高效地在主应用和子应用间共享登录状态。这种方法在实际项目中具有广泛的应用前景,能够显著提升用户体验。
61 8
|
30天前
|
存储 设计模式 JavaScript
Vue 组件化开发:构建高质量应用的核心
本文深入探讨了 Vue.js 组件化开发的核心概念与最佳实践。
77 1
|
1月前
|
JavaScript 前端开发
【Vue.js】监听器功能(EventListener)的实际应用【合集】
而此次问题的核心就在于,Vue实例化的时机过早,在其所依赖的DOM结构尚未完整构建完成时就已启动挂载流程,从而导致无法找到对应的DOM元素,最终致使计算器功能出现异常,输出框错误地显示“{{current}}”,并且按钮的交互功能也完全丧失响应。为了让代码结构更为清晰,便于后续的维护与管理工作,我打算把HTML文件中标签内的JavaScript代码迁移到外部的JS文件里,随后在HTML文件中对其进行引用。
52 8
|
2月前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
151 1
|
2月前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
3月前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的

热门文章

最新文章