index.ts 8.5 KB

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