face.html 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>H5摄像头(新版浏览器https)(兼容老版浏览器)</title>
  6. <script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js">
  7. </script>
  8. </head>
  9. <body style="background-color: blanchedalmond;display: flex;flex-direction: column;">
  10. <!-- 说明:将网页更改为https访问才行 否者报错:NotSupportedError Only secure origins are allowed (see: https://goo.gl/Y0ZkNV). -->
  11. <!-- video用于显示媒体设备的视频流,自动播放;属性:https://zhuanlan.zhihu.com/p/535917105 -->
  12. <video id="video" autoplay webkit-playsinline="true" playsinline="true"></video>
  13. <!-- <img class="face-mark" src="../images/face-mark.png" alt="" /> -->
  14. <!-- transform: rotateY(180deg); 镜像解决 -->
  15. <!-- 可以通过画布canvas渲染,获取使用默认的video也行 -->
  16. <!-- <canvas id="canvas" width="500" height="400" style="transform: rotateY(180deg);"></canvas> -->
  17. <div class="box">
  18. <span class="text">请正对摄像头</span>
  19. <canvas class="canvas" id="canvas"></canvas>
  20. </div>
  21. <div class="wenziBox">
  22. <div class="zhuyi">
  23. <p class="line"></p>
  24. <span>注意事项</span>
  25. <p class="line"></p>
  26. </div>
  27. <div class="imgBox">
  28. <div class="item">
  29. <img class="buttonImg" src="../images/face.png" alt="" />
  30. <span class="span">正脸</span>
  31. </div>
  32. <div class="item">
  33. <img class="buttonImg" src="../images/deng.png" alt="" />
  34. <span class="span">光线</span>
  35. </div>
  36. <div class="item">
  37. <img class="buttonImg" src="../images/qingxi.png" alt="" />
  38. <span class="span">清晰</span>
  39. </div>
  40. </div>
  41. </div>
  42. <div class="buttonBox">
  43. <!-- 拍照按钮 -->
  44. <img class="buttonImg" id="back" src="../images/back.png" />
  45. <img class="buttonImg" id="capture" src="../images/pai.png" />
  46. <img class="buttonImg" id="fanzhuan" src="../images/fan.png" />
  47. </div>
  48. <div id="popup">
  49. <img id="popupImg" alt="" src="">
  50. <div class="popupBox">
  51. <button id="closeBtn">取消</button>
  52. <button id="okBtn">确认</button>
  53. </div>
  54. </div>
  55. <script type="text/javascript">
  56. // 获取弹窗元素
  57. var popup = document.getElementById('popup');
  58. var popupImg = document.getElementById('popupImg');
  59. popup.style.display = 'none';
  60. // 获取关闭按钮元素
  61. var closeBtn = document.getElementById('closeBtn');
  62. var okBtn = document.getElementById('okBtn');
  63. var video = document.getElementById("video");
  64. var canvas = document.getElementById("canvas");
  65. var context = canvas.getContext("2d");
  66. var v_t_w = 200,
  67. v_t_h = 200;
  68. var mode = 'environment';
  69. var mediaStreamTrack;
  70. document.addEventListener('UniAppJSBridgeReady', function() {
  71. okBtn.addEventListener('click', function() {
  72. uni.postMessage({
  73. data: {
  74. faceBase64: popupImg.src
  75. }
  76. });
  77. window.history.back(-1)
  78. });
  79. });
  80. // 弹出弹窗
  81. function openPopup() {
  82. popup.style.display = 'block';
  83. }
  84. // 关闭弹窗
  85. function closePopup() {
  86. takePhoto();
  87. popup.style.display = 'none';
  88. }
  89. // 绑定关闭按钮的点击事件
  90. closeBtn.addEventListener('click', closePopup);
  91. // 访问用户媒体设备的兼容方法
  92. function getUserMedia(constrains, successFun, errorFun) {
  93. //like12 modified,20210628,bug,navigator.mediaDevices为空会导致后面卡死
  94. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  95. //最新标准API(新版浏览器https)
  96. navigator.mediaDevices.getUserMedia(constrains).then(successFun).catch(errorFun);
  97. } else if (navigator.webkitGetUserMedia) {
  98. //like12 modified,20210628,不是这种调用方法 应该为后者
  99. //webkit内核浏览器(老版浏览器)
  100. //navigator.webkitGetUserMedia(constrains).then(successFun).catch(errorFun);
  101. navigator.webkitGetUserMedia({
  102. "video": true
  103. }, successFun, errorFun);
  104. } else if (navigator.mozGetUserMedia) {
  105. //Firefox浏览器
  106. navagator.mozGetUserMedia(constrains).then(successFun).catch(errorFun);
  107. } else if (navigator.getUserMedia) {
  108. //旧版API
  109. navigator.getUserMedia(constrains).then(successFun).catch(errorFun);
  110. }
  111. }
  112. // 成功的回调函数
  113. function successFun(stream) {
  114. //like12 modifed,20210618,Chrome升级后,新版本不再支持该用法
  115. //摄像头视频流显示报错Failed to execute 'createObjectURL' on 'URL'
  116. //研究即时通信的过程中需要调用摄像头,发现报错,原来是谷歌弃用了这个方法,根据官方提示修改即可
  117. //所以原先的代码:video.src = URL.createObjectURL(stream);
  118. //需要被修改为:video.srcObject = stream;
  119. //(新版浏览器https)
  120. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  121. video.srcObject = stream;
  122. }
  123. //(老版浏览器)
  124. else {
  125. //兼容webkit内核浏览器
  126. var CompatibleURL = window.URL || window.webkitURL;
  127. //将视频流设置为video元素的源
  128. //此处的代码将会报错 解决的办法是将video的srcObject属性指向stream即可
  129. video.src = CompatibleURL.createObjectURL(stream);
  130. }
  131. mediaStreamTrack = stream.getTracks()[0];
  132. // 播放视频
  133. video.play();
  134. // 可以通过画布canvas渲染,获取使用默认的video也行
  135. setInterval(function() {
  136. canvas.width = v_t_w;
  137. canvas.height = v_t_h;
  138. // context.scale(-1, 1);
  139. context.drawImage(video, 0, 0, v_t_w, v_t_h);
  140. // canvas.width = v_t_w;
  141. // canvas.height = v_t_h;
  142. // context.strokeRect(60, 60, 100, 100);
  143. // context.beginPath();
  144. // context.arc(v_t_w / 2, v_t_w / 2, v_t_w / 2, 0, Math.PI * 2, true); // 绘制
  145. // context.closePath(); // 结束路径绘制
  146. // context.clip(); // 根据路径切割画布内容
  147. // context.drawImage(video, 0, 0, v_t_w, v_t_h);
  148. // context.beginPath();
  149. // context.arc(centerX, centerY, 70, 0, Math.PI * 2, false); // 绘制
  150. // context.stroke();
  151. }, 10);
  152. }
  153. // 异常的回调函数
  154. function errorFun(error) {
  155. console.log("访问用户媒体设备失败:", error.name, error.message);
  156. alert("访问用户媒体设备失败:" + error.name + " " + error.message);
  157. }
  158. document.getElementById('fanzhuan').addEventListener('click', function() {
  159. changeMode();
  160. })
  161. document.getElementById('back').addEventListener('click', function() {
  162. window.history.back(-1)
  163. })
  164. // 注册拍照按钮的单击事件-截图
  165. document.getElementById("capture").addEventListener("click", function() {
  166. canvas.width = v_t_w;
  167. canvas.height = v_t_h;
  168. // context.strokeRect(60, 60, 100, 100);
  169. // context.beginPath();
  170. // context.arc(v_t_w / 2, v_t_w / 2, v_t_w / 2, 0, Math.PI * 2, true); // 绘制
  171. // context.closePath(); // 结束路径绘制
  172. // context.clip(); // 根据路径切割画布内容
  173. context.setTransform(-1, 0, 0, 1, v_t_w, 0);
  174. context.drawImage(video, 0, 0, v_t_w, v_t_h);
  175. mediaStreamTrack && mediaStreamTrack.stop();
  176. var base64Img = canvas.toDataURL('image/jpg');
  177. //var base64 = canvas.toDataURL('image/jpeg',0.5);// 图片质量0.5
  178. popupImg.src = base64Img;
  179. // console.log(base64Img);
  180. openPopup()
  181. });
  182. function takePhoto() {
  183. // 开始调用摄像头
  184. //like12 modified,20210628,bug,navigator.mediaDevices为空会导致后面卡死
  185. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia ||
  186. navigator.getUserMedia ||
  187. navigator.webkitGetUserMedia ||
  188. navigator.mozGetUserMedia) {
  189. // 调用用户媒体设备,访问摄像头
  190. getUserMedia({
  191. video: {
  192. width: v_t_w,
  193. height: v_t_h,
  194. facingMode: mode //设置使用后置摄像头,user为前置摄像头
  195. },
  196. }, successFun, errorFun);
  197. } else {
  198. alert("你的浏览器不支持访问用户媒体设备");
  199. }
  200. }
  201. takePhoto();
  202. function changeMode() {
  203. console.log("翻转摄像头");
  204. mode = mode === 'user' ? 'environment' : 'user';
  205. mediaStreamTrack.stop();
  206. takePhoto();
  207. }
  208. </script>
  209. </body>
  210. <style scoped>
  211. video {
  212. /* transform: rotateY(180deg); */
  213. display: none;
  214. transform: scaleX(-1);
  215. -o-transform: scaleX(-1);
  216. -webkit-transform: scaleX(-1);
  217. filter: fliph;
  218. -ms-filter: fliph;
  219. }
  220. .face-mark {
  221. position: absolute;
  222. top: 5%;
  223. left: 50%;
  224. transform: translateX(-50%);
  225. width: 80%;
  226. }
  227. .box {
  228. margin: 6vh auto 12vh;
  229. display: flex;
  230. flex-direction: column;
  231. text-align: center;
  232. .canvas {
  233. margin-top: 6vh;
  234. border-radius: 50%;
  235. transform: rotateY(180deg)
  236. }
  237. .text {
  238. /* position: absolute; */
  239. /* align-items: center; */
  240. }
  241. }
  242. .wenziBox {
  243. .zhuyi {
  244. margin-top: 15px;
  245. display: flex;
  246. flex-direction: row;
  247. justify-content: space-around;
  248. .line {
  249. border-bottom: 1px solid black;
  250. width: 20vw;
  251. }
  252. }
  253. .imgBox {
  254. display: flex;
  255. flex-direction: row;
  256. justify-content: space-around;
  257. margin-top: 2vh;
  258. .item {
  259. display: flex;
  260. flex-direction: column;
  261. .span {
  262. text-align: center;
  263. }
  264. }
  265. }
  266. }
  267. .buttonBox {
  268. display: flex;
  269. justify-content: space-around;
  270. margin-top: 10vh;
  271. }
  272. .buttonImg {
  273. width: 15vw;
  274. object-fit: contain;
  275. margin-bottom: 1vh;
  276. }
  277. .noneImg {
  278. display: none;
  279. }
  280. #popup {
  281. display: flex;
  282. justify-content: center;
  283. position: fixed;
  284. width: 100%;
  285. top: 30%;
  286. left: 50%;
  287. transform: translate(-50%, -50%);
  288. background: #fff;
  289. padding: 20px;
  290. border-radius: 5px;
  291. box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  292. z-index: 9999;
  293. }
  294. #popup h2 {
  295. margin-top: 0;
  296. }
  297. #closeBtn {
  298. width: 30vw;
  299. height: 4vh;
  300. border-radius: 16px;
  301. }
  302. #okBtn {
  303. width: 30vw;
  304. height: 4vh;
  305. border-radius: 16px;
  306. }
  307. #popupImg {
  308. margin-left: 25vw;
  309. }
  310. .popupBox {
  311. margin-top: 2vh;
  312. width: 100%;
  313. display: flex;
  314. justify-content: space-around;
  315. }
  316. </style>
  317. </html>