sweepPage_html5_qrcode.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <template>
  2. <div class="sweep" v-if="show">
  3. <div id="reader"></div>
  4. <div class="ku-scanner">
  5. <div class="ku-scanner-content">
  6. <div class="ku-scanner-tooltip">
  7. 将二维码/条码放入框内,即自动扫描
  8. {{ msg }}
  9. </div>
  10. <div class="ku-scanner-section">
  11. <div class="ku-scanner-section-animation-line"></div>
  12. <div class="ku-scanner-section-angle"></div>
  13. </div>
  14. </div>
  15. </div>
  16. <!-- <QrcodeStream
  17. class="ku-scanner"
  18. ref="QrcodeStreamRef"
  19. v-if="show"
  20. :constraints="{ facingMode: { ideal: 'environment' } }"
  21. :camera="camera"
  22. :torch="torchActive"
  23. :zoom="1"
  24. :key="cameraKey"
  25. @decode="onDecode"
  26. @init="onInit"
  27. >
  28. <div class="ku-scanner-content">
  29. <div class="ku-scanner-tooltip">
  30. 将二维码/条码放入框内,即自动扫描
  31. </div>
  32. <div class="ku-scanner-section">
  33. <div class="ku-scanner-section-animation-line"></div>
  34. <div class="ku-scanner-section-angle"></div>
  35. </div>
  36. </div>
  37. </QrcodeStream> -->
  38. <span class="closeBtn" @click="close">
  39. <svg
  40. t="1733477770331"
  41. class="icon"
  42. viewBox="0 0 1024 1024"
  43. version="1.1"
  44. xmlns="http://www.w3.org/2000/svg"
  45. p-id="4265"
  46. width="64"
  47. height="64"
  48. >
  49. <path
  50. d="M623.807 528.693a113.756 113.756 0 0 0-0.139-33.126l252.787-252.692c26.245-26.236 26.245-68.772 0-95.006-26.248-26.236-68.798-26.236-95.043 0L528.281 400.902c-5.296-0.762-10.708-1.164-16.216-1.164s-10.92 0.402-16.216 1.164l-253.13-253.031c-26.245-26.236-68.796-26.236-95.043 0-26.245 26.234-26.245 68.77 0 95.006l252.789 252.692a113.726 113.726 0 0 0-0.14 33.124L147.676 781.247c-26.245 26.236-26.245 68.77 0 95.006 26.248 26.236 68.798 26.236 95.043 0L494.85 624.218c5.613 0.859 11.362 1.305 17.215 1.305s11.602-0.446 17.215-1.305l252.13 252.035c26.245 26.236 68.796 26.236 95.043 0 26.245-26.236 26.245-68.77 0-95.006L623.807 528.693z"
  51. fill="#000000"
  52. p-id="4266"
  53. ></path>
  54. <path
  55. d="M512.063 444.326c37.407 0 67.73 30.327 67.73 67.736 0 37.41-30.323 67.735-67.73 67.735-37.405 0-67.728-30.325-67.728-67.735 0.001-37.408 30.323-67.736 67.728-67.736z"
  56. fill="#000000"
  57. p-id="4267"
  58. ></path>
  59. </svg>
  60. </span>
  61. </div>
  62. </template>
  63. <script>
  64. // import { Html5QrcodeScanner } from 'html5-qrcode'
  65. import { Html5Qrcode } from 'html5-qrcode'
  66. export default {
  67. data() {
  68. return {
  69. show: false,
  70. cameras: null,
  71. camerasList: [],
  72. msg: ''
  73. // constraints:{
  74. // width:300,
  75. // height:300
  76. // }
  77. }
  78. },
  79. methods: {
  80. open() {
  81. this.show = true
  82. this.$nextTick(() => {
  83. this.startCameras()
  84. // this.getCamerasList()
  85. })
  86. // setTimeout(()=>{
  87. // this.$emit('success', "功能测试2503261429")
  88. // },1000)
  89. },
  90. getCamerasList() {
  91. Html5Qrcode.getCameras()
  92. .then(devices => {
  93. /**
  94. * devices would be an array of objects of type:
  95. * { id: "id", label: "label" }
  96. */
  97. if (devices && devices.length) {
  98. // var cameraId = devices[0].id
  99. this.camerasList = devices
  100. this.$message.info(JSON.stringify(devices))
  101. this.startCameras()
  102. // .. use this to start scanning.
  103. }
  104. })
  105. .catch(err => {
  106. // handle err
  107. console.log(err)
  108. this.msg = err
  109. })
  110. },
  111. startCameras() {
  112. // if (this.camerasList.length <= 0) {
  113. // this.$emit('error', '未找到相机')
  114. // return
  115. // }
  116. // const html5QrCode = new Html5Qrcode('reader')
  117. // html5QrCode
  118. // .start(
  119. // this.camerasList[0].id,
  120. // { fps: 10 },
  121. // (decodedText, decodedResult) => {
  122. // // do something when code is read
  123. // this.msg += decodedText + '👈'
  124. // this.msg += decodedResult + '?👈'
  125. // this.$emit('success', decodedText)
  126. // },
  127. // errorMessage => {
  128. // this.msg += errorMessage
  129. // this.$emit('error', errorMessage)
  130. // // parse error, ignore it.
  131. // }
  132. // )
  133. // .catch(err => {
  134. // this.msg += err
  135. // })
  136. this.cameras = new Html5Qrcode('reader', {
  137. fps: 10,
  138. videoConstraints: {
  139. width: { ideal: 1280 },
  140. height: { ideal: 720 },
  141. // 或尝试 facingMode
  142. facingMode: 'environment' // 后置摄像头
  143. }
  144. })
  145. this.cameras.start({ facingMode: { exact: 'environment' }}, { fps: 10 }, this.CamerasSuccess)
  146. },
  147. CamerasSuccess(decodedText, decodedResult) {
  148. console.log(decodedResult)
  149. this.$emit('success', decodedText)
  150. },
  151. // stopCamera() {
  152. // const video = document.querySelector('video')
  153. // if (video && video.srcObject) {
  154. // const stream = video.srcObject
  155. // const tracks = stream.getTracks()
  156. // tracks.forEach(track => track.stop())
  157. // video.srcObject = null
  158. // }
  159. // },
  160. close() {
  161. this.camerasList = []
  162. if (this.cameras) {
  163. this.cameras
  164. .stop()
  165. .then(ignore => {
  166. this.cameras = null
  167. // QR Code scanning is stopped.
  168. })
  169. .catch(err => {
  170. // Stop failed, handle it.
  171. this.msg = err
  172. this.cameras = null
  173. })
  174. }
  175. this.show = false
  176. }
  177. // onDecode(result) {
  178. // this.$emit('success', result)
  179. // // this.$toast(result)
  180. // },
  181. // async onInit(promise) {
  182. // try {
  183. // const { capabilities } = await promise
  184. // const TORCH_IS_SUPPORTED = !!capabilities.torch
  185. // console.log('TORCH_IS_SUPPORTED', TORCH_IS_SUPPORTED)
  186. // await promise
  187. // } catch (error) {
  188. // this.$emit('error', error)
  189. // this.close()
  190. // }
  191. // }
  192. }
  193. }
  194. </script>
  195. <style lang="scss" scoped>
  196. .sweep {
  197. width: 100vw;
  198. height: 100vh;
  199. z-index: 9990;
  200. background-color: #000;
  201. position: fixed;
  202. top: 0;
  203. left: 0;
  204. }
  205. .closeBtn {
  206. width: 40px;
  207. height: 40px;
  208. position: absolute;
  209. top: 20px;
  210. border-radius: 50%;
  211. background-color: #ffffff80;
  212. right: 20px;
  213. z-index: 9992;
  214. box-sizing: border-box;
  215. padding: 10px;
  216. svg {
  217. width: 100%;
  218. height: 100%;
  219. }
  220. }
  221. .ku-scanner {
  222. width: 100%;
  223. height: 100%;
  224. position: absolute;
  225. right: 0;
  226. top: 0;
  227. z-index: 9991;
  228. display: flex;
  229. justify-content: center;
  230. align-items: center;
  231. .ku-scanner-content {
  232. background-size: 3rem 3rem;
  233. background-position: -1rem -1rem;
  234. width: 100%;
  235. height: 100%;
  236. position: relative;
  237. background-color: rgba(18, 18, 18, 0);
  238. margin: auto;
  239. // 提示信息
  240. .ku-scanner-tooltip {
  241. width: 100%;
  242. height: 35px;
  243. line-height: 35px;
  244. font-size: 14px;
  245. text-align: center; /* color: #f9f9f9; */
  246. margin: 0 auto;
  247. position: absolute;
  248. top: 0;
  249. left: 0;
  250. color: #ffffff;
  251. }
  252. .ku-scanner-section {
  253. width: 213px;
  254. height: 213px;
  255. position: absolute;
  256. left: 50%;
  257. top: 50%;
  258. transform: translate(-50%, -50%);
  259. overflow: hidden;
  260. border: 0.1rem solid rgba(86, 247, 118, 0.301);
  261. // // 装饰角
  262. // &:after {
  263. // content: '';
  264. // display: block;
  265. // position: absolute;
  266. // width: 12px;
  267. // height: 12px;
  268. // border: 0.2rem solid transparent;
  269. // top: 0;
  270. // border-top-color: #2ffc58;
  271. // right: 0;
  272. // border-right-color: #2ffc58;
  273. // }
  274. // &:before {
  275. // content: '';
  276. // display: block;
  277. // position: absolute;
  278. // width: 12px;
  279. // height: 12px;
  280. // border: 0.2rem solid transparent;
  281. // top: 0;
  282. // border-top-color: #2ffc58;
  283. // left: 0;
  284. // border-left-color: #2ffc58;
  285. // }
  286. // // 装饰角
  287. // .ku-scanner-section-angle {
  288. // &:after {
  289. // content: '';
  290. // display: block;
  291. // position: absolute;
  292. // width: 12px;
  293. // height: 12px;
  294. // border: 0.2rem solid transparent;
  295. // bottom: 0;
  296. // border-bottom-color: #2ffc58;
  297. // right: 0;
  298. // border-right-color: #2ffc58;
  299. // }
  300. // &:before {
  301. // content: '';
  302. // display: block;
  303. // position: absolute;
  304. // width: 12px;
  305. // height: 12px;
  306. // border: 0.2rem solid transparent;
  307. // bottom: 0;
  308. // border-bottom-color: #2ffc58;
  309. // left: 0;
  310. // border-left-color: #2ffc58;
  311. // }
  312. // }
  313. // 扫描活动线
  314. .ku-scanner-section-animation-line {
  315. height: calc(100% - 2px);
  316. width: 100%;
  317. background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #2ffc58 211%);
  318. border-bottom: 3px solid #2ffc58;
  319. transform: translateY(-100%);
  320. animation: radar-beam 2s infinite alternate;
  321. animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99);
  322. animation-delay: 1.4s;
  323. }
  324. }
  325. }
  326. }
  327. // 扫描活动线--上下 动画
  328. @keyframes radar-beam {
  329. 0% {
  330. transform: translateY(-100%);
  331. }
  332. 100% {
  333. transform: translateY(0);
  334. }
  335. }
  336. #reader {
  337. width: 100%;
  338. height: 100%;
  339. position: absolute;
  340. top: 0;
  341. left: 0;
  342. object-fit: cover; /* 使视频自适应填充 */
  343. }
  344. #reader >>> video {
  345. width: 100% !important;
  346. height: 100% !important;
  347. object-fit: cover;
  348. }
  349. // #reader__dashboard_section_cameras,
  350. // #reader__dashboard_section_swaplink,
  351. // #reader__header_message,
  352. // #reader__scan_region > div {
  353. // display: none !important; /* 隐藏默认的一些 UI */
  354. // }
  355. // #reader__scan_region video {
  356. // width: 100% !important;
  357. // height: 100% !important;
  358. // object-fit: cover; /* 视频填充满容器,裁切多余部分 */
  359. // }
  360. // #reader__dashboard {
  361. // display: none !important;
  362. // }
  363. </style>