index.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // pages/faceRecognition/index.ts
  2. import { base } from "../../config/index";
  3. let ctx: WechatMiniprogram.CameraContext | undefined = undefined;
  4. let listener: WechatMiniprogram.CameraFrameListener | undefined;
  5. let createVKSession: WechatMiniprogram.VKSession | undefined = undefined;
  6. let innerAudioContext: WechatMiniprogram.InnerAudioContext;
  7. const app = getApp<IAppOption>();
  8. const topTips = {
  9. ready: '请正对手机,保持光线充足',
  10. front: '请正对屏幕',
  11. left: '请向左转头',
  12. right: '请向右转头',
  13. }
  14. const bottomTips = {
  15. recording: '脸部信息录入中...',
  16. complete: '脸部信息录入成功',
  17. error: '脸部信息录入失败'
  18. }
  19. let T: number = 0;
  20. const daoJiShi: number = 15;
  21. Page({
  22. /**
  23. * 页面的初始数据
  24. */
  25. data: {
  26. isStartVideo: false, // 是否点击开始按钮
  27. isEmpower: false, // 是否授权
  28. isUpLoad: false, // 是否上传中
  29. initFace: false, // 是否初始化人脸识别
  30. isTranscribe: false, // 是否录制中
  31. seconds: 0, // 倒计
  32. topTips: topTips.ready, // 提示语句
  33. bottomTips: "", // 底部提示语句
  34. buttomText: "录制"
  35. },
  36. // 开始初始化录制
  37. async initCamera() {
  38. if (this.data.isStartVideo) return;
  39. wx.showLoading({
  40. title: ""
  41. })
  42. this.setData({
  43. isStartVideo: true
  44. })
  45. wx.nextTick(() => {
  46. this.getMssage().then(async (r: any) => {
  47. console.log('是否授权发送消息:', r)
  48. if (!ctx) ctx = wx.createCameraContext();
  49. this.createVK();
  50. ctx.startRecord({
  51. timeout: daoJiShi, // 录制时长
  52. selfieMirror: false,
  53. timeoutCallback: (res) => {
  54. // 超出录制时长 关闭录屏
  55. this.setData({
  56. isStartVideo: false,
  57. bottomTips: "",
  58. topTips: topTips.ready, // 提示语句
  59. })
  60. createVKSession?.stop();
  61. listener?.stop();
  62. this.getUrl(res);
  63. },
  64. success: () => {
  65. // 调用成功,开始录制
  66. wx.hideLoading();
  67. this.setData({
  68. seconds: daoJiShi,
  69. bottomTips: bottomTips.recording,
  70. topTips: topTips.front
  71. });
  72. innerAudioContext.src = "assets/voice/front.mp3";
  73. innerAudioContext.play();
  74. },
  75. fail: () => {
  76. // 调用失败
  77. wx.hideLoading();
  78. wx.showToast({
  79. icon: "none",
  80. title: "相机调用失败",
  81. duration: 2000
  82. })
  83. },
  84. complete: () => { }
  85. })
  86. });
  87. })
  88. },
  89. // 添加监听
  90. addListen() {
  91. createVKSession?.start(() => { });
  92. T = Date.now();
  93. !listener && (listener = ctx?.onCameraFrame((frame: any) => {
  94. const Time = daoJiShi * 1000 - (Date.now() - T);
  95. if (Time % 1000 >= 50) return
  96. const s = Math.floor(Time / 1000);
  97. if (s == 10) {
  98. innerAudioContext.src = "assets/voice/left.mp3";
  99. this.setData({
  100. topTips: topTips.left
  101. })
  102. }
  103. if (s == 5) {
  104. innerAudioContext.src = "assets/voice/right.mp3";
  105. this.setData({
  106. topTips: topTips.right
  107. })
  108. }
  109. if (s % 5 === 0 && s !== 0) innerAudioContext.play();
  110. this.setData({
  111. seconds: s
  112. })
  113. // 识别人脸是否在画面种
  114. createVKSession?.detectFace({
  115. frameBuffer: frame.data, // 图片 ArrayBuffer 数据。人脸图像像素点数据,每四项表示一个像素点的 RGBA
  116. width: frame.width, // 图像宽度
  117. height: frame.height, // 图像高度
  118. scoreThreshold: 0.5, // 评分阈值
  119. sourceType: 1
  120. })
  121. }));
  122. listener?.start();
  123. },
  124. // 创建人脸追踪
  125. createVK() {
  126. // 初始化人脸识别
  127. if (this.data.initFace) return this.addListen();
  128. if (!createVKSession) createVKSession = wx.createVKSession({
  129. version: "v1",
  130. track: {
  131. plane: {
  132. mode: 1
  133. },
  134. face: { mode: 2 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  135. },
  136. })
  137. // 静态图片检测模式下,每调一次 detectFace 接口就会触发一次 updateAnchors 事件
  138. createVKSession?.on('removeAnchors', () => {
  139. wx.showToast({
  140. title: "未检测到人脸, 请重新采集",
  141. icon: "error"
  142. });
  143. this.overVideo();
  144. })
  145. this.addListen();
  146. },
  147. // 结束录像
  148. overVideo() {
  149. if (!ctx || !this.data.isStartVideo) return;
  150. ctx.stopRecord({
  151. compressed: true,
  152. complete: () => {
  153. createVKSession?.stop();
  154. innerAudioContext?.pause();
  155. listener?.stop();
  156. this.setData({
  157. isStartVideo: false,
  158. bottomTips: "",
  159. topTips: topTips.ready,
  160. seconds: 0
  161. })
  162. }
  163. })
  164. },
  165. // 获取本地临时地址
  166. getUrl(res: WechatMiniprogram.StartRecordTimeoutCallbackResult) {
  167. this.setData({
  168. isUpLoad: true
  169. })
  170. wx.showLoading({
  171. title: '上传进度:0%',
  172. mask: true //是否显示透明蒙层,防止触摸穿透
  173. })
  174. const uploadTask = wx.uploadFile({
  175. url: base.url + '/v3/upload',
  176. filePath: res.tempVideoPath,
  177. name: 'file', //服务器定义的Key值
  178. formData: {},
  179. header: {
  180. Authorization: wx.getStorageSync("token")
  181. },
  182. success: () => {
  183. wx.hideLoading();
  184. wx.showToast({
  185. title: "上传成功",
  186. duration: 2000,
  187. icon: "none"
  188. });
  189. this.setData({
  190. topTips: topTips.ready, // 提示语句
  191. bottomTips: bottomTips.complete
  192. })
  193. let time = setTimeout(() => {
  194. clearTimeout(time);
  195. wx.reLaunch({
  196. url: "/pages/downloadPage/index"
  197. })
  198. }, 2000)
  199. },
  200. fail: () => {
  201. wx.hideLoading();
  202. wx.showToast({
  203. title: "上传失败",
  204. icon: "none",
  205. duration: 2000
  206. })
  207. this.setData({
  208. topTips: topTips.ready, // 提示语句
  209. bottomTips: bottomTips.error
  210. })
  211. },
  212. complete: () => {
  213. this.setData({
  214. isUpLoad: false
  215. })
  216. }
  217. })
  218. //监听上传进度变化事件
  219. uploadTask.onProgressUpdate((res) => {
  220. wx.showLoading({
  221. title: '上传进度:' + res.progress + '%',
  222. mask: true //是否显示透明蒙层,防止触摸穿透
  223. })
  224. })
  225. },
  226. // 非正常中止
  227. improperStop() {
  228. wx.navigateBack();
  229. },
  230. // 未授权
  231. unauthorized() {
  232. wx.showToast({
  233. title: "拒绝授权将无法录制视频信息"
  234. });
  235. wx.navigateBack();
  236. },
  237. // 初始化完成
  238. initialization() { },
  239. // 获取消息授权
  240. getMssage() {
  241. return new Promise((resolve) => {
  242. if (!app.globalData.configPage?.messageID || !wx.canIUse("requestSubscribeMessage")) {
  243. resolve("reject");
  244. return
  245. }
  246. wx.requestSubscribeMessage({
  247. tmplIds: app.globalData.configPage?.messageID || [],
  248. success: (res: WechatMiniprogram.RequestSubscribeMessageSuccessCallbackResult) => {
  249. resolve(res.p1mpCydIQ6OtxCSa62NaSFiEkQiTsb8KPFaAs1SuKMw || 'reject');
  250. },
  251. fail: () => {
  252. resolve("reject");
  253. }
  254. })
  255. })
  256. },
  257. /**
  258. * 生命周期函数--监听页面加载
  259. */
  260. onLoad() {
  261. innerAudioContext = wx.createInnerAudioContext();
  262. innerAudioContext.loop = false;
  263. },
  264. /**
  265. * 生命周期函数--监听页面初次渲染完成
  266. */
  267. onReady() {
  268. },
  269. /**
  270. * 生命周期函数--监听页面显示
  271. */
  272. onShow() {
  273. },
  274. /**
  275. * 生命周期函数--监听页面隐藏
  276. */
  277. onHide() {
  278. // 在录制中退出后台页面隐藏,返回上一页,确保重新进入当前页
  279. this.overVideo();
  280. },
  281. /**
  282. * 生命周期函数--监听页面卸载
  283. */
  284. onUnload() {
  285. },
  286. /**
  287. * 页面相关事件处理函数--监听用户下拉动作
  288. */
  289. onPullDownRefresh() {
  290. },
  291. /**
  292. * 页面上拉触底事件的处理函数
  293. */
  294. onReachBottom() {
  295. },
  296. /**
  297. * 用户点击右上角分享
  298. */
  299. // onShareAppMessage() {
  300. // },
  301. })
  302. export { }