index.ts 8.9 KB

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