在鸿蒙 ASCF 中,相机相关 API 的核心方法只有三个:takePhoto 拍照、startRecord 开始录像、stopRecord 结束录像。前后置切换、闪光灯、对焦等并不在 API 层提供,而是通过 camera 组件的属性控制。实际开发时,权限声明、camera 组件事件和临时路径处理反而更容易踩坑。
权限先配全:CAMERA 与 MICROPHONE
相机 API 需要两个系统权限,在 module.json5 的 requestPermissions 中声明:- {
- "requestPermissions": [
- { "name": "ohos.permission.CAMERA" },
- { "name": "ohos.permission.MICROPHONE" }
- ]
- }
复制代码 拍照只需要 CAMERA,录像还需要 MICROPHONE,因为要录音。随后在代码中用 has.authorize 请求授权:- has.authorize({
- scope: 'scope.camera',
- success: () => { console.info('相机权限已授权'); },
- fail: (err) => { console.error('授权失败:', err); }
- });
- has.authorize({
- scope: 'scope.record',
- success: () => { console.info('录音权限已授权'); },
- fail: (err) => { console.error('授权失败:', err); }
- });
复制代码 如果没拿到权限就调用相机 API,会直接进入 fail 回调。原文作者就因为没有先配权限,调了半天一直失败。
创建相机上下文
相机控制通过 CameraContext 完成。createCameraContext 不需要传参数,也不像 VideoContext 那样需要组件 id:- Page({
- onReady() {
- this.ctx = has.createCameraContext();
- }
- });
复制代码 注意要在页面加载完成后创建,放在 onLoad 或 onReady 中都可以。
拍照:takePhoto 的三个参数关注点- this.ctx.takePhoto({
- quality: 'high',
- selfieMirror: false,
- success: (res) => {
- console.info('照片路径:', res.tempImagePath);
- },
- fail: (err) => {
- console.error('拍照失败:', err);
- }
- });
复制代码 quality 控制成像质量,有 high、normal、low 三档。实际体验 normal 够用,high 文件大不少,上传会更慢。selfieMirror 控制前置摄像头是否镜像,默认 true;自拍功能保持默认即可,若需要与后置方向一致可设为 false。拍照结果通过 res.tempImagePath 返回。
录像:startRecord 与 stopRecord
开始录像:- this.ctx.startRecord({
- timeout: 30,
- timeoutCallback: (res) => {
- console.info('录像超时,视频路径:', res.tempVideoPath);
- console.info('封面路径:', res.tempThumbPath);
- },
- success: () => {
- console.info('开始录像');
- },
- fail: (err) => {
- console.error('录像失败:', err);
- }
- });
复制代码 timeout 是录制时长上限,单位秒,默认 30,最长不能超过 5 分钟,也就是 300 秒。timeoutCallback 会在两种场景触发:录制时间达到 timeout 后自动停止,或者录像异常退出。返回值里包含 tempVideoPath 和 tempThumbPath。
结束录像:- this.ctx.stopRecord({
- success: (res) => {
- console.info('视频路径:', res.tempVideoPath);
- console.info('封面路径:', res.tempThumbPath);
- },
- fail: (err) => {
- console.error('停止录像失败:', err);
- }
- });
复制代码 stopRecord 的返回值和 timeoutCallback 一致,同样有视频路径和封面路径。一个按钮控制开始和停止时,可以用 isRecording 状态切换。
camera 组件负责预览和属性控制
API 只操作相机逻辑,画面预览要靠 camera 组件:- <camera flash="off" mode="normal" device-position="back" frame-size="medium" binderror="onCameraError" bindstop="onCameraStop" bindinitdone="onCameraInit" />
复制代码 常用属性方面,flash 控制闪光灯,mode 一般用 normal,device-position 控制前后置,frame-size 控制帧大小。组件事件里 binderror 一定要处理,否则用户拒绝相机权限时无法感知:- onCameraError(e) {
- console.error('相机错误:', e.detail);
- has.showToast({ title: '无法访问相机' });
- }
复制代码 前后置切换也不是 API,而是通过 camera 组件的 device-position 属性完成,例如在 data 中维护 position,再绑定到 device-position。
实际场景中的做法
证件拍照上传:takePhoto 建议用 quality 为 high,保证文字清晰;拿到 tempImagePath 后可用 has.getImageInfo 获取宽高,再上传服务器。短视频录制:startRecord 的 timeout 可设为 15 秒,超时回调中停止计时并处理 tempVideoPath;手动 stopRecord 成功后同样处理视频,可用 has.getVideoInfo 获取时长和分辨率。前后置切换:只更新绑定到 camera 组件的 device-position 值,不需要调用额外 API。
容易踩的坑
第一,权限不全导致录像没声音。拍照只需要 CAMERA,录像还需要 MICROPHONE,两个权限都要在 module.json5 声明并请求授权。
第二,takePhoto 的 quality 大小写。文档写的是 high、normal、low 小写,Demo 里用过 HIGH、MEDIUM、LOW 大写,实测两种都能用,但建议按文档使用小写。
第三,startRecord 的 timeout 不要超过 300 秒。超过 5 分钟可能被截断或报错,需要更长录制时间时可以考虑分段录制。
第四,临时路径要尽快使用。takePhoto 返回的 tempImagePath、stopRecord 返回的 tempVideoPath 都是临时路径;需要保存时及时调用 saveImageToPhotosAlbum 或 saveVideoToPhotosAlbum,临时文件可能在应用重启后被清理。
第五,camera 组件的 mode 不要随便设成 scanCode。扫码模式下拍照功能可能不正常,普通拍照录像用 mode="normal"。
第六,frame-size 影响清晰度和性能。small 低清晰度省性能,medium 中等,large 高清晰度更费性能;扫码场景 small 够用,拍照建议 medium 或 large。
Demo 与方案选择
原文项目中包含两个相机相关 Demo:组件级 Demo 展示 camera 组件基本用法;API 级 Demo 展示 CameraContext 的拍照和录像功能,包括三档画质拍照、开始/结束录像、超时自动停止、拍照结果预览和录像结果预览。
如果只是需要拍照或选视频,不一定要使用 camera 组件加 CameraContext。has.chooseImage 和 has.chooseVideo 也能调起系统相机,用法更简单。只有需要自定义相机界面,比如加滤镜、水印、实时预览时,才需要自己搭建 camera 组件并结合这套 API。 |