html部分

  1. <template>
  2. <div class="views">
  3. <video style="width: 100vw; height: calc(100vh - 18vh)" object-fit="fill"></video>
  4. <!-- <img style="width: 100vw; height: calc(100vh - 18vh)" :src="str" alt="" srcset=""> -->
  5. <div class="picture" @click="pictureClick">
  6. <i class="iconfont icon-picture"></i>
  7. </div>
  8. <div @click="handlePhotographClick" class="action"></div>
  9. <div class="folder" @click="folderClick">
  10. <i class="iconfont icon-folder"></i>
  11. </div>
  12. <div class="bac">
  13. <div>
  14. <div class="img_box" v-for="(img, index) in srcList" :key="img.src + index">
  15. <i class="iconfont icon-guanbi" @click="delImg(img.name, index)"></i>
  16. <img src="../../../../public/img/files1.png" v-if="img.name === 'files'" />
  17. <img :src="img.src" v-else />
  18. <span>{{ index + 1 }}</span>
  19. </div>
  20. </div>
  21. <div>
  22. <button class="btn" @click="upload">确认</button>
  23. </div>
  24. </div>
  25. </div>
  26. </template>

js部分

  1. <script>
  2. export default {
  3. data() {
  4. return {
  5. imageUrl: '',
  6. // 媒体流,用于关闭摄像头
  7. mediaStreamTrack: null,
  8. fileName: '', // 上传文件名
  9. fileList: [], // 上传文件列表
  10. isCamera: true, // 是否是摄像头
  11. imgBase64: '', // 图片base64
  12. photoList: [], // 图片列表
  13. localHeight: 0, // 本地视频高度
  14. srcList: [], // 图片路径列表
  15. }
  16. },
  17. mounted() {
  18. this.invokingCamera()
  19. },
  20. destroyed() {
  21. this.handlePhotographCloseClick()
  22. },
  23. methods: {
  24. // 调用摄像头
  25. invokingCamera() {
  26. const self = this
  27. // 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。
  28. // 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
  29. if (navigator.mediaDevices === undefined) {
  30. navigator.mediaDevices = {}
  31. }
  32. // 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
  33. // 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
  34. if (navigator.mediaDevices.getUserMedia === undefined) {
  35. navigator.mediaDevices.getUserMedia = function (constraints) {
  36. // 首先,如果有getUserMedia的话,就获得它
  37. const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia
  38. // 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
  39. if (!getUserMedia) {
  40. return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
  41. }
  42. // 否则,为老的navigator.getUserMedia方法包裹一个Promise
  43. return new Promise(function (resolve, reject) {
  44. getUserMedia.call(navigator, constraints, resolve, reject)
  45. })
  46. }
  47. }
  48. const constraints = {
  49. audio: false,
  50. video: {
  51. // 前置摄像头
  52. facingMode: { exact: 'environment' },
  53. // 手机端相当于高
  54. width: Math.max(window.innerWidth, window.innerHeight),
  55. // 手机端相当于宽
  56. height: Math.min(window.innerWidth, window.innerHeight),
  57. },
  58. }
  59. navigator.mediaDevices
  60. .getUserMedia(constraints)
  61. .then(function (stream) {
  62. self.mediaStreamTrack = stream
  63. const video = document.querySelector('video')
  64. // 旧的浏览器可能没有srcObject
  65. if ('srcObject' in video) {
  66. video.srcObject = stream
  67. } else {
  68. // 防止在新的浏览器里使用它,应为它已经不再支持了
  69. video.src = window.URL.createObjectURL(stream)
  70. }
  71. video.onloadedmetadata = function (e) {
  72. video.play()
  73. }
  74. })
  75. .catch(function (err) {
  76. console.log(err.name + ': ' + err.message)
  77. })
  78. },
  79. // 关闭摄像头
  80. handlePhotographCloseClick() {
  81. if (this.mediaStreamTrack) {
  82. // 关闭摄像头
  83. this.mediaStreamTrack.getTracks().forEach(function (track) {
  84. track.stop()
  85. })
  86. this.mediaStreamTrack = null
  87. }
  88. },
  89. // 拍照
  90. handlePhotographClick() {
  91. const canvas = document.createElement('canvas')
  92. const ctx = canvas.getContext('2d')
  93. const video = document.querySelector('video')
  94. canvas.width = Math.min(video.videoWidth, video.videoHeight)
  95. canvas.height = Math.max(video.videoWidth, video.videoHeight)
  96. ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
  97. // 将图片转为base64
  98. const base64Image = canvas.toDataURL('image/png')
  99. // const str = base64Image.replace('data:image/png;base64,', '')
  100. // // 文件对象数组
  101. // this.photoList.push(this.convertBlobToFile(this.convertBase64ToBlob(str), new Date().getTime()))
  102. // 图片路径数组
  103. this.srcList.push({
  104. src: base64Image,
  105. // src: this.str,
  106. name: new Date().getTime() + '.png',
  107. })
  108. },
  109. folderClick() {
  110. // 选择文件
  111. var input = document.createElement('input')
  112. input.type = 'file'
  113. input.accept = '.doc,.docx,.txt,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf'
  114. input.addEventListener('change', (e) => {
  115. // console.log(e.target.files, 'eeeeee')
  116. // 读取选择的文件
  117. this.fileList.push(e.target.files)
  118. this.srcList.push({
  119. src: null,
  120. name: 'files',
  121. })
  122. })
  123. // 触发点击事件,打开文件
  124. input.click()
  125. },
  126. pictureClick() {
  127. const self = this
  128. // 选择图片
  129. var input = document.createElement('input')
  130. input.type = 'file'
  131. input.accept = 'image/*'
  132. input.multiple = true
  133. // 点击事件处理函数
  134. input.addEventListener('change', function () {
  135. if (this.files && this.files[0]) {
  136. // 读取选择的图片文件
  137. var reader = new FileReader()
  138. reader.onload = (e) => {
  139. // 图片加载完成后,将图片URL赋值给input的src属性,即可显示图片
  140. // console.log(e.target.result, 'e.target.result')
  141. const type = this.files[0].type.split('/')[1]
  142. self.srcList.push({
  143. src: e.target.result,
  144. name: new Date().getTime() + '.' + type,
  145. })
  146. }
  147. reader.readAsDataURL(this.files[0])
  148. }
  149. })
  150. // 触发点击事件,打开图库
  151. input.click()
  152. },
  153. // 转换为blob格式
  154. convertBase64ToBlob(base64Str) {
  155. // 将base64字符串转为二进制数据
  156. const byteCharacters = atob(base64Str)
  157. // 创建Blob对象
  158. const blob = new Blob([byteCharacters], { type: 'application/octet-stream' })
  159. return blob
  160. },
  161. // 将blob转为file对象
  162. convertBlobToFile(blob, fileName) {
  163. // 创建File对象
  164. const type = fileName.split('.')[1]
  165. const file = new File([blob], fileName, { type: `image/${type}` })
  166. return file
  167. },
  168. delImg(name, index) {
  169. // 删除图片
  170. if (name === 'files') {
  171. this.fileList.splice(index, 1)
  172. }
  173. this.srcList.splice(index, 1)
  174. },
  175. upload() {
  176. // 上传按钮
  177. if (this.srcList.length > 0) {
  178. for (let i = 0; i < this.srcList.length; i++) {
  179. // 文件对象数组
  180. if (this.srcList[i].src === null) {
  181. continue
  182. }
  183. const type = this.srcList[i].name.split('.')[1]
  184. this.photoList.push(this.convertBlobToFile(this.convertBase64ToBlob(this.srcList[i].src.replace(`data:image/${type};base64,`, '')), new Date().getTime() + `.${type}`))
  185. }
  186. }
  187. const fileObj = []
  188. for (let i = 0; i < this.fileList.length; i++) {
  189. fileObj.push(this.fileList[i][0])
  190. }
  191. for (let i = 0; i < this.photoList.length; i++) {
  192. fileObj.push(this.photoList[i])
  193. }
  194. console.log(fileObj, 'fileObj')
  195. },
  196. },
  197. }
  198. </script>

css部分

  1. <style scoped lang="stylus">
  2. .views{
  3. width: 100vw;
  4. height: 100vh;
  5. background-color: #ccc;
  6. position: relative
  7.  
  8. .picture{
  9. position: absolute;
  10. left: 1rem;
  11. z-index: 9;
  12. bottom: 4rem;
  13. .icon-picture{
  14. font-size: 1rem;
  15. font-weight: bold;
  16. }
  17. }
  18. .folder{
  19. position: absolute;
  20. right: 1rem;
  21. z-index: 9;
  22. bottom: 4rem;
  23. .icon-folder{
  24. font-size: 1rem;
  25. font-weight: bold;
  26. }
  27. }
  28. .action{
  29. position: absolute;
  30. bottom: 20vh;
  31. left: 50%;
  32. margin-left: -35px;
  33. border-radius: 50px;
  34. border: 10px solid #ccc;
  35. box-shadow: 0 0 10px black;
  36. background-color: #fff;
  37. width: 70px;
  38. height: 70px;
  39. display: flex;
  40. justify-content: center;
  41. z-index :99
  42. }
  43. .bac {
  44. overflow-x: scroll;
  45. white-space: nowrap; /* 横向内容不换行 */
  46. align-items: flex-end; /* 图片索引在左下角 */
  47. }
  48.  
  49. .img_box {
  50. margin-top: 10px;
  51. position: relative;
  52. display: inline-block; /* img_box横向排列 */
  53. margin-left: 8px;
  54. margin-bottom: 8px;
  55. }
  56.  
  57. .icon-guanbi {
  58. position: absolute;
  59. top: -10px;
  60. right: -10px;
  61. width: 20px;
  62. height: 20px;
  63. font-size : 20px;
  64. color: #fff;
  65. color:red;
  66. border-radius: 50%;
  67. display: flex;
  68. align-items: center;
  69. justify-content: center;
  70. cursor: pointer;
  71. }
  72.  
  73. img {
  74. // width: 100px;
  75. height:15vh;
  76. margin-left: 8px
  77. }
  78.  
  79. span {
  80. position: absolute;
  81. bottom: 0;
  82. left: 0;
  83. margin-left: 4px;
  84. margin-bottom: 4px;
  85. color: #fff;
  86. background-color: #64e8ff;
  87. padding: 4px;
  88. font-size: 20px;
  89. border-radius: 2px 2px 2px 6px;
  90. }
  91. .btn{
  92. color: #fff;
  93. border: none;
  94. background: #029afc;
  95. height: 1rem;
  96. width: 1.5rem;
  97. position: absolute;
  98. right: 0;
  99. bottom: 1rem;
  100. z-index: 99;
  101. border-radius: 10px;
  102. line-height: 1rem;
  103. }
  104. }
  105. </style>

以上代码可以直接复制使用,留个关注吧。

h5移动端使用video实现拍照、上传文件对象、选择相册,做手机兼容。的更多相关文章

  1. android 拍照上传文件 原生定位

    最近公司需要一个android拍照上传和定位功能的的单一功能页面,一开始选择ionic cordova angular的一套H5框架,但是遇到和上传文件报错的问题,bug找了一天没找到原因,怀疑是io ...

  2. NSURLSession/NSURLConnection的上传文件方法(已做了更新)

    最好的学习方法就是 领悟 + 证悟. 此篇文章的理论基础主要是与HTTP网络通信协议相关.为集中精力,可以先把TCP/IP协议这些置之不理,也就是先只关注HTTP的请求和响应的结构.HTTP完整的原理 ...

  3. jquery.form.js mvc 上传文件 layer 选择框与等待效果

    HTML <form role="form" id="form1"> <div class="form-group"> ...

  4. JQuery Ajax 使用FormData上传文件对象

    FormData部分: 先new FormData对象 :let somedata = new FormData(),然后将数据添加进去,这里我们使用append()进行添加. 这里举一个上传头像的例 ...

  5. webAPP如何实现移动端拍照上传(Vue组件示例)?

    摘要:使用HTML5编写移动Web应用,主要是为了尝试一下“一套代码多处运行”,一个webapp几乎可以不加修改的运行在PC/Android/iOS等上面运行.但是写到现在觉得虽然这种方式弊大于利,不 ...

  6. 【Demo】HTML5 拍照上传

    本文主要讲解 手机浏览器 如何拍照 为什么会有这个需求 最近做一个项目要用到拍照然后上传照片,但是网页拍照一般都是用Flash做的,而我们主要是H5页面,如果在微信里面有权限就可以通过JSSDK调起摄 ...

  7. jquery上传文件控件Uploadify

    基于jquery的文件上传控件,支持ajax无刷新上传,多个文件同时上传,上传进行进度显示,删除已上传文件. 要求使用jquery1.4或以上版本,flash player 9.0.24以上. 有两个 ...

  8. WebUploader分片断点上传文件(二)

    写在前面: 这几天,有去研究一下WebUploader上传文件,前面的博客有记录下使用WebUploader简单上传文件的例子,今天就把分片断点上传的例子也记录下吧,在博客园中,也查看了一些资料,基本 ...

  9. WebUploader上传文件(一)

    写在前面: 文件上传方式很多的,对于大文件的上传,在本次项目中也有涉及,主要是用了分片断点上传大文件.所以就去了解了一下WebUploader,先从简单的上传文件开始吧~ 在代码中写注释,这样看的比较 ...

  10. 20-1 django上传文件和项目里上传头像如何查看

    一 普通上传方式 1 views def upload(request): if request.method == "POST": # print(request.POST) # ...

随机推荐

  1. 推荐一个高效解压缩工具 - XZ

    前情提要:前段时间搞某较大型项目的性能测试,使用的是Oracle数据库.压测阶段搞的业务基础数据和压测数据耗时耗力,想把数据导出,后续有类似项目,可以导入复用. 遇到问题:通过数据泵方式导出的数据库文 ...

  2. MongoDB索引操作和执行计划Explain()详解

    一.索引操作 说明,下面的内容举例时,以"dailyTrip"collection为例. 字段内容如下: { "_id" : ObjectId("63 ...

  3. Programming abstractions in C阅读笔记: p114-p117

    <Programming Abstractions in C>学习第48天,p114-p117,​总结如下: 一.技术总结 主要通过random number介绍了随机数的相关用法,int ...

  4. JavaScript动态更新数组

    1.数组的创建var arrayObj = new Array(); //创建一个数组var arrayObj = new Array([size]); //创建一个数组并指定长度,注意不是上限,是长 ...

  5. 小知识:将普通用户加入到docker组

    新的OCI实例,OS选择的是OEL7.9,初始环境是没有安装docker的,我们可以直接使用yum安装,之后启动docker服务: [opc@oci-001 ~]$ sudo yum install ...

  6. iperf 工具使用总结

    转载请注明出处: iperf是一个用于测量网络带宽的工具,可以通过客户端和服务器之间的数据传输来评估网络性能.下面详细介绍iperf的使用方法.常用命令和参数以及注意事项,并提供一些示例说明.在ipe ...

  7. GO 中的时间操作(time & dateparse)【GO 基础】

    〇.前言 日常开发过程中,对于时间的操作可谓是无处不在,但是想实现时间自由还是不简单的,多种时间格式容易混淆,那么本文将进行梳理,一起学习下. 官方提供的库是 time,功能很全面,本文也会详细介绍. ...

  8. 在C#中如何自定义配置上周和本周起始日来查询业务数据?

    作者:西瓜程序猿 主页传送门:https://www.cnblogs.com/kimiliucn 前言 在做某个报表管理功能时,有一个需求:需要根据自定义配置的[周起始日]来统计上周.本周的订单数据. ...

  9. Ds100p -「数据结构百题」1~10

    1.「一本通 4.6 例 1」营业额统计 原题来自:HNOI 2002 Tiger 最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger 拿出 ...

  10. Python 有趣的模块之pynupt——通过pynput控制鼠标和键盘

    写在前面 Python中有许多有趣和强大的模块,其中一个非常有趣的模块就是pynupt.pynupt是基于pynput模块的一个封装,用于控制鼠标和键盘.它可以实现自动化操作和游戏外挂等功能. 本文将 ...