资讯动态

微信小程序录制视频:camera组件与wx.chooseMedia选型及上传实践

发布时间:2026/9/30 16:30:16 来源:尧图企业网站定制
1. 从 wx.chooseVideo 到 camera 组件录制方案到底该怎么选做过微信小程序录制视频功能的人多半都在选型这一步卡过壳。需求方一句要能录视频背后可能是完全不同的两套实现路径一套是调用系统的拍摄界面录完把文件拿回来另一套是自己用 camera 组件搭一个录制页从取景框到按钮全都自己画。这两条路能做的事、要写的代码量、踩的坑完全不在一个量级选错了后期返工的成本非常高。我前后做过直播答题、健身打卡、家校作业提交这几个场景的录制功能每次方案都不一样所以这里先把选型的判断逻辑讲透再往下讲具体的实现细节。先把这个功能的核心目标说清楚微信小程序录制视频本质上是让用户在授权相机和麦克风之后拿到一段视频的本地临时路径tempFilePath 或其变体然后再决定是预览、上传还是本地留存。围绕这个目标小程序提供了两条主线的能力一条是wx.chooseMedia老项目里常见的是wx.chooseVideo另一条是camera组件配合CameraContext。很多人一上来就抄一段 chooseVideo 的代码跑通之后发现产品要加个录制倒计时、要在取景框上叠一行提示文字、要限制只能录 15 秒并自动停结果发现自己根本没有控制权只能推倒重来。1.1 两条录制路径的能力边界差在哪wx.chooseMedia走的是借系统界面的路子。调用之后微信会拉起一个拍摄/相册选择的界面用户在里面录完点确认回调里给你一个临时文件信息包含 tempFilePath、size、duration、height、width、thumbTempFilePath 这些字段。这个过程里你几乎不参与取景和录制过程界面是系统给的录制时长、分辨率这些只能通过参数做有限的约束比如 maxDuration 限制最大时长camera 指定前后置。camera组件走的是自建录制页的路子。你在页面的 wxml 里放一个camera它就是一块实时的取景画面你可以在它上面叠 UI虽然原生组件有层级坑后面会讲然后通过wx.createCameraContext()拿到上下文对象调用startRecord开始录、stopRecord结束录。结束的时候回调会给你 tempThumbPath封面图和 tempVideoPath视频文件这两个路径才是你后续要用的东西。这条路的代价是你要自己处理权限申请、自己写录制按钮、自己处理超时停止、自己处理各种真机异常但换来的是完整的过程控制权。选型的判断其实就归结到几个问题。第一录制过程中用户需不需要看到业务相关的浮层比如要显示请朗读屏幕上的文字、要叠一个美颜滤镜、要显示题目的倒计时。有这类需求chooseMedia 直接出局。第二录制时长和分辨率的控制精度要求高不高chooseMedia 的 maxDuration 在真机上偶有不准的情况而且用户可以在系统界面里手动提前结束节奏不完全由你掌控camera 组件则可以直接用 timeoutCallback 做硬性截断。第三团队有没有精力维护一套自绘 UI如果只是最朴素的录一段传上去chooseMedia 半天就能交付没必要为了炫技去做 camera 组件。1.2 为什么官方一直在推 chooseMedia 而不是 chooseVideo老代码里满地都是wx.chooseVideo新项目里官方文档更推荐wx.chooseMedia。这不是简单的改名chooseMedia把图片和视频的选择收敛到一个 API 里用 mediaType 数组来指定你要哪种同时 sourceType 可以控制是从相册选还是现场拍。对录制场景来说最关键的变化是它能明确区分拍摄和相册选择这两件事。wx.chooseVideo时代用户点进去既可以拍也可以从相册翻一段已有的视频出来如果你的业务是现场作业打卡这种从相册选的能力反而是一个漏洞——用户完全可以从相册挑一段旧的视频交上来。所以在真实项目里如果你的场景对必须现场拍有强要求要么用 camera 组件把相册这条路彻底堵死要么在用 chooseMedia 时把 sourceType 设成只允许 camera拍摄不给相册留口子。这一点在作业提交、考勤打卡、外卖取餐这类场景里非常关键很多团队上线之后才被风控同学发现用户能上传相册里的旧视频回头补这个洞的成本远高于一开始就想清楚。我把两条路径的关键差异整理成一张表选型的时候对着看会清楚很多对比维度wx.chooseMediacamera 组件 CameraContext界面控制权系统界面几乎不可定制完全自绘想叠什么叠什么录制过程干预无法干预可做倒计时、超时自动停、实时提示相册视频混入风险存在需靠 sourceType 约束不存在只能现场录权限处理框架代为申请但拒绝后仍需兜底需自行申请 scope.camera / scope.record实现成本低几十行能跑通高涉及原生组件层级、真机兼容适用场景通用拍摄、发帖、简单收集打卡、作业、直播连麦前的本地录制选型做完方案定下来接下来真正动手的时候第一个拦路虎往往不是 API 怎么调而是权限。很多人写的代码在开发者工具里一路绿灯一上真机就黑屏不录十有八九就是权限链路没走通。2. 权限链路相机与录音的两道门小程序里的相机和麦克风属于敏感权限用户没点头你连取景框都点不亮。这套权限机制在真机上比工具里严格得多我见过太多工具里好好的、真机上一片漆黑的案例。要平稳地把这道门推开得先搞清楚它到底是两把锁还是一把锁——答案是两把scope.camera管摄像头scope.record管麦克风。哪怕你只是录一段没有声音的画面录音权限缺失也可能让部分机型在录制时直接报错或者录出一段无声文件具体表现因系统版本而异。2.1 scope.camera 与 scope.record 的申请时机小程序的权限申请有个硬规则必须在用户主动交互的回调里触发。你不能在 onLoad 或者页面一进来就默默调wx.authorize去要权限那样在真机上大概率会被静默拒绝或者直接失败。正确的做法是把申请动作挂在用户点击开始录制这个按钮的回调里或者放在进入录制页时那个明确的开启相机按钮上。标准的权限检查流程是三步走。第一步用wx.getSetting查一下当前授权状态看看authSetting[scope.camera]和authSetting[scope.record]分别是true、false还是undefined。undefined表示从没问过true表示已授权false表示用户明确拒绝过。这三种状态对应完全不同的处理逻辑undefined直接从交互回调里调wx.authorize发起申请系统会弹窗。true直接进入录制页不用再问。false这时候再调wx.authorize是无效的系统不会弹窗只能引导用户去设置页手动开。第二步拿到授权结果之后再决定是初始化 camera 组件还是走引导。这里有个细节容易被忽略wx.authorize一旦被用户在弹窗里点了拒绝这个权限在本次会话内就变false了你再调 authorize 也不会弹窗。所以权限被拒之后的兜底引导是必须写的不能省。第三步是把去设置页的动作串起来用wx.openSetting打开小程序的授权设置页让用户手动把相机和录音打开。用户从设置页回来之后在 onShow 里重新用 getSetting 检查一遍状态如果两个权限都齐了就把录制界面渲染出来。这套流程写下来大概是这样的结构// 检查并申请权限 async function ensurePermission() { const setting await wx.getSetting() const cameraAuth setting.authSetting[scope.camera] const recordAuth setting.authSetting[scope.record] // 已授权 if (cameraAuth recordAuth) return true // 从未申请过发起申请 if (cameraAuth undefined || recordAuth undefined) { try { await wx.authorize({ scope: scope.camera }) await wx.authorize({ scope: scope.record }) return true } catch (e) { return false } } // 被拒绝过需要引导去设置页 return false }注意这里我用wx.authorize的顺序是先 camera 后 record因为有些机型在同时申请两个权限时弹窗会连弹两次用户体验割裂分开申请在逻辑上更清晰。这里可以用 async/await 是因为新一代基础库对大部分 wx API 都做了 Promise 化支持如果你的基础库版本较老就得回退到 success/fail 回调的写法。2.2 用户拒绝之后的兜底处理用户拒绝权限这件事产品经理往往觉得不可能发生但实际数据里拒绝率并不低尤其是录音权限很多人出于隐私顾虑会直接点拒绝。所以拒绝之后的引导页一定要设计好不能只是干巴巴一句请开启权限。我一般会在拒绝之后弹一个自定义的弹窗不是wx.showModal是页面内的浮层把为什么需要这两个权限讲清楚然后给一个明确的去开启按钮触发wx.openSetting。这里有个真机上的坑要特别提醒iOS 上如果用户在系统层面把微信的相机权限关了wx.openSetting打开的页面里可能根本不显示相机这一个开关因为系统权限在小程序授权之上还有一层。这种情况只能提示用户去手机的设置 - 微信里把相机权限打开小程序层面无能为力。所以完整的兜底文案应该分两层小程序授权被拒引导去 openSetting如果 openSetting 之后还是不行再提示去系统设置。另一个容易踩的坑是权限申请和 camera 组件渲染的时序。camera 组件在页面渲染的时候就会尝试去拉摄像头如果这时候权限还没拿到真机上可能出现组件初始化失败、黑屏、或者 binderror 被触发。所以稳妥的做法是先把相机区域占位渲染成一张静态图加一个开启相机按钮等用户点击、权限拿到之后再通过条件渲染把真正的 camera 组件挂上去。这样既符合必须在交互中申请权限的规则也避免了权限没到位就硬渲染相机导致的异常。权限这道门走通了接下来才是 camera 组件本体的实操。这一块是整个功能的骨架我会把页面结构、上下文调用、录制时序完整地拆一遍。3. camera 组件录制实操从初始化到拿到临时文件真正动手写录制页的时候你会发现文档里的示例和生产环境里的实现差距挺大。文档给的是一段能跑通的最小 demo但真实项目要考虑录制按钮的状态管理、录制时长的实时反馈、超时自动停止、录制失败的重试、用户中途退出页面的清理。我按一个能直接上线的实现来讲把每一步为什么这么写都交代清楚。3.1 页面结构camera 组件与自定义 UI 的配合页面骨架分两块一块是 camera 组件负责取景另一块是叠在上面的操作区。wxml 大致是这样的结构要注意 camera 的各个属性都有明确用途不是随便写的view classrecord-page camera wx:if{{cameraReady}} device-position{{devicePosition}} flash{{flashMode}} frame-sizemedium resolutionmedium bindinitdoneonCameraInit binderroronCameraError classcamera-view /camera view classoverlay view classtimer{{recordTimeText}}/view view classtips{{tipText}}/view /view view classcontrol-bar view classbtn-switch bindtapswitchCamera翻转/view view classbtn-record {{isRecording ? recording : }} bindtaptoggleRecord {{isRecording ? 停止 : 开始}} /view /view /view这里有几个属性的取值需要解释。device-position控制前后置值只能是back或front。frame-size影响的是取景回调的帧数据尺寸如果你不做人脸识别或者实时图像处理用medium就够用large会白白增加内存压力低端机上有卡顿甚至崩溃的风险。resolution控制录制分辨率取值low、medium、high这个直接影响录出来的文件大小。我一般根据业务选作业类、打卡类用medium文件大小和清晰度能平衡需要看清细节的比如商品瑕疵拍摄才用high。3.2 CameraContext 的调用时序与录制流程拿到 camera 组件之后用wx.createCameraContext()创建上下文注意这个上下文是跟页面绑定的一个页面一个就够不要每次点录制都重新 create 一次重复创建在部分基础库上会拿到指向混乱的实例。整个录制流程是startRecord 开始stopRecord 结束并拿到结果中间用 timeoutCallback 处理超时。const ctx wx.createCameraContext() Page({ data: { isRecording: false, recordTime: 0, recordTimeText: 00:00, maxDuration: 15, tipText: 请保持面部在取景框内 }, toggleRecord() { if (this.data.isRecording) { this.stopRecord() } else { this.startRecord() } }, startRecord() { this.setData({ isRecording: true, recordTime: 0 }) this.timer setInterval(() { const t this.data.recordTime 1 this.setData({ recordTime: t, recordTimeText: formatTime(t) }) }, 1000) ctx.startRecord({ timeoutCallback: (res) { // 到达 maxDuration 自动停止返回临时文件 this.clearTimer() this.setData({ isRecording: false }) this.handleRecordResult(res) }, success: () {}, fail: (err) { this.clearTimer() this.setData({ isRecording: false }) wx.showToast({ title: 录制启动失败, icon: none }) console.error(startRecord fail, err) } }) }, stopRecord() { this.clearTimer() ctx.stopRecord({ success: (res) { this.setData({ isRecording: false }) this.handleRecordResult(res) }, fail: (err) { this.setData({ isRecording: false }) wx.showToast({ title: 录制结束失败, icon: none }) console.error(stopRecord fail, err) } }) }, handleRecordResult(res) { // res.tempThumbPath 封面图res.tempVideoPath 视频文件 this.setData({ videoPath: res.tempVideoPath, coverPath: res.tempThumbPath, showPreview: true }) }, clearTimer() { if (this.timer) { clearInterval(this.timer) this.timer null } }, onUnload() { this.clearTimer() } })这段代码里有几处是踩过坑才补上的。第一计时器一定要在组件卸载onUnload和停止录制时清掉否则页面退出了定时器还在跑轻则内存泄漏重则用户已经离开页面了却弹出录制结束的提示。第二timeoutCallback和success的职责要分清楚timeoutCallback 是到达你设定的 maxDuration 时框架主动停录并回调success 是你手动调 stopRecord 成功后的回调两者拿到的结果结构是一样的都带 tempThumbPath 和 tempVideoPath但触发路径不同都要处理。第三startRecord 的失败要捕获真机上权限被中途收回、摄像头被其他 App 占用都会在这里报错不处理的话用户会看到一个卡住的录制按钮。3.3 录制时长的两种控制方式别混着用录制时长这里有个设计上容易打架的地方maxDuration到底设多少、超时之后是自动完成还是允许用户提前结束。我的做法是统一一个上限比如 15 秒或 60 秒到达上限自动停并直接进入下一步同时允许用户随时手动点停止提前结束。这样用户想短录就短录超时了也不会无限录下去。要特别注意的是maxDuration是传给 startRecord 的一个参数或通过 CameraContext 的相关配置约束它的实际精度在不同机型上会有百毫秒到几百毫秒的误差所以你在 UI 上显示的秒数一定是以你自己的计时器为准不要试图去跟系统回调对齐。我见过有同学把 UI 显示的时长直接绑在超时回调上结果用户看到进度条走到一半突然就停了体验很差。计时器自己维护、显示自己算、停止以实际回调为准这三件事解耦开逻辑最清晰。另外录制的分辨率设置和实际输出文件大小直接相关。用medium分辨率录 15 秒文件通常在 2MB 到 5MB 之间浮动具体取决于画面复杂度high分辨率下同样时长可能到十几兆。如果你的后端对上传大小有限制或者用户网络环境差就要在录制前就把分辨率压下来而不是事后去压缩——压缩很费时用户等不起。录制拿到临时文件只是上半场下半场是怎么把这个文件展示给用户确认、然后稳妥地传上去。这两步看似简单实际也是坑比较集中的地方。4. 自定义录制界面那些原生 UI 给不了的东西选择 camera 组件本质上就是为了这个自定义的权利。但自绘 UI 在小程序里不是普通的前端绘图因为 camera 是原生组件它和普通 view 的层级关系跟你想的不一样稍微不注意就会遇到我明明写了浮层为什么被相机画面盖住了的经典问题。这一节把界面这块的关键点讲清楚。4.1 浮层的层级陷阱与 cover-view 的取舍在早期的微信基础库里原生组件camera、video、map、canvas、live-player 这些会浮在所有普通组件的最上层你在它上面叠普通 view 是没有用的会被相机画面直接盖住。官方的解决方案是cover-view和cover-image这两种组件能盖在原生组件之上。但 cover-view 的能力很有限它不支持很多 CSS 属性动画、复杂的 flex 布局、自定义字体有时候都不太听话写起来很别扭。好在较新的基础库版本放宽了这个限制普通 view 在大多数场景下已经可以正常叠加在 camera 之上同层渲染的兼容性好了很多。我现在的做法是先在目标基础库版本上实测如果普通 view 能正常显示在相机画面上就用普通 view省得跟 cover-view 的各种限制较劲如果发现某些机型上还是被盖住再回退到 cover-view。判断标准以真机为准工具里的表现不完全可信。如果确实要用 cover-view有几个约束得记牢cover-view 里只能嵌套 cover-view 和 cover-image你不能在里面塞一个普通的 view 或者 button文本必须放在 cover-view 里不能用 text它支持的基础 CSS 有限圆角、阴影这些效果可能不生效。我一般只在必须覆盖的极简 UI比如一行提示文字、一个录制计时上用它复杂的控件区还是尽量放在相机画面之外也就是相机只占页面的一部分操作按钮放在相机区域下面的普通区域里彻底绕开层级问题。4.2 前后置切换、镜像与闪光灯的实际表现device-position切换前后置的时候真机上会有一个短暂的画面黑一下再刷新这是正常的不用惊慌。但有个点要注意前置摄像头的画面默认是镜像的你看到自己的脸是左右反的这是取景预览的常见行为录出来的文件通常也是镜像的。对于打卡、人脸核验这类场景镜像与否通常不影响业务但如果你的业务对左右方向敏感比如要识别画面里的文字就得提前和产品确认清楚必要时在后端或前端做翻转处理。闪光灯这块flash属性支持auto、on、off、torch几个值。注意torch常亮手电在部分安卓机型上前置摄像头是不支持的切换的时候要做能力判断或者干脆只在后置模式下开放闪光灯控制。我踩过一次坑在前后置切换时没有重置 flash 状态导致切到前置之后 flash 还停留在torch结果前置相机莫名报错。后来改成每次切换 device-position 就把 flash 重置成off问题就没了。再说说取景框的宽高比和适配。camera 组件的默认比例和不同手机屏幕的宽高比差异会导致画面出现拉伸或黑边。稳妥的做法是给 camera 设一个固定的宽高比比如 3:4 或 9:16用容器约束住别让它随屏幕自由伸缩。同时要留意刘海屏、水滴屏这些异形屏对顶部提示文字的遮挡把重要信息放在安全区域内。这些细节单独看都是小事但叠在一起就是这个录制页看着不太专业和这个录制页很顺眼的区别。界面做顺了用户能顺畅地录完一段视频接下来的核心问题就变成了这段临时文件怎么预览、怎么压缩、怎么可靠地上传到服务端。这才是整个功能能不能真正落地的关键。5. 录制完成之后预览、压缩与上传录完视频拿到的tempVideoPath是一个本地临时路径它的有效期只在小程序本次运行期间页面刷新、小程序被杀掉之后就失效了。所以拿到路径之后要么尽快上传要么在本地留存一份。这一节把预览、压缩、上传三个方面拆开讲尤其是上传是问题最多的一环。5.1 预览用的 video 组件与临时路径的坑预览视频直接用一个video组件src 指向刚拿到的 tempVideoPath 就行。但这里有几个实际问题。第一video 组件本身也是原生组件同样有层级问题如果你在预览页还要叠 UI参考上一节的层级处理。第二预览页最好把录制好的封面图 tempThumbPath 作为 poster这样视频加载前不会是一片黑。第三用户的预览确认流程要设计好给重录和确认提交两个按钮重录就回到相机页重新来一遍。还有一点很关键临时文件在开发者工具和真机上的路径格式不一样。工具里拿到的是类似http://tmp/开头的本地路径真机上是wxfile://或者http://usr/这样的格式你在做路径判断、拼接或者传给后端做校验的时候绝对不能用工具里的路径做写死的判断否则一上真机就出问题。我的做法是拿到路径后一律不做格式假设直接原样透传给 video 组件或者上传接口让框架自己去解析。5.2 用 wx.uploadFile 上传的完整流程上传这件事最直接的方式是wx.uploadFile。它接受一个 filePath、一个 name服务端接收文件的字段名和 formData附带的其他参数通过 multipart/form-data 的方式把文件传上去。用起来很简单但生产环境里要考虑的远比一次调用多function uploadVideo(filePath, extraData) { return new Promise((resolve, reject) { const task wx.uploadFile({ url: https://your-domain.com/api/upload, filePath: filePath, name: video, formData: { token: extraData.token, bizType: homework, duration: extraData.duration }, timeout: 60000, success: (res) { if (res.statusCode 200) { try { resolve(JSON.parse(res.data)) } catch (e) { reject(new Error(返回数据解析失败)) } } else { reject(new Error(上传失败状态码 res.statusCode)) } }, fail: (err) { reject(err) } }) // 监听上传进度用于 UI 展示 task.onProgressUpdate((res) { console.log(上传进度, res.progress) }) }) }这段代码里timeout设成 60 秒是必要的视频文件几百 KB 到几兆弱网环境下很容易超时默认超时有时候太短。onProgressUpdate拿到的进度可以用来做进度条让用户知道还要等多久不然一个转圈圈转半天用户会以为卡死了。还有一点上传接口的域名必须提前在小程序后台的服务器域名里配置好开发阶段可以在开发者工具里勾选不校验合法域名但真机调试和上线之后必须配置这个坑几乎每个人都会踩一次。5.3 上传前的压缩什么时候该压、怎么压是不是所有视频都要压缩不一定。如果录的时候已经把分辨率设成medium、时长控制在 15 秒以内文件本身就不大直接传就行。真正需要压缩的是那些高分辨率、长时长、文件达到十几兆的场景。小程序的压缩能力有限比较实用的思路有两条一是在录制源头就把参数压下来低分辨率、短时长从根上减少文件体积二是如果确实需要处理已有文件可以用一些图像/视频处理的第三方能力但实话讲小程序端做视频压缩的性能和兼容性都不算理想能不在前端压就别在前端压。我更推荐的做法是前端只负责录一个合理大小、合理时长的视频把真正的转码、压缩、多清晰度生成放到服务端去做。前端做预处理服务端做后处理职责分明兼容性问题也少。前端这边要做的是在录制前用数据说话——比如告诉用户为节约流量将录制 15 秒标清视频让用户对结果有预期。上传成功了不代表万事大吉真正让开发者头疼的是各种只在真机才会冒出来的问题。下面这一节我把这几年踩过的典型坑整理成排查思路方便你遇到问题时能快速定位。6. 真机上才会暴露的坑几个典型问题的排查链路开发者工具是个温室它能跑通不代表真机能跑通。相机和麦克风这两个硬件相关的功能恰恰是工具和真机差异最大的地方。我按现象 - 排查 - 根因 - 修复的思路把几个高频问题讲一遍方便你照着自己遇到的现象对号入座。6.1 现象真机上面一片漆黑录制按钮点了没反应这是最高频的问题没有之一。排查链路应该这样走第一步确认权限是否真的拿到了。很多人以为自己在 onLoad 里申请了权限实际上真机上那个申请根本没弹窗或者被静默拒绝了。用 getSetting 把 authSetting 打出来看一眼最直接。第二步确认 camera 组件是不是在权限到位之前就渲染了。前面讲过正确顺序是先按钮占位 - 用户点击 - 申请权限 - 拿到后再渲染 camera。如果组件在权限没好时就挂载真机上初始化失败的表现就是黑屏。第三步看 binderror 有没有被触发把错误信息打出来常见的错误信息能指向具体原因比如摄像头被占用、初始化超时。还有一种黑屏是在开发者工具里预览正常但真机上取景框是黑的按钮却能点。这种往往是 camera 组件的层级或者尺寸问题——组件虽然初始化了但被别的东西盖住了或者宽高算出来是 0。检查一下 camera 容器的宽高有没有正确计算尤其是用了百分比高度又没给父容器定高的时候在真机上算出来可能是 0。6.2 现象iOS 能录、安卓录出来没声音或者反过来不同平台对录音权限和音频输入的处理差异是这类平台相关故障的根源。排查的时候先确认scope.record在出问题的那个平台上是不是真的授权了——用户可能只授权了相机没授权录音。其次要注意有些安卓机型在录制期间如果系统来了电话、或者其他 App 抢占音频焦点会出现录出来无声或者录制中断的情况这是系统层面的行为前端只能通过监听录制中断、给出友好提示来处理没法完全规避。我遇到的另一个平台差异是静音模式下录制的声音处理。iOS 设备如果开了静音有些场景下录制出来的音频表现和预期不同这个跟具体系统和微信版本都有关系。对付这类问题最靠谱的办法不是查文档而是列一台目标机型清单逐个真机实测。文档只能告诉你应该是什么样真机才能告诉你实际是什么样。6.3 现象录制中途切到后台回来之后状态错乱用户正在录视频的时候一个电话进来或者用户手滑切到了后台这时候 camera 组件会被系统挂起录制可能被中断但你的计时器还在跑isRecording 还是 true回来之后 UI 状态和实际状态完全对不上用户点停止也停不下来。这个问题在真机上很常见处理的核心是监听页面的隐藏事件。在onHide里要做两件事一是把计时器停掉二是把录制状态重置成未录制同时如果当时正在录制主动调一次 stopRecord 把已经录了的部分保存下来或者直接丢弃并提示用户重新录。光处理 onHide 还不够onShow回来的时候也要重新检查一遍相机状态因为切后台之后 camera 组件可能需要重新初始化。我一般会在 onShow 里做一次轻量的状态自检如果 isRecording 是 true 但页面已经重新显示说明中途出了状况直接重置状态并提示用户重录避免带着脏状态往下跑。除了上面这三个典型问题还有一个值得单独说的坑是原生组件的滚动穿透。当录制页或预览页里要滚动而页面上又放着 camera 或 video 这类原生组件时滚动行为可能跟预期不一致出现滚动到相机区域就卡住、或者页面整体滚动而相机不动的情况。处理办法是尽量让原生组件所在区域不参与整体滚动给它独立的固定布局把滚动限制在普通组件区域里。这些细节说到底都指向同一个原则凡是涉及原生组件的地方都别用普通组件的思维去假设它的行为一切以真机实测为准把目标机型列清楚一个机型一个机型地过才敢说这套录制功能是真的稳了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑