CameraUpload.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <template>
  2. <div class="camera-upload">
  3. <div class="row">
  4. <svg-icon icon-class="paizhao" size="80" @click="openMedia" />
  5. <svg-icon icon-class="bendishangchuan" size="80" @click="selectFile" />
  6. </div>
  7. <div class="row">
  8. <RefreshRight color="#0a59f7" size="70px" @click="clickReset" />
  9. <Delete color="#ff4d4f" size="60px" @click="clickDeleteAll" />
  10. </div>
  11. <input v-show="false" id="fileInput" accept="image/*" type="file" />
  12. <el-dialog
  13. id="carmer-dialog"
  14. v-model="visible"
  15. :close-on-click-modal="false"
  16. :title="null"
  17. close-icon="null"
  18. width="840px"
  19. >
  20. <div v-loading="isLoading">
  21. <video
  22. id="video"
  23. :height="photoSize + 'px'"
  24. :width="photoSize + 'px'"
  25. autoplay="autoplay"
  26. ></video>
  27. <canvas
  28. v-show="false"
  29. id="canvas"
  30. :height="photoSize + 'px'"
  31. :width="photoSize + 'px'"
  32. ></canvas>
  33. <div class="bottom-btns">
  34. <el-button class="cancelBtn" @click="closeMedia">关闭</el-button>
  35. <el-button class="sureBtn" type="primary" @click="takePhoto"
  36. >拍照
  37. </el-button>
  38. </div>
  39. </div>
  40. </el-dialog>
  41. </div>
  42. </template>
  43. <script setup>
  44. import { uploadFileApi } from "@/api/file";
  45. import { Delete, RefreshRight } from "@element-plus/icons-vue";
  46. const isLoading = ref(false);
  47. let mediaStreamTrack = null; // 视频对象(全局)
  48. let video;
  49. let photoSize = 800;
  50. let realSize = 1000;
  51. const cameraEmit = defineEmits(["uploadFinish", "resetSelect", "deleteAll"]);
  52. const openMedia = async () => {
  53. visible.value = true;
  54. nextTick(() => {
  55. let constraints = {
  56. video: { width: photoSize, height: photoSize },
  57. audio: false,
  58. };
  59. //获得video摄像头
  60. video = document.getElementById("video");
  61. navigator.mediaDevices
  62. .getUserMedia(constraints)
  63. .then((mediaStream) => {
  64. console.log("mediaStream", mediaStream);
  65. // mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[1];
  66. mediaStreamTrack = mediaStream.getVideoTracks();
  67. video.srcObject = mediaStream;
  68. video.play();
  69. })
  70. .catch((error) => {
  71. console.log("Error: " + error);
  72. ElMessage.error("没有找到设备,或者没有权限");
  73. });
  74. });
  75. };
  76. function dataURItoBlob(base64Data) {
  77. var byteString;
  78. if (base64Data.split(",")[0].indexOf("base64") >= 0)
  79. byteString = atob(base64Data.split(",")[1]);
  80. else byteString = unescape(base64Data.split(",")[1]);
  81. var mimeString = base64Data.split(",")[0].split(":")[1].split(";")[0];
  82. var ia = new Uint8Array(byteString.length);
  83. for (var i = 0; i < byteString.length; i++) {
  84. ia[i] = byteString.charCodeAt(i);
  85. }
  86. return new Blob([ia], { type: mimeString });
  87. }
  88. // 拍照
  89. function takePhoto() {
  90. //获得Canvas对象
  91. let video = document.getElementById("video");
  92. let canvas = document.getElementById("canvas");
  93. canvas.width = realSize; // 实际渲染像素
  94. canvas.height = realSize; // 实际渲染像素
  95. let ctx = canvas.getContext("2d");
  96. ctx.imageSmoothingEnabled = false; //关闭抗锯齿
  97. ctx.drawImage(video, 0, 0, realSize, realSize);
  98. isLoading.value = true;
  99. // toDataURL --- 可传入'image/png'---默认, 'image/jpeg'
  100. let base64Data = document.getElementById("canvas").toDataURL();
  101. let blob = dataURItoBlob(base64Data);
  102. let file = new File([blob], `photo.png`, { type: "image/png" });
  103. uploadFileApi(file).then((res) => {
  104. cameraEmit("uploadFinish", res.data.fileUrl);
  105. isLoading.value = false;
  106. });
  107. }
  108. // 关闭摄像头
  109. function closeMedia() {
  110. let stream = document.getElementById("video").srcObject;
  111. if (stream) {
  112. let tracks = stream.getTracks();
  113. tracks?.forEach(function (track) {
  114. track.stop();
  115. });
  116. }
  117. document.getElementById("video").srcObject = null;
  118. visible.value = false;
  119. }
  120. const visible = ref(false);
  121. const selectFile = () => {
  122. let input = document.getElementById("fileInput");
  123. input.click();
  124. input.onchange = function () {
  125. let file = this.files[0];
  126. console.log(file);
  127. if (file) {
  128. uploadFileApi(file).then((res) => {
  129. cameraEmit("uploadFinish", res.data.fileUrl);
  130. });
  131. }
  132. };
  133. };
  134. const clickReset = () => {
  135. cameraEmit("resetSelect");
  136. };
  137. const clickDeleteAll = () => {
  138. cameraEmit("deleteAll");
  139. };
  140. </script>
  141. <style lang="scss" scoped>
  142. .camera-upload {
  143. display: flex;
  144. flex-direction: column;
  145. justify-content: space-evenly;
  146. align-items: center;
  147. width: 292px;
  148. height: 292px;
  149. border-radius: 16px 16px 16px 16px;
  150. border: 2px dashed rgba(0, 0, 0, 0.2);
  151. .row {
  152. width: 100%;
  153. display: flex;
  154. justify-content: space-evenly;
  155. align-items: center;
  156. }
  157. .icon-back:nth-child(1) {
  158. margin-right: 58px;
  159. }
  160. }
  161. </style>
  162. <style lang="scss" scoped>
  163. #carmer-dialog {
  164. background: #f1f3f5;
  165. box-shadow: 0px 0px 80px 10px rgba(0, 0, 0, 0.25);
  166. border-radius: 16px 16px 16px 16px;
  167. width: 924px;
  168. max-height: 80vh;
  169. .top-title {
  170. width: 100%;
  171. height: 38px;
  172. font-weight: 500;
  173. font-size: 38px;
  174. color: rgba(0, 0, 0, 0.9);
  175. text-align: center;
  176. }
  177. .center-content {
  178. margin-top: 24px;
  179. width: 100%;
  180. //height: 200px;
  181. display: flex;
  182. justify-content: center;
  183. align-items: center;
  184. font-size: 24px;
  185. color: rgba(0, 0, 0, 0.9);
  186. }
  187. .body {
  188. width: calc(100% - 240px);
  189. max-height: calc(50vh - 80px);
  190. margin: 0 auto;
  191. overflow-y: auto;
  192. .box {
  193. width: 100%;
  194. height: 100px;
  195. }
  196. }
  197. .bottom-btns {
  198. display: flex;
  199. justify-content: center;
  200. margin-top: 20px;
  201. margin-bottom: 20px;
  202. .button {
  203. margin-right: 20px;
  204. }
  205. .cancelBtn {
  206. width: 292px;
  207. height: 80px;
  208. background: rgba(0, 0, 0, 0.06);
  209. border-radius: 76px 76px 76px 76px;
  210. }
  211. .sureBtn {
  212. width: 292px;
  213. height: 80px;
  214. background: #0a59f7;
  215. border-radius: 76px 76px 76px 76px;
  216. }
  217. }
  218. }
  219. </style>