康巴易测肤/伤疤uniapp小程序类
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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