康巴易测肤/伤疤uniapp小程序类
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

756 lines
17KB

  1. <template>
  2. <view class="modal bottom-modal" :class="show ? 'show' : ''">
  3. <view class="camera-con" :style="{'margin-top': top + 'px'}">
  4. <camera flash="off" :device-position="device" resolution="high" @stop="stop" @error="error"
  5. style="width: 100vw;height: 100vh; position: fixed;top:0;left: 0;z-index: -99;"
  6. :style="{'top': top + 'px',}">
  7. <cover-view class="cover">
  8. </cover-view>
  9. </camera>
  10. </view>
  11. <!-- 人脸轮廓 -->
  12. <!-- <cover-view class="face-con" :style="{'top': (Number(top)) + 'px',}">
  13. <cover-view class="title" style="text-align: center;">
  14. 【人脸正面请对准下面人脸框】
  15. </cover-view>
  16. <cover-view>
  17. <cover-image :src="faceImg" class="face-img"></cover-image>
  18. </cover-view>
  19. </cover-view> -->
  20. <!-- 检测提示 -->
  21. <cover-view class="face-tips">
  22. </cover-view>
  23. <cover-view class="camera-action">
  24. <cover-view class="action clooseimg" @click="closeImage">
  25. <cover-image :src="clooseimg" class="img"></cover-image>
  26. </cover-view>
  27. <cover-view class="action photoimg" @click="onGetPhoto">
  28. <cover-image :src="photoimg" class="img"></cover-image>
  29. </cover-view>
  30. <cover-view class="action QHimg" @click="deviceQH">
  31. <cover-image :src="QHimg" class="img"></cover-image>
  32. </cover-view>
  33. </cover-view>
  34. </view>
  35. </template>
  36. <script>
  37. // 人脸检测
  38. /**
  39. * @event {Function} photoChange 拍照完成事件
  40. * @event {Function} detectFailed 人脸检测失败
  41. * @event {Function} detectOver 人脸检测结束
  42. * @method {Function} initData 初始化人脸检测
  43. */
  44. export default {
  45. name: 'face-detect',
  46. data() {
  47. return {
  48. device: 'front',
  49. show: false,
  50. tipsText: '',
  51. isSuccess: false, //是否检测完成
  52. face: {},
  53. actionsList: null,
  54. context: "",
  55. tipsTextCss: "tipsTextCss",
  56. listener: null,
  57. VKSession: null,
  58. // 是否是人脸
  59. isFace: null,
  60. // 人脸识别加载是否成功
  61. loadingState: null,
  62. // QHimg
  63. QHimg: '',
  64. // photoimg
  65. photoimg: '',
  66. // clooseimg
  67. clooseimg: '',
  68. backImg: require("@/static/detection/back.png"),
  69. faceImg: '',
  70. tipsList: [
  71. /* {id: 0, imgPath: 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/7722c2de9b344422b7c9d243afac2374.png', text: '正对手机', className: 'phone'}, */
  72. {id: 1, imgPath: 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/0667c02c3a464a1098d1fa040b922a2d.png', text: '光线充足', className: 'line'},
  73. /* {id: 2, imgPath: 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/456926ae60884010918471e68de150cb.png', text: '脸无遮挡', className: 'smile'}, */
  74. {id: 3, imgPath: 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/ae3846b0acd04bc791e3f8117ae8677a.png', text: '不要化妆', className: 'brush'},
  75. {id: 4, imgPath: 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/9077a2a4695a4e5ea171bf777c53459d.png', text: '不戴眼镜', className: 'glasses'},
  76. {id: 5, imgPath:'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/1fa444eb992c42e6b77b8d148550336c.png', text: '不戴帽子', className: 'hat'},
  77. ]
  78. }
  79. },
  80. props: {
  81. buildActionContainer: Function,
  82. actions: Function,
  83. hasSwitch: false,
  84. isDev: false,
  85. navbarTitle: String,
  86. isShowNavbar: true,
  87. top: String
  88. },
  89. onLoad() {
  90. /* this.initData(); */
  91. },
  92. onUnload() {
  93. this.VKSession.destroy();
  94. },
  95. mounted() {
  96. },
  97. methods: {
  98. onNavBack() {
  99. console.log("返回");
  100. this.$emit('onBack')
  101. },
  102. onLoadReset() {
  103. this.initData();
  104. },
  105. createdVKSession() {
  106. uni.showLoading({
  107. title: '相机加载中'
  108. })
  109. let count = 0;
  110. let that = this;
  111. this.context = uni.createCameraContext();
  112. this.listener = this.context.onCameraFrame((frame) => {
  113. /* count++;
  114. if (count === 10) {
  115. that.detectFace(frame);
  116. count = 0;
  117. } */
  118. });
  119. this.VKSession = wx.createVKSession({
  120. version: 'v1',
  121. track: {
  122. plane: {
  123. mode: 1
  124. },
  125. face: {
  126. mode: 2
  127. }
  128. }
  129. })
  130. // 摄像头实时检测模式下,监测到人脸时,updateAnchors 事件会连续触发 (每帧触发一次)
  131. /* this.VKSession.on('updateAnchors', anchors => {
  132. anchors.forEach(anchor => {
  133. console.log('anchor.points', anchor.points)
  134. console.log('anchor.origin', anchor.origin)
  135. console.log('anchor.size', anchor.size)
  136. console.log('anchor.angle', anchor.angle)
  137. });
  138. this.isFace = true;
  139. }) */
  140. // 当人脸从相机中离开时,会触发 removeAnchors 事件
  141. /* this.VKSession.on('removeAnchors', () => {
  142. this.$u.toast('请将人脸对准屏幕', 1500)
  143. console.log("检测不到人脸", 'removeAnchors');
  144. this.isFace = false;
  145. }) */
  146. // 需要调用一次 start 以启动
  147. this.VKSession.start(errno => {
  148. if (errno) {
  149. console.log("初始化失败", errno);
  150. this.$u.toast('相机加载失败')
  151. this.loadingState = false;
  152. this.show = true;
  153. // 如果失败,将返回 errno
  154. setTimeout(() => {
  155. this.QHimg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/f5dd3ef3f3bc49a48aec330c43e638da.png',
  156. this.clooseimg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/2ef369bc619e4098b091cf7fefe26ecb.png',
  157. this.faceImg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/d8565c6b51404b4996820083d684a111.png',
  158. this.photoimg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/de6c268fcced49069c3f75ad0f2eb5be.png'
  159. }, 800)
  160. } else {
  161. console.log("初始化成功", errno);
  162. this.$u.toast('相机加载成功')
  163. this.loadingState = true;
  164. this.show = true;
  165. setTimeout(() => {
  166. this.QHimg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/f5dd3ef3f3bc49a48aec330c43e638da.png',
  167. this.clooseimg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/2ef369bc619e4098b091cf7fefe26ecb.png',
  168. this.faceImg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/d8565c6b51404b4996820083d684a111.png',
  169. this.photoimg = 'https://telpo-healthy.oss-cn-hangzhou.aliyuncs.com/healthy/knowledge/202412/de6c268fcced49069c3f75ad0f2eb5be.png'
  170. }, 800)
  171. // 否则,返回null,表示成功
  172. }
  173. });
  174. this.listener.start();
  175. },
  176. async detectFace(frame) {
  177. console.warn(frame.data);
  178. /* this.VKSession.detectFace({
  179. frameBuffer: frame.data,
  180. width: frame.width,
  181. height: frame.height,
  182. scoreThreshold: 0.8,
  183. sourceType: 0,
  184. modelMode: 1
  185. }); */
  186. },
  187. onGetPhoto() {
  188. let that = this;
  189. this.listener.stop()
  190. this.context.takePhoto({
  191. quality: 'high',
  192. success: (res) => {
  193. // 把地址缓存到本地
  194. /* that.$store.commit('setForKey', {
  195. key: 'detectFaceImg',
  196. value: res.tempImagePath,
  197. }) */
  198. that.compressImage(res.tempImagePath)
  199. /* that.$emit('photoChange', res.tempImagePath) */
  200. },
  201. fail: (e) => {
  202. console.log(e)
  203. },
  204. complete: (e) => {
  205. console.log(e)
  206. }
  207. });
  208. },
  209. closeImage() {
  210. let that = this;
  211. console.log("选择相片");
  212. uni.chooseImage({
  213. count: 1,
  214. sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
  215. sourceType: ['album'], //从相册选择
  216. success: function(res) {
  217. console.log("选择的图片地址", res.tempFilePaths[0]);
  218. /* that.$store.commit('setForKey', {
  219. key: 'detectFaceImg',
  220. value: res.tempFilePaths[0],
  221. }) */
  222. that.VKSession.destroy()
  223. that.compressImage(res.tempFilePaths[0])
  224. /* that.$emit('photoChange', res.tempFilePaths[0]) */
  225. },
  226. fail: function(err) {
  227. console.log("选择图片失败", err.errMsg);
  228. }
  229. })
  230. },
  231. deviceQH() {
  232. console.log("点击切换");
  233. this.device = this.device == 'back' ? 'front' : 'back';
  234. },
  235. error() {
  236. // this.tipsText = '相机异常'
  237. // this.cameraError()
  238. },
  239. stop() {
  240. // this.tipsText = '相机异常'
  241. // this.cameraError()
  242. },
  243. // 核验失败
  244. cameraError(e) {
  245. this.t = setTimeout(() => {
  246. clearTimeout(this.t)
  247. this.hideModal()
  248. this.$emit('detectFailed', e)
  249. }, 2000);
  250. },
  251. close() {
  252. clearTimeout(this.t)
  253. this.hideModal()
  254. this.$emit('detectFailed')
  255. },
  256. // 关闭
  257. hideModal() {
  258. uni.stopFaceDetect()
  259. this.face = {}
  260. this.isSuccess = false
  261. },
  262. compressImage(imgPath) {
  263. let that = this;
  264. uni.compressImage({
  265. src: imgPath,
  266. quality: 80, // 压缩质量
  267. success: (res) => {
  268. const compressedFilePath = res.tempFilePath;
  269. console.log('压缩后的图片路径:', compressedFilePath);
  270. that.$emit('photoChange', compressedFilePath);
  271. /* that.$store.commit('setForKey', {
  272. key: 'detectFaceImg',
  273. value: compressedFilePath,
  274. }) */
  275. },
  276. fail: (err) => {
  277. console.error(err);
  278. }
  279. });
  280. },
  281. // 拍照
  282. takePhoto() {
  283. this.context.takePhoto({
  284. quality: 'high',
  285. success: (res) => {
  286. that.compressImage(res.tempImagePath)
  287. /* this.$emit('photoChange', res.tempImagePath); */
  288. // 停止监听
  289. },
  290. fail: (e) => {
  291. console.log(e)
  292. },
  293. complete: (e) => {
  294. console.log(e)
  295. }
  296. });
  297. },
  298. // 检测完成
  299. detectOver() {
  300. this.isSuccess = true
  301. let t = setTimeout(() => {
  302. this.hideModal()
  303. clearTimeout(t)
  304. this.$emit('detectOver')
  305. }, 3000);
  306. },
  307. initData() {
  308. uni.getSetting({
  309. success: (res) => {
  310. if (res.authSetting['scope.camera'] === true) {
  311. this.createdVKSession()
  312. } else if (res.authSetting['scope.camera'] === false) {
  313. this.getCameraAuth()
  314. } else {
  315. this.createdVKSession()
  316. }
  317. }
  318. })
  319. },
  320. getCameraAuth() {
  321. uni.showModal({
  322. title: '温馨提示',
  323. content: '需要获取您摄像头权限才能更好的为您服务!是否授权摄像头权限?',
  324. confirmText: '授权',
  325. confirmColor: '#f94218',
  326. success: (res) => {
  327. if (res.confirm) {
  328. // 选择弹框内授权
  329. uni.openSetting({
  330. success: (res) => {
  331. if (res.authSetting[
  332. 'scope.camera'
  333. ]) {
  334. this.createdVKSession()
  335. } else {
  336. this.tipsText = "您未授权摄像头权限"
  337. this.cameraError('noAuth')
  338. }
  339. }
  340. })
  341. } else if (res.cancel) {
  342. this.tipsText = "您未授权摄像头权限"
  343. this.cameraError('noAuth')
  344. }
  345. }
  346. })
  347. },
  348. showData(faceData) {
  349. this.$emit("showData", faceData)
  350. if (this.isDev) {
  351. let face = faceData.faceInfo[0].angleArray
  352. this.face = face
  353. }
  354. },
  355. }
  356. }
  357. </script>
  358. <style lang="scss" scoped>
  359. .modal {
  360. height: 100vh;
  361. position: fixed;
  362. top: 0;
  363. right: 0;
  364. bottom: 0;
  365. left: 0;
  366. z-index: 1000;
  367. opacity: 0.1;
  368. outline: 0;
  369. text-align: center;
  370. backface-visibility: hidden;
  371. perspective: 2000rpx;
  372. pointer-events: none;
  373. overflow: hidden;
  374. .back {
  375. position: absolute;
  376. top: 30rpx;
  377. left: 0;
  378. height: 60rpx;
  379. /* line-height: 60rpx; */
  380. width: 100vw;
  381. padding: 0 20rpx;
  382. z-index: 9999;
  383. display: flex;
  384. justify-content: flex-start;
  385. align-items: center;
  386. .back-img {
  387. height: 30rpx;
  388. width: 23rpx;
  389. }
  390. .back-text {
  391. padding-left: 12rpx;
  392. font-size: 30rpx;
  393. text-align: center;
  394. font-family: Alibaba PuHuiTi;
  395. }
  396. }
  397. .camera-con {
  398. /* position: relative; */
  399. z-index: 9999;
  400. }
  401. }
  402. .qhImage {
  403. background-color: #ccc;
  404. width: 80rpx;
  405. height: 80rpx;
  406. z-index: 1001;
  407. margin: 30rpx;
  408. border-radius: 50%;
  409. padding: 16rpx;
  410. }
  411. .modal::before {
  412. content: "";
  413. display: inline-block;
  414. height: 100%;
  415. vertical-align: middle;
  416. }
  417. .modal.show {
  418. opacity: 1;
  419. overflow-x: hidden;
  420. overflow-y: hidden;
  421. pointer-events: auto;
  422. }
  423. .modal.bottom-modal::before {
  424. vertical-align: bottom;
  425. }
  426. .modal.bottom-modal .dialog {
  427. width: 100%;
  428. border-radius: 0;
  429. }
  430. .modal.bottom-modal {
  431. margin-bottom: -1000rpx;
  432. }
  433. .modal.bottom-modal.show {
  434. margin-bottom: 0;
  435. }
  436. .dialog {
  437. position: fixed;
  438. display: inline-block;
  439. vertical-align: middle;
  440. margin-left: auto;
  441. margin-right: auto;
  442. /* width: 680rpx; */
  443. height: 80vh;
  444. width: 100vw;
  445. max-width: 100%;
  446. background-color: #f8f8f8;
  447. border-radius: 10rpx;
  448. overflow: hidden;
  449. left: 0;
  450. top: 0;
  451. }
  452. .bar {
  453. display: flex;
  454. position: relative;
  455. align-items: center;
  456. min-height: 90rpx;
  457. height: 90rpx;
  458. padding: 0rpx 40rpx;
  459. justify-content: space-between;
  460. }
  461. .bg-white {
  462. background-color: #ffffff;
  463. color: #666666;
  464. }
  465. .img {
  466. height: 60rpx;
  467. width: 60rpx;
  468. object-fit: contain;
  469. }
  470. .detectInfo {
  471. padding: 20rpx 0rpx;
  472. font-size: 34rpx;
  473. text-align: center;
  474. animation-duration: 1.5s;
  475. color: #000000;
  476. z-index: 999;
  477. }
  478. .faceContent {
  479. height: 700rpx;
  480. position: relative;
  481. }
  482. .successImage {
  483. overflow: hidden;
  484. width: 600rpx;
  485. height: 600rpx;
  486. border-radius: 50%;
  487. position: absolute;
  488. top: 0;
  489. left: 50%;
  490. z-index: 999;
  491. transform: translateX(-50%);
  492. }
  493. .tipsTextCss {
  494. animation: 1.5s tipsTextAnimation;
  495. animation-duration: 1.5s;
  496. }
  497. @keyframes tipsTextAnimation {
  498. 0% {
  499. transform: scale(1);
  500. }
  501. 20% {
  502. transform: scale(1.5);
  503. }
  504. 70% {
  505. transform: scale(1.5);
  506. }
  507. 100% {
  508. transform: scale(1);
  509. }
  510. }
  511. .cover {
  512. width: 100vw;
  513. height: 100vh;
  514. display: flex;
  515. flex-direction: column;
  516. /* overflow: hidden; */
  517. }
  518. .cover-bottom {
  519. height: 60rpx;
  520. display: flex;
  521. justify-content: center;
  522. }
  523. .cover-item {
  524. height: 60rpx;
  525. width: 100%;
  526. background-color: #FFFFFF;
  527. display: flex;
  528. justify-content: space-around;
  529. align-items: center;
  530. }
  531. .load-fail {
  532. position: absolute;
  533. top: 0;
  534. left: 0;
  535. height: 100vh;
  536. width: 100vw;
  537. overflow: hidden;
  538. display: flex;
  539. justify-content: center;
  540. align-items: center;
  541. }
  542. .load-reset {
  543. width: 260rpx;
  544. font-size: 28rpx;
  545. padding: 20rpx;
  546. color: #fff;
  547. background-color: #8f7aed;
  548. border-radius: 15rpx;
  549. box-shadow: 0rpx 0rpx 20rpx 0rpx rgba(0, 0, 0, 0.2);
  550. ;
  551. }
  552. .camera {
  553. width: 100vw;
  554. height: 100vh;
  555. transform: scale(1.05);
  556. z-index: 999;
  557. overflow: hidden;
  558. }
  559. .camera-tips {
  560. position: fixed;
  561. top: 50%;
  562. left: 0;
  563. height: 120rpx;
  564. width: 100%;
  565. background-color: #FFFFFF;
  566. display: flex;
  567. justify-content: space-around;
  568. align-items: center;
  569. }
  570. .camera-action {
  571. position: absolute;
  572. bottom: 0;
  573. left: 0;
  574. width: 100%;
  575. display: flex;
  576. justify-content: space-around;
  577. align-items: center;
  578. z-index: 99999;
  579. overflow: hidden;
  580. background: #333;
  581. padding: 10rpx 0;
  582. .action {
  583. .img {
  584. height: 51rpx;
  585. width: 58rpx;
  586. object-fit: contain;
  587. }
  588. &.photoimg {
  589. .img {
  590. height: 161rpx;
  591. width: 161rpx;
  592. }
  593. }
  594. &.QHimg {
  595. .img {
  596. height: 56rpx;
  597. width: 64rpx;
  598. }
  599. }
  600. }
  601. }
  602. .face-con {
  603. position: absolute;
  604. bottom: 22vh;
  605. left: 0;
  606. width: 100vw;
  607. z-index: 9999;
  608. display: flex;
  609. justify-content: space-between;
  610. align-items: center;
  611. flex-direction: column;
  612. .title {
  613. height: 200rpx;
  614. line-height: 200rpx;
  615. width: 100%;
  616. font-family: Alibaba PuHuiTi;
  617. font-weight: bold;
  618. font-size: 30rpx;
  619. color: #FFFFFF;
  620. text-align: center;
  621. }
  622. .face-img {
  623. padding: 0 70rpx;
  624. height: 826rpx;
  625. width: calc(100vw - 140rpx);
  626. /* z-index: 9999; */
  627. object-fit: contain;
  628. }
  629. }
  630. .face-tips {
  631. position: absolute;
  632. bottom: 15vh;
  633. left: 0;
  634. z-index: 9999;
  635. width: 100vw;
  636. .title {
  637. font-family: Alibaba PuHuiTi;
  638. font-weight: bold;
  639. font-size: 30rpx;
  640. color: #FFFFFF;
  641. line-height: 38rpx;
  642. }
  643. .tips-con {
  644. padding: 0 100rpx 0 100rpx;
  645. .tips-list {
  646. width: 100%;
  647. display: flex;
  648. justify-content: flex-start;
  649. align-items: center;
  650. flex-wrap: nowrap;
  651. .item {
  652. height: 120rpx;
  653. width: 30%;
  654. padding: 30rpx 0 0 0;
  655. /* margin-top: 30rpx; */
  656. display: flex;
  657. justify-content: space-between;
  658. align-items: center;
  659. flex-direction: column;
  660. margin: 0 20rpx;
  661. .item-img {
  662. height: 100%;
  663. width: 100%;
  664. object-fit: contain;
  665. &.phone {
  666. width: 36rpx;
  667. height: 52rpx;
  668. /* padding-bottom: 25rpx; */
  669. }
  670. &.line {
  671. width: 61rpx;
  672. height: 61rpx;
  673. padding-top: 10rpx;
  674. /* padding-bottom: 18rpx; */
  675. }
  676. &.smile {
  677. width: 56rpx;
  678. height: 56rpx;
  679. /* padding-bottom: 24rpx; */
  680. }
  681. &.brush {
  682. width: 56rpx;
  683. height: 61rpx;
  684. padding-top: 10rpx;
  685. /* padding-bottom: 12rpx; */
  686. }
  687. &.glasses {
  688. width: 62rpx;
  689. height: 39rpx;
  690. padding-top: 20rpx;
  691. }
  692. &.hat {
  693. width: 54rpx;
  694. height: 37rpx;
  695. padding-top: 20rpx;
  696. /* padding-bottom: 35rpx; */
  697. }
  698. }
  699. .item-text {
  700. width: auto;
  701. font-family: Alibaba PuHuiTi;
  702. font-weight: bold;
  703. font-size: 24rpx;
  704. color: #FFFFFF;
  705. line-height: 38rpx;
  706. }
  707. }
  708. }
  709. }
  710. }
  711. </style>