easy-scancode.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <template>
  2. <view class="easy-scancode" v-if="show">
  3. <view id="reader"></view>
  4. <!-- 扫码提示区域 -->
  5. <view class="scan-tip-overlay">
  6. <view class="scan-tip"> 请将二维码放入框内 </view>
  7. </view>
  8. </view>
  9. </template>
  10. <script>
  11. import { Html5Qrcode } from "html5-qrcode";
  12. export default {
  13. name: "easy-scancode",
  14. data() {
  15. return {
  16. options: {
  17. success: () => {},
  18. fail: () => {},
  19. },
  20. show: false,
  21. html5QrCode: null,
  22. };
  23. },
  24. methods: {
  25. start(options) {
  26. this.options = options;
  27. // #ifdef H5
  28. // 检查摄像头权限
  29. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  30. navigator.mediaDevices
  31. .getUserMedia({
  32. video: true,
  33. })
  34. .then((stream) => {
  35. // 获得权限后关闭流
  36. stream.getTracks().forEach((track) => track.stop());
  37. this.show = true;
  38. // 有权限后调用扫码
  39. this.getCameras();
  40. })
  41. .catch((err) => {
  42. // 用户拒绝了权限
  43. uni.showToast({
  44. title: "请允许使用摄像头权限!",
  45. icon: "none",
  46. });
  47. this.options.fail("未获得摄像头权限");
  48. });
  49. } else {
  50. uni.showToast({
  51. title: "您的浏览器不支持访问摄像头",
  52. icon: "none",
  53. });
  54. this.options.fail("浏览器不支持访问摄像头");
  55. }
  56. return;
  57. // #endif
  58. // 其他环境
  59. uni.scanCode({
  60. onlyFromCamera: true,
  61. success: (res) => {
  62. this.options.success(res.result, res);
  63. },
  64. fail: (rej) => {
  65. this.options.fail("扫码失败");
  66. },
  67. });
  68. },
  69. stop() {
  70. this.show = false;
  71. let that = this;
  72. this.html5QrCode
  73. .stop()
  74. .then((ignore) => {
  75. // 二维码扫描已停止
  76. console.log("二维码扫描已停止");
  77. })
  78. .catch((err) => {
  79. // 停止失败,处理错误
  80. console.log("无法停止扫描");
  81. });
  82. },
  83. getCameras() {
  84. let that = this;
  85. Html5Qrcode.getCameras()
  86. .then((devices) => {
  87. // console.log(devices)
  88. that.html5QrCode = new Html5Qrcode("reader");
  89. let windowInfo = uni.getWindowInfo();
  90. console.log(windowInfo);
  91. const width = windowInfo.windowWidth;
  92. const height = windowInfo.windowHeight;
  93. // 横屏
  94. const aspectRatio = width / height;
  95. // 竖屏
  96. const reverseAspectRatio = height / width;
  97. const mobileAspectRatio =
  98. reverseAspectRatio > 1.5
  99. ? reverseAspectRatio + (reverseAspectRatio * 12) / 100
  100. : reverseAspectRatio;
  101. if (devices && devices.length) {
  102. let cameraId = "";
  103. if (devices.length > 1) {
  104. cameraId = devices[1].id;
  105. } else {
  106. cameraId = devices[0].id;
  107. }
  108. // 计算二维码扫描框大小 - 使用宽度/高度中较小的值来确保方框适合屏幕
  109. // 并在扫描区域周围留出一些边距
  110. const qrboxSize = Math.min(width, height) * 0.7;
  111. console.log(qrboxSize);
  112. // 使用这个来开始扫描
  113. that.html5QrCode
  114. .start(
  115. {
  116. facingMode: "environment",
  117. },
  118. {
  119. fps: 10, // 可选,二维码扫描的每秒帧数
  120. aspectRatio: aspectRatio,
  121. qrbox: { width: qrboxSize, height: qrboxSize }, // 可选,如果你想要边界框UI
  122. videoConstraints: {
  123. facingMode: "environment",
  124. aspectRatio: width < 600 ? mobileAspectRatio : aspectRatio,
  125. },
  126. },
  127. (decodedText, decodedResult) => {
  128. // 当码被读取时执行操作
  129. console.log("decodedText", decodedText);
  130. that.options.success(decodedText, decodedResult);
  131. that.stop();
  132. },
  133. (errorMessage) => {
  134. // 解析错误,忽略它
  135. // console.log('解析错误,忽略它', errorMessage)
  136. }
  137. )
  138. .catch((err) => {
  139. // 启动失败,处理它
  140. console.log("启动失败,需要处理");
  141. that.options.fail(err);
  142. this.show = false;
  143. });
  144. }
  145. })
  146. .catch((err) => {
  147. // 处理错误
  148. console.log(err);
  149. that.options.fail(err);
  150. this.show = false;
  151. });
  152. },
  153. },
  154. };
  155. </script>
  156. <style lang="scss" scoped>
  157. .easy-scancode {
  158. width: 100vw;
  159. height: 100vh;
  160. position: fixed;
  161. z-index: 1000;
  162. top: 0;
  163. left: 0;
  164. background-color: #000;
  165. }
  166. #reader {
  167. top: 50%;
  168. left: 0;
  169. transform: translateY(-50%);
  170. }
  171. .scan-tip-overlay {
  172. position: absolute;
  173. top: 0;
  174. left: 0;
  175. width: 100%;
  176. height: 100%;
  177. z-index: 10000;
  178. pointer-events: none; /* 不阻挡摄像头画面 */
  179. }
  180. .scan-tip {
  181. position: absolute;
  182. top: 74%;
  183. left: 50%;
  184. transform: translateX(-50%);
  185. color: #fff;
  186. font-size: 16px;
  187. text-align: center;
  188. z-index: 10001;
  189. background-color: rgba(0, 0, 0, 0.5);
  190. padding: 10px 20px;
  191. border-radius: 5px;
  192. }
  193. </style>