☰
uniapp微信小程序人脸取景框实现:VKSession实时检测与拍照对齐
2026/9/30 6:18:43 网站建设 项目流程

做小程序里的人脸取景框摄像,乍一听像是个「调个 camera 组件、叠个背景框」的简单活,真做起来才知道里面全是坑。尤其是用 uniapp 写,既要照顾跨端编译,又要在微信小程序里跟原生组件的同层渲染、人脸检测能力打交道,踩一圈下来,比想象中费劲不少。

我这次做的是一个基于 uniapp 的微信小程序版本,需求很直接:打开前置摄像头,画面里有一个随人脸移动的取景框,用户按下拍照,最终保存的照片也要跟取景框的位置对得上。整个流程不涉及云端人脸比对,纯粹是「检测人脸位置 + 实时框选 + 拍照」。这篇文章就从需求拆解、技术选型、页面搭建、人脸检测逻辑、取景框绘制、拍照对齐到最后的真机踩坑,完整过一遍。适合刚拿到同类需求、或者准备在小程序里碰人脸能力的开发者,看完能少走不少弯路。

1. 需求拆解与技术选型,先把“人脸取景框”这五个字拆明白

1.1 你要的到底是“引导框”还是“真实人脸检测框”

很多产品经理嘴里说的“人脸取景框”,其实有两种完全不同的东西,一定要在最开始问清楚。

第一种是固定引导框,就是界面上画一个椭圆或矩形,提示用户把脸放到这个区域里。这种做法不检测人脸,只是一个静态的取景参考,常见于证件照小程序、实名认证第一步。实现极其简单,一个 cover-view 加个边框就行,不存在任何检测逻辑。

第二种是实时检测框,系统真的去识别画面里的人脸,算出一个坐标范围,然后让一个框跟着人脸走。人脸往左,框就往左;人脸靠近,框就变大。这种体验更适合美颜相机、人脸试妆、打卡考勤这类场景,但它依赖的人脸检测能力在小程序里并不像网页上那么好拿。

我这里遇到的需求是第二种,所以整条技术路线都是围绕真实人脸检测来设计的。如果你只是第一种,那这篇文章后面的大半内容其实可以跳过,直接看第 2 章的布局和第 4 章的拍照对齐就够了。

1.2 小程序实时人脸上的三条技术路线

微信小程序里做实时人脸检测,绕不开三条路线,我一开始都调研过,说下各自的情况。

路线一:使用微信 VK 视觉能力。微信在部分基础库版本里提供了wx.createVKSession这个底层视觉接口,可以用来做人脸检测、人脸关键点、表情识别等。它跑在端上,检测速度够快,不需要额外付费,也不需要把自己的视频帧上传到任何服务器。这是目前纯小程序前端方案里最现实的一条。

路线二:把视频帧传给后端检测。小程序端用camera组件拿预览流,再通过wx.createCameraContext的onCameraFrame订阅帧数据,把帧传给后端的人脸检测接口,后端返回坐标后再画框。这条路线的问题在于帧传输延迟高,实时性很难保证,而且数据量大会消耗流量和服务器成本。做实时取景框基本不推荐,更适合“拍照后检测”的形态。

路线三:前端跑轻量模型。比如用 TensorFlow.js 的 facemesh 等模型做实时关键点检测。但在微信小程序环境里,运行 tfjs 类库需要额外适配,模型体积、初始化耗时、线程阻塞都是问题,普通业务很难接受。社区里确实有项目跑通了,但说实话,为了一个取景框引入这么重的方案,性价比不高。

我最终选了路线一,也就是 VK。原因很直接:它在端上计算,延迟低,能真正“实时”;其次是代码量不大,核心逻辑集中在一个 Session 的初始化和轮询检测上,和 uniapp 的 Vue 页面逻辑耦合度低,好维护。

注意:VK 能力在不同版本的基础库、不同小程序类目下开放程度不完全一样。开发前先确认自己的基础库版本和账号类目能正常调用,否则就要准备降级方案(比如退回固定引导框,或者用拍照后检测的替代逻辑)。这个我在第 5 章排查部分会细说。

2. 摄像头与页面搭建,先让画面亮起来

技术路线定了,接下来先不急着写检测逻辑。第一步是把 camera 组件在 uniapp 页面里跑起来,并且把 UI 布局搭对。这里有两个前置条件:manifest 里的小程序配置要正确,页面上覆盖层要用对组件。

2.1 manifest 与页面配置,别在第一步就翻车

uniapp 编译到微信小程序时,manifest.json的mp-weixin节点会直接影响到最终小程序的表现。做摄像头功能,要确认这个节点下面没有屏蔽掉不必要的权限描述,同时注意基础库版本设置。

实际上微信小程序的相机权限不需要单独在后台申请,用户第一次用camera组件时,微信会自动弹权限框。但有一种情况会翻车:如果你还在页面里用uni.chooseImage或者uni.saveImageToPhotosAlbum这类接口,需要在 app.json 的requiredPrivateInfos里声明相关字段,否则真机上接口直接报错。

我在 uniapp 里的做法是,在manifest.json的mp-weixin节点中设置:

{ "mp-weixin": { "appid": "你的小程序appid", "setting": { "urlCheck": false }, "usingComponents": true } }

然后需要在源码视图中,找到编译后生成的app.json里补充requiredPrivateInfos,但 uniapp 有时候不会自动帮你合并。更稳妥的方式是在 manifest 的mp-weixin里加"requiredPrivateInfos"字段,或者直接使用条件编译在页面内调用时做好失败兜底。

提示:如果你运行的是 Vue 3 版本的 uniapp,manifest 配置入口没变,但部分字段的校验更严格,配置完记得重新编译而不是热更新,否则可能不生效。

2.2 页面结构:camera 组件加覆盖层的正确姿势

页面结构上,camera组件必须铺满或尽量铺满屏幕,这样取景框才能有足够的位置空间。推荐的做法是让 camera 占据全屏,然后在其上方用一个绝对定位的覆盖层来绘制取景框。

这里要特别强调一个知识点:微信小程序的 camera 属于原生组件,在旧版本的基础库中,原生组件层级最高,普通 view 无法覆盖在它上面。虽然新版基础库开始支持同层渲染,很多普通 view 也能盖上去,但兼容性仍有隐患。所以在正式项目里,我建议直接使用cover-view和cover-image来作为覆盖层,这是专门用于覆盖原生组件的方案,稳妥得多。

我搭的页面结构大概是这样:

<template> <view class="camera-page"> <camera class="camera" device-position="front" flash="off" resolution="high" @error="onCameraError" ></camera> <!-- 人脸取景框覆盖层 --> <cover-view class="frame-layer" v-if="faceBox.show"> <cover-view class="face-box" :style="{ left: faceBox.x + 'px', top: faceBox.y + 'px', width: faceBox.w + 'px', height: faceBox.h + 'px' }" ></cover-view> </cover-view> <!-- 底部拍照按钮 --> <cover-view class="bottom-bar"> <cover-view class="capture-btn" @tap="handleCapture"></cover-view> </cover-view> </view> </template>

camera 的device-position="front"用的是前置摄像头,适合人脸自拍场景;flash="off"是因为前置摄像头一般没有闪光灯需求,避免部分机型调用失败。resolution="high"是为了拍照时保留更高的原始分辨率,细节上后面对齐要用到。

2.3 为什么覆盖层必须用 cover-view,同层渲染聊透

很多第一次接触小程序的开发者会在这块卡住:明明写了一个view放在 camera 上面,编辑器里看层级也对,手机上一运行,取景框就不见了,或者拍照按钮点不到。

原因就是 camera 是原生组件,它的渲染层级不是普通 DOM 层级能压得住的。虽然微信后续推出了同层渲染,把部分原生组件和 webview 层打通了,但 camera 在全屏、视频流等场景下的表现并不稳定,尤其在一些低端 Android 机上,普通 view 覆盖 camera 依旧时灵时不灵。

cover-view是官方专门设计用来覆盖在原生组件上的组件,它使用原生层绘制,能保证在 camera、map、video 等组件之上展示。代价是它的样式支持没有普通 view 那么全,常见的 CSS 属性有限,比如部分机型不支持box-shadow、border-radius的兼容性也有差异,至于动画,能用但别太花哨。

实际操作时,我给取景框用的都是 border 边框和基本定位属性,尽量不用渐变、阴影这些 fancy 的特性。底部拍照按钮用的也是 cover-View,因为如果这里用普通 view,在部分安卓真机上点击区域会被 camera 盖住,出现“按钮能看到但点不了”的诡异问题。

3. 人脸检测核心逻辑,用 VKSession 拿到人脸位置

页面布局没问题之后,重头戏来了:怎么拿到人脸的实时位置。

3.1 VKSession 是什么,能干什么

简单理解,微信小程序的 VK(Visual Kit)是一套端上视觉能力接口。它的形态更像一个“检测会话”,你创建一个 Session,告诉它你要检测什么(人脸、人体、手势等),它就开始在自己的内部逻辑里处理摄像头数据,并提供给前端调用。

拿人脸检测来说,创建 Session 后,每一帧你都可以主动调用一次检测方法,拿到这一帧画面里所有人脸的信息,包括关键点坐标、人脸矩形等。因为它是本地计算的,所以实时性不错,不需要把摄像头画面传出去。

但注意,VK 的实时检测并不是“自动持续回调”的,而是需要你通过一个定时器不断去拉取检测结果。这个设计其实是好事,方便我们控制检测频率,避免每帧都跑导致手机发热。

3.2 初始化与开始检测,直接上代码

在 uniapp 中,这段逻辑要放在微信小程序平台下执行。如果将来要兼容 App 端或 H5 端,你需要用条件编译把 VK 部分包起来,其他端接入对应的原生能力或前端模型。

下面是我在项目里实际用的初始化代码,做了注释:

// #ifdef MP-WEIXIN initVKSession() { if (typeof wx.createVKSession === 'undefined') { this.setFallbackMode(); return; } const session = wx.createVKSession({ version: 'v1', track: { face: { mode: 1 } } }); this.vkSession = session; session.start((err) => { if (err) { console.error('VK session start error:', err); this.setFallbackMode(); return; } this.detectLoop(); }); }, detectLoop() { if (!this.vkSession) return; const res = this.vkSession.detectFace(); if (res && res.faceInfo && res.faceInfo.length > 0) { const face = res.faceInfo[0]; const points = face.points; // 归一化坐标数组,两个一组表示一个关键点: [x0, y0, x1, y1, ...] // 计算包围盒 const xs = []; const ys = []; for (let i = 0; i < points.length; i += 2) { xs.push(points[i]); ys.push(points[i + 1]); } const minX = Math.min(...xs); const maxX = Math.max(...xs); const minY = Math.min(...ys); const maxY = Math.max(...ys); // 归一化坐标转页面像素坐标 const windowWidth = uni.getSystemInfoSync().windowWidth; const windowHeight = uni.getSystemInfoSync().windowHeight; this.faceBox = { show: true, x: minX * windowWidth, y: minY * windowHeight, w: (maxX - minX) * windowWidth, h: (maxY - minY) * windowHeight }; } else { this.faceBox.show = false; } // 控制检测频率,150ms 一次足够 this.vkTimer = setTimeout(() => this.detectLoop(), 150); }, // #endif

这里面的faceInfo[0]取的是第一个人脸。如果产品需求要同时支持多人脸框选,可以遍历整个faceInfo数组,分别计算包围盒,再用一个数组来渲染多个 cover-view 框。原理完全一样。

关键点是points是归一化坐标,范围通常在 0 到 1 之间,代表相对画面宽高的比例。所以把它乘以页面像素宽高,就能换算成我们需要的left、top、width、height。

注意:VK 返回的字段名「不同基础库版本可能有差异」,比如有些版本返回的可能是faceRect而不是points。我这里写points是基于多数版本的形态,开发时务必用真机 console.log 打印出来确认一下。如果字段对不上,再用实际字段解析。

3.3 关键点如何换算成取景框,别把框画歪了

拿到归一化坐标以后,还有几个细节要注意,否则框会跟人脸错位。

第一,坐标系的范围要确认。归一化坐标是以画面区域为基准还是以整个相机预览区域为基准,这会影响换算。实际操作中我遇到过返回的坐标范围不是 0~1,而是以某个固定尺寸为基准的情况,这时就需要用返回尺寸和实际展示尺寸做一次缩放。

第二,关键点包围盒和人脸实际区域的关系。直接用所有关键点的 min/max 作为取景框,边缘通常会贴脸贴得太紧,看起来不太自然。正常产品里一般会给一个放大系数,把这个包围盒向外扩展一点,视觉上更接近“脑袋框”。

我在项目里是这么处理的:取到minX, minY, maxX, maxY后,计算中心点和宽高,然后按比例放大 1.3 倍左右,再重新计算包围盒的左上角:

const centerX = (minX + maxX) / 2; const centerY = (minY + maxY) / 2; let w = (maxX - minX) * 1.3; let h = (maxY - minY) * 1.3; const x = centerX - w / 2; const y = centerY - h / 2;

这个 1.3 不是拍脑袋定的,是用多台真机试出来的。放太大人脸在框里会显得很空,放太小又不自然,你可以根据产品视觉稿调整。

第三,检测结果存在抖动。人脸静止时,VK 返回的坐标也不是完全稳定的,框架如果直接按原始坐标渲染,会看到取景框一直在微抖。解决办法是加一层平滑滤波,最简单的就是用上一次的坐标与当前坐标做线性插值,每次只更新一部分偏移量。

this.faceBox.x = this.lastX + (targetX - this.lastX) * 0.3; this.faceBox.y = this.lastY + (targetY - this.lastY) * 0.3; this.faceBox.w = this.lastW + (targetW - this.lastW) * 0.3;

这个系数 0.3 控制响应速度和平滑度的平衡。系数越大,框越跟手但越抖;系数越小,框越稳但越迟钝。我实测 0.3 左右在真机上观感比较好,你可以根据自己的机型再调。

3.4 框与人脸错位的几个原因

你可能遇到过这种情况:框确实在动,但对不上人脸的位置,或者偏左偏右。这通常有两个原因。

一是检测坐标范围和实际展示画面不一致。VK 的检测输入可能是相机采样的某一帧,这一帧的尺寸比例和 camera 组件展示出来的画面比例未必一致。比如相机传感器是 4:3,但页面上的 camera 是全屏 9:16,组件会对画面进行裁剪或缩放。此时归一化坐标如果基于 4:3 的原始帧,直接套到 9:16 的页面上自然就对不上。

我把这个坑挖到底之后,发现最简单的解法是:尽量让 camera 的比例和相机传感器比例保持一致。但实际 UI 上不可能所有人都用 4:3 全屏,所以我采用的方案是让相机预览居中铺满,两边如果有多余画面,就通过计算裁剪掉。这部分放到第 4 章拍照对齐里一起说明,因为取景框和照片的换算关系本质上是同一回事。

二是坐标系旋转。手机横竖屏切换、前置摄像头的镜像效果等都会影响坐标。前置摄像头在大多数手机上默认是镜像显示,这会导致人脸的左右位置和检测坐标的左右位置相反。解决方法是根据device-position和实际体验,决定是否对 x 坐标做一次镜像映射:

x = 1 - x;

但注意这个操作要谨慎,不同机型的表现不完全一样,建议真机上重点测试。

4. 拍照与取景框对齐,让“所见即所得”真正成立

人脸框动起来之后,第二个大坑来了:按下拍照,得到的照片和用户看到的取景框位置经常对不上。用户屏幕上看到的框套住了脸,保存下来的照片里人脸却偏了。

4.1 拍照流程与照片尺寸

小程序里拍照用的是wx.createCameraContext,在 uniapp 中直接通过uni.createCameraContext()获取,然后调用它的takePhoto方法。

handleCapture() { if (!this.cameraContext) { this.cameraContext = uni.createCameraContext(); } uni.showLoading({ title: '处理中...' }); this.cameraContext.takePhoto({ quality: 'high', success: (res) => { this.tempImagePath = res.tempImagePath; // 后续可以做保存相册或者上传 uni.saveImageToPhotosAlbum({ filePath: res.tempImagePath, success: () => { uni.hideLoading(); uni.showToast({ title: '已保存到相册', icon: 'success' }); }, fail: (err) => { // 用户拒绝相册权限等情况 uni.hideLoading(); uni.showToast({ title: '保存失败', icon: 'none' }); } }); }, fail: (err) => { uni.hideLoading(); uni.showToast({ title: '拍照失败', icon: 'none' }); } }); }

拍照返回的tempImagePath就是最终照片。照片尺寸由 camera 的分辨率决定,high档位下通常接近相机传感器的原生比例,可能是 4:3 或者 16:9,不同机型会有差异。

4.2 照片、预览画面与框的比例关系怎么换算

这里我把换算逻辑展开讲,因为大部分人就是栽在这里。

假设 camera 组件在页面上展示的宽高是viewW和viewH(比如全屏 375 x 812),照片实际像素宽高是imageW和imageH(比如 4032 x 3024)。如果两者的宽高比不一致,相机组件在预览时默认是裁剪填充模式(aspect fill),也就是把照片等比放大,直到完全覆盖展示区域,多出来的部分裁掉。那么照片上的某个点映射到页面展示区域时,就要先算出缩放比例,再考虑裁剪偏移。

我用一个实际例子说明。

比如照片是 4032 x 3024,宽高比是 4:3;页面展示区域是 375 x 812,宽高比约 9:19.4。为了填满页面,照片会被等比放大到高度 812 时,宽度约为 812 * 4032 / 3024 ≈ 1082,页面只显示中间部分宽 375,左右两边被裁掉。这就是典型的“上下铺满,左右裁剪”。

反过来,如果页面上有一个框,我们要知道它对应照片上的哪个区域,就需要用展示区域和照片区域的比例关系做映射。但由于 VK 检测返回的是相机输入画面的归一化坐标,而相机输入画面的比例和照片比例基本一致(同一颗摄像头),所以更稳的思路是:直接用归一化坐标 * 照片宽高,得到的就是照片上的人脸框真实位置。

如果要把这个框再显示到页面的 cover-view 上,那就需要把照片坐标系映射回页面坐标系,中间要考虑展示区域对照片的裁剪。这一步我没在代码里多做复杂计算,而是采用了一个更省事的方法:把 camera 组件的展示比例强行设置为和相机画面一致,比如 3:4(竖屏前置摄像头的常见比例),让展示画面不裁剪,这样页面坐标和照片坐标的比例就完全统一了。

具体做法是把 camera 放到一个固定宽高的容器中,容器比例为 3:4,居中显示,背景用深色填充。这样拍摄照片的比例与展示比例一致,VK 返回的归一化坐标乘以页面容器宽高,就能直接用于封面绘制,拍照之后照片里的人脸框位置也和预览时完全一致。

这个方案牺牲了一部分全屏效果,但极大降低了换算复杂度。如果产品一定要全屏,那就得老老实实计算裁剪映射,我建议写一个工具函数,输入预览区域尺寸、照片尺寸、归一化坐标,输出实际页面坐标。

4.3 保存到相册与上传前的处理

如果你只是要保存照片到相册,那上面就够用了。但很多业务场景里,拍完照之后还要把“人脸区域”单独截出来或者做后续处理。

一个常见需求是:只保留取景框内的人脸,作为头像或者证件照素材。这时可以用wx.createCanvasContext或新版 Canvas 2D 把照片按框的坐标裁剪出来。但注意,使用 Canvas 处理这种大尺寸照片时,内存占用会比较大,建议先把照片压缩到合适的显示尺寸再裁剪,不然低端机很容易直接白屏。

我通常的做法是先用uni.compressImage把照片压到宽度不超过 1080,再做裁剪,既能保证清晰度,又能控制内存。如果不需要裁剪,只是原样上传,就直接把tempImagePath交给uni.uploadFile,省去 Canvas 这一步,性能和稳定性能好不少。

5. 常见问题与排查技巧,把这些坑提前帮你踩了

这一章是整篇文章最值钱的部分,全都是真机调试过程中一个坑一个坑踩出来的,遇到同样问题可以直接照方抓药。

5.1 摄像头调不起来,长时间黑屏

如果 camera 组件在真机上不显示画面,先不要怀疑代码,按这个顺序排查:

  1. 确认小程序有摄像头权限。如果用户之前在设置里关掉了权限,需要引导去打开。
  2. 确认不是体验版或开发版的基础库太老。VK 需要基础库在 2.16.0 以上,camera 的稳定性也会随基础库版本变化。
  3. 检查页面里是不是同时存在多个 camera 组件。部分安卓机同时创建多个 camera 会导致黑屏,尽量只保留一个。

还有一个比较容易忽略的点:camera组件不能是display: none的状态,也不能用v-if频繁销毁重建。我遇到过用户在页面里通过 tab 切换导致 camera 组件被销毁又重新创建,结果画面迟迟不出来的情况。解决办法是始终渲染 camera,需要隐藏时用cover-view盖一层遮罩,而不是销毁组件。

5.2 VK 初始化失败或 detect 没反应

wx.createVKSession初始化失败,最常见的原因就是当前小程序类目或基础库不支持。我在测试号上遇到过开发环境正常、体验版却初始化失败的情况,排查下来是基础库版本不一致。

另外,VK 的 Session 是重量级对象,页面onUnload时一定要销毁,不然后续页面不断创建 Session,内存会持续上涨,最终导致小程序白屏或卡死。销毁代码这样写:

onUnload() { if (this.vkTimer) { clearTimeout(this.vkTimer); this.vkTimer = null; } if (this.vkSession) { this.vkSession.destroy(); this.vkSession = null; } }

如果 detect 一直返回空数组,先看摄像头是否真的在出流。VK 依赖相机帧,如果 camera 黑屏,detect 结果一定是空的。优先解决摄像头出流问题。

5.3 取景框抖动、偏移、角度不对

这几类问题都属于坐标处理细节,我在 3.3 和 3.4 已经写了大半,这里再补两个容易忽略的点。

第一个是竖屏和横屏。如果页面只支持竖屏,VK 的检测坐标通常不需要额外旋转处理;但如果页面开了横屏,或者用户手机自动旋转,坐标就会乱。最简单的办法是锁定页面方向,竖屏场景下pageOrientation设为portrait,可以规避大量旋转问题。

第二个是前置摄像头的镜像。我在部分安卓机型上发现,VK 返回的人脸 x 坐标方向和实际画面是反向的。遇到这种情况,直接做一个x = 1 - x映射,再用映射后的坐标计算左右位置,问题立刻消失。但奇怪的是,同型号换一台设备可能又不需要。所以我建议写一个全局开关,方便线上出问题时迅速切换。

5.4 覆盖层被遮挡、点击穿透

如果你已经用的 cover-view 但按钮还是点不到,通常是因为 cover-view 的层级和 event 穿透设置问题。小程序原生组件和 cover-view 之间偶尔也会出现点击事件被吞掉的状况,尤其是按钮比较大、背景有透明区域时。

我的经验是给可点击的 cover-view 添加一个明确的背景色(哪怕接近透明),能明显减少点击事件的异常。另外,cover-view 内部不要嵌套多层 cover-view,层级越简单越稳定,嵌套过多在低端安卓机上非常容易触发渲染 Bug。

5.5 真机调试与体验版差异

这类功能在开发者工具里基本测不出真实问题,因为开发者工具里的相机画面是模拟的,VK 能力也不是完整生效。你必须用真机预览,并且看 console 日志来判断。

值得留意的是,微信开发者工具上的相机是虚拟摄像头,人脸检测的结果是假数据,不能作为功能验证依据。我遇到过开发者在工具里测得好好的,一上真机全废的情况,就是这个原因。所以每改一次坐标换算逻辑,都建议直接真机扫码验证,不要迷信工具。

另外,体验版和开发版的权限、基础库策略可能不同,如果体验版功能不可用,去「小程序管理后台-设置-基本设置-基础库最低版本」里检查设置,同时确认自己的微信号有足够的体验权限。

最后再分享两个小技巧

第一个是关于取景框动画的。cover-view 直接更新 left/top 属性在部分安卓机上会显得跳跃感很强,我给取景框加了一个极简的 CSS transition,让位置变化变得柔和。cover-view 对 transition 的支持有限,我实测下来只有 transform 的兼容性尚可,left 和 top 的 transition 时灵时不灵,所以最后我还是选择在 JS 层做平滑插值,而不是依赖 CSS。

第二个是关于多场景复用的。如果你做的产品里,用户先预览再拍照,拍完还有“重拍”的需求,一定要把 VK Session 的生命周期管理好。重拍如果只是重新进入页面,就把 camera 和 VK 统一初始化和销毁;如果是在同一页面内切换前后摄像头,需要先销毁 Session 重建 camera 组件再初始化 Session,否则很多机型上画面会停留在上一颗摄像头,这个坑我调试了整整一个下午。

人脸取景框摄像这个功能,拆开看都不算复杂,但每一个环节都跟设备硬件和平台特性强相关,稍不注意就会在一个小细节上卡很久。如果你也在做类似的需求,希望这篇文章能帮你把这些坑提前绕过去。做完微信小程序版本之后,后面的场景无非是加美颜、加活体检测、或者接上报后台,底层的页面和取景框逻辑都能复用,扩展起来并不难。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询