index.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <template>
  2. <div id="">
  3. <el-dialog
  4. width="800px"
  5. class="take-photo"
  6. :visible.sync="takePhotoModal"
  7. :close-on-click-modal="false"
  8. :close-on-press-escape="false"
  9. :show-close="false"
  10. >
  11. <div class="take-photo__content">
  12. <!-- <div class="take-photo__close" @click="takePhotoModal = false">X</div> -->
  13. <div class="take-photo__header">人脸验证</div>
  14. <div class="take-photo__body clearfix">
  15. <div class="left-box">
  16. <div class="title">重要提示:</div>
  17. <div class="content">
  18. <p>1、请保证摄像头正对自己,避免头像偏左或者偏右。</p>
  19. <p>
  20. 2、请保证拍照环境光线充足(照片太暗或曝光会降低验证通过率)。
  21. </p>
  22. <p>
  23. 3、请保证整个头像在人脸识别区域内,脸部无遮挡装饰物(佩戴眼镜会降低通过率)。
  24. </p>
  25. <p>
  26. 4、如果下面视频中出现黑屏,摄像头可能被其他进程占用,请关闭其他调用摄像头的程序,重新刷新当前页面重新拍照识别。
  27. </p>
  28. </div>
  29. </div>
  30. <div class="right-box">
  31. <img v-show="!isTaking" :src="faceUrl" alt="" />
  32. <video v-show="isTaking" id="video"></video>
  33. <div v-show="isTaking" class="mask"></div>
  34. </div>
  35. </div>
  36. <div class="take-photo__footer">
  37. <el-button
  38. type="primary"
  39. v-if="isTaking"
  40. class="take"
  41. :disabled="!successOpen"
  42. @click="onPhoto"
  43. >拍照</el-button
  44. >
  45. <el-button
  46. type="primary"
  47. v-if="!isTaking"
  48. class="take"
  49. :loading="loading"
  50. @click="reTake"
  51. >重拍</el-button
  52. >
  53. <el-button
  54. type="primary"
  55. v-if="!isTaking"
  56. :loading="loading"
  57. class="take"
  58. @click="takeOk"
  59. >确认</el-button
  60. >
  61. </div>
  62. </div>
  63. </el-dialog>
  64. </div>
  65. </template>
  66. <script>
  67. export default {
  68. data() {
  69. return {
  70. successOpen:false,//是否授权拍照
  71. takePhotoModal: false,
  72. isTaking: true,
  73. loading: false,
  74. faceUrl: "",
  75. photoBadStatus:false,//摄像头无法使用触发true
  76. };
  77. },
  78. methods: {
  79. //拍照
  80. openPhoto() {
  81. var polyvPlayerContext = this.player;
  82. if (polyvPlayerContext) {
  83. this.isFullScreen();
  84. }
  85. this.successOpen = false
  86. this.faceUrl = "";
  87. this.loading = false;
  88. this.isTaking = true;
  89. this.takePhotoModal = true;
  90. this.$nextTick(() => {
  91. if (
  92. (window.navigator.mediaDevices &&
  93. window.navigator.mediaDevices.getUserMedia) ||
  94. window.navigator.getUserMedia ||
  95. window.navigator.webkitGetUserMedia ||
  96. window.navigator.mozGetUserMedia
  97. ) {
  98. // 调用用户媒体设备, 访问摄像头
  99. this.getUserMedia(
  100. {
  101. video: {
  102. width: 400,
  103. height: 400
  104. }
  105. },
  106. this.photographSuccess,
  107. this.photographError
  108. );
  109. } else {
  110. this.photographError();
  111. }
  112. });
  113. },
  114. //成功读取摄像头
  115. photographSuccess(stream) {
  116. // 兼容webkit核心浏览器
  117. // --虚拟摄像头
  118. if (this.isVirtualCamera(stream)) {
  119. return;
  120. }
  121. this.successOpen = true
  122. this.$nextTick(() => {
  123. const video = document.getElementById("video");
  124. // 将视频流设置为video元素的源
  125. video.srcObject = stream;
  126. video.play().catch(() => {});
  127. });
  128. },
  129. //未读取到摄像头 兼容webkit核心浏览器
  130. isVirtualCamera(stream) {
  131. const list = [
  132. "VCam",
  133. "ManyCam",
  134. "OBS",
  135. "ClassInCam",
  136. "Ev",
  137. "Video2Webcam"
  138. ];
  139. let isT = list.some(e => {
  140. return stream.getTracks()[0].label.indexOf(e) != -1;
  141. });
  142. if (isT) {
  143. this.photoBadStatus = true; //摄像头无法使用触发true
  144. this.$confirm("检测到你使用虚拟摄像头,无法继续学习。", "提示", {
  145. confirmButtonText: "返回",
  146. showConfirmButton: true,
  147. closeOnClickModal: false,
  148. showCancelButton: false,
  149. closeOnPressEscape: false,
  150. distinguishCancelAndClose: false,
  151. showClose: false
  152. }).then(() => {
  153. this.$router.go(-1);
  154. });
  155. }
  156. return isT;
  157. },
  158. //未读取到摄像头
  159. photographError(err) {
  160. this.photoBadStatus = true; //摄像头无法使用触发true
  161. this.$confirm(
  162. "课程学习需要开启摄像头进行拍照,经检测您的设备无摄像头可使用,请检测环境是否支持。",
  163. "提示",
  164. {
  165. confirmButtonText: "返回",
  166. showConfirmButton: true,
  167. closeOnClickModal: false,
  168. showCancelButton: false,
  169. closeOnPressEscape: false,
  170. distinguishCancelAndClose: false,
  171. showClose: false
  172. }
  173. ).then(() => {
  174. this.$router.back(-1);
  175. });
  176. },
  177. // 调用用户媒体设备, 访问摄像头
  178. getUserMedia(constraints, success, error) {
  179. if (window.navigator.mediaDevices.getUserMedia) {
  180. // 最新的标准API
  181. window.navigator.mediaDevices
  182. .getUserMedia(constraints)
  183. .then(success)
  184. .catch(error);
  185. } else if (window.navigator.webkitGetUserMedia) {
  186. // webkit核心浏览器
  187. window.navigator.webkitGetUserMedia(constraints, success, error);
  188. } else if (window.navigator.mozGetUserMedia) {
  189. // firfox浏览器
  190. window.navigator.mozGetUserMedia(constraints, success, error);
  191. } else if (window.navigator.getUserMedia) {
  192. // 旧版API
  193. window.navigator.getUserMedia(constraints, success, error);
  194. }
  195. },
  196. //判断是全屏则退出全屏
  197. isFullScreen() {
  198. if (!!(document.webkitIsFullScreen || this.fullele())) {
  199. try {
  200. var de = document;
  201. if (de.exitFullscreen) {
  202. de.exitFullscreen();
  203. } else if (de.mozCancelFullScreen) {
  204. de.mozCancelFullScreen();
  205. } else if (de.webkitCancelFullScreen) {
  206. de.webkitCancelFullScreen();
  207. }
  208. } catch (err) {}
  209. }
  210. },
  211. fullele() {
  212. return (
  213. document.fullscreenElement ||
  214. document.webkitFullscreenElement ||
  215. document.msFullscreenElement ||
  216. document.mozFullScreenElement ||
  217. null
  218. );
  219. },
  220. //拍照
  221. onPhoto() {
  222. const canvas = document.createElement("canvas");
  223. canvas.width = 400;
  224. canvas.height = 400;
  225. const context = canvas.getContext("2d");
  226. const video = document.getElementById("video");
  227. context.drawImage(video, 0, 0, 400, 400);
  228. this.faceUrl = canvas.toDataURL("image/png");
  229. this.isTaking = false;
  230. },
  231. //重拍-重置拍照
  232. reTake() {
  233. this.faceUrl = "";
  234. this.isTaking = true;
  235. this.getUserMedia({
  236. video: {
  237. width: 400,
  238. height: 400
  239. }
  240. });
  241. },
  242. //确认拍照
  243. async takeOk() {
  244. this.loading = true;
  245. this.$emit("returnParameter", this.faceUrl);
  246. this.takePhotoModal = false;
  247. }
  248. }
  249. };
  250. </script>
  251. <style lang="scss" scoped>
  252. .take-photo {
  253. /deep/ .el-dialog__header {
  254. display: none;
  255. }
  256. /deep/ .el-dialog__body {
  257. padding: 0;
  258. overflow: unset;
  259. }
  260. &__close {
  261. cursor: pointer;
  262. position: absolute;
  263. right: 0;
  264. top: -28px;
  265. width: 24px;
  266. height: 24px;
  267. line-height: 24px;
  268. text-align: center;
  269. color: #eee;
  270. border: 1px solid #eee;
  271. border-radius: 50%;
  272. }
  273. &__header {
  274. height: 40px;
  275. border-bottom: 1px solid #eee;
  276. line-height: 40px;
  277. font-size: 16px;
  278. font-family: Microsoft YaHei;
  279. font-weight: bold;
  280. color: #333333;
  281. padding-left: 24px;
  282. }
  283. &__body {
  284. // height: 400px;
  285. padding: 40px 24px;
  286. .left-box {
  287. width: 336px;
  288. float: left;
  289. .title {
  290. font-size: 16px;
  291. font-family: Microsoft YaHei;
  292. font-weight: bold;
  293. color: #ff3b30;
  294. line-height: 24px;
  295. }
  296. .content {
  297. font-size: 14px;
  298. font-family: Microsoft YaHei;
  299. font-weight: 400;
  300. color: #333333;
  301. line-height: 28px;
  302. margin-top: 32px;
  303. }
  304. }
  305. .right-box {
  306. float: right;
  307. width: 400px;
  308. height: 400px;
  309. position: relative;
  310. overflow: hidden;
  311. video {
  312. width: 100%;
  313. height: 100%;
  314. }
  315. .mask {
  316. width: 55%;
  317. height: 200px;
  318. position: absolute;
  319. top: 0;
  320. left: 0;
  321. right: 0;
  322. bottom: 0;
  323. margin: 30px auto 0;
  324. box-shadow: 0 0 0 2000px rgba(0, 0, 0, 0.4);
  325. }
  326. }
  327. }
  328. &__footer {
  329. height: 90px;
  330. border-top: 1px solid #eee;
  331. text-align: center;
  332. .take {
  333. display: inline-block;
  334. width: 200px;
  335. height: 40px;
  336. padding: 0;
  337. border-radius: 20px;
  338. text-align: center;
  339. line-height: 40px;
  340. margin: 24px auto;
  341. }
  342. }
  343. }
  344. </style>