@capacitor/camera
Camera API 提供了使用相机拍照或从相册中选择现有照片的能力。
安装
npm install @capacitor/camera
npx cap sync
iOS
iOS 需要在 Info.plist 中添加并填写以下使用说明:
NSCameraUsageDescription(Privacy - Camera Usage Description)NSPhotoLibraryAddUsageDescription(Privacy - Photo Library Additions Usage Description)NSPhotoLibraryUsageDescription(Privacy - Photo Library Usage Description)
请参阅 iOS 指南 中的 配置 Info.plist 了解更多关于在 Xcode 中设置 iOS 权限的信息。
Android
从设备图库选取已有图片时,现在会使用 Android 图片选择器组件。图片选择器适用于满足以下条件的设备:
- 运行 Android 11(API 级别 30)或更高版本
- 通过 Google 系统更新接收模块化系统组件的更新
运行 Android 11 或 12 且支持 Google Play 服务的旧设备及 Android Go 设备可以安装向后移植版本的图片选择器。要启用通过 Google Play 服务自动安装向后移植的图片选择器模块,请在 AndroidManifest.xml 文件的 <application> 标签中添加以下条目:
<!-- Trigger Google Play services to install the backported photo picker module. -->
<!--suppress AndroidDomInspection -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
android:enabled="false"
android:exported="false"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
</intent-filter>
<meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>
如果未添加该条目,在不支持图片选择器的设备上,图片选择器组件会回退到 Intent.ACTION_OPEN_DOCUMENT。
Camera 插件不需要任何权限,除非使用 saveToGallery: true,在这种情况下,需要在 AndroidManifest.xml 中添加以下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
你也可以仅在需要请求这些权限的 Android 版本中指定它们:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>
存储权限用于读取/保存照片文件。
请参阅 Android 指南 中的 设置权限 了解更多关于设置 Android 权限的信息。
此外,由于 Camera API 会启动一个单独的 Activity 来处理拍照,你应该监听 App 插件中的 appRestoredResult 事件,以处理在 Activity 运行时应用被操作系统终止的情况下可能发送的任何相机数据。
变量
该插件将使用以下项目变量(定义在你应用的 variables.gradle 文件中):
androidxExifInterfaceVersion:androidx.exifinterface:exifinterface的版本(默认:1.4.1)androidxMaterialVersion:com.google.android.material:material的版本(默认:1.13.0)
PWA 注意事项
在 Web 上,takePhoto 可以使用 PWA Elements 的 pwa-camera-modal 自定义元素来提供类似原生的相机界面。如果该元素未注册,插件会回退到 <input type="file"> 选择器。chooseFromGallery 在 Web 上始终使用 <input type="file">, 无论是否安装了 PWA Elements。
以编程方式安装 PWA Elements
有关完整说明,请参阅 PWA Elements 安装指南。
提供自定义相机元素
你可以注册自己的 pwa-camera-modal 自定义元素,而不是使用 @ionic/pwa-elements。插件通过以下接口与其交互:
| 成员 | 类型 | 描述 |
|---|---|---|
facingMode | string 属性 | 在呈现之前设置为 'user'(前置摄像头)或 'environment'(后置摄像头) |
componentOnReady() | 方法 → Promise<void> | 插件创建元素后调用;当元素就绪时 resolve |
present() | 方法 | 插件调用以显示相机界面 |
dismiss() | 方法 | 插件在拍照或取消后调用以关闭相机界面 |
onPhoto | 事件 | 当用户拍照或取消时触发。event.detail 必须是 Blob(已拍照)、null(用户取消)或 Error(出错) |
class MyCameraModal extends HTMLElement {
facingMode = 'environment';
componentOnReady() {
return Promise.resolve();
}
present() {
// Show your custom camera UI, then dispatch exactly one 'onPhoto' event when done:
// - Blob: user took a photo
// - null: user cancelled
// - Error: something went wrong
// Example:
this.dispatchEvent(new CustomEvent('onPhoto', { detail: photoBlob }));
}
dismiss() {
// Hide your custom camera UI (called by the plugin after receiving 'onPhoto')
}
}
customElements.define('pwa-camera-modal', MyCameraModal);
示例
拍照
import { Camera } from '@capacitor/camera';
const takePicture = async () => {
try {
const result = await Camera.takePhoto({
quality: 90,
includeMetadata: true,
});
// result.webPath 可以直接设置为 image 元素的 src
imageElement.src = result.webPath;
// 原生平台:将 result.uri 传递给 Filesystem API 以获取完整分辨率的 base64 数据,
// 或使用 result.thumbnail 获取较低分辨率的 base64 预览。
// Web 平台:result.thumbnail 包含完整图片的 base64 编码。
console.log('Format:', result.metadata?.format);
console.log('Resolution:', result.metadata?.resolution);
} catch (e) {
const error = e as any;
// error.code 包含结构化的错误代码(例如 'OS-PLUG-CAMR-0003')
// 当错误由原生层抛出时。有关所有代码,请参阅“错误”部分。
const message = error.code ? `[${error.code}] ${error.message}` : error.message;
console.error('takePhoto failed:', message);
}
};
从相册中选择
import { Camera, MediaTypeSelection } from '@capacitor/camera';
const pickMedia = async () => {
try {
const { results } = await Camera.chooseFromGallery({
mediaType: MediaTypeSelection.All, // 照片、视频或两者都选
allowMultipleSelection: true,
limit: 5,
includeMetadata: true,
});
for (const item of results) {
console.log('Type:', item.type); // MediaType.Photo 或 MediaType.Video
console.log('webPath:', item.webPath);
console.log('Format:', item.metadata?.format);
console.log('Size:', item.metadata?.size);
}
} catch (e) {
const error = e as any;
const message = error.code ? `[${error.code}] ${error.message}` : error.message;
console.error('chooseFromGallery failed:', message);
}
};
录制并播放视频
import { Camera } from '@capacitor/camera';
const recordAndPlay = async () => {
let videoUri: string | undefined;
try {
const result = await Camera.recordVideo({
saveToGallery: false,
isPersistent: true, // 让文件在应用多次启动后依然可用
includeMetadata: true,
});
videoUri = result.uri;
console.log('Duration:', result.metadata?.duration);
console.log('Saved to gallery:', result.saved);
} catch (e) {
const error = e as any;
const message = error.code ? `[${error.code}] ${error.message}` : error.message;
console.error('recordVideo failed:', message);
return;
}
if (videoUri) {
try {
await Camera.playVideo({ uri: videoUri });
} catch (e) {
const error = e as any;
const message = error.code ? `[${error.code}] ${error.message}` : error.message;
console.error('playVideo failed:', message);
}
}
};
编辑来自 base64 字符串的照片
editPhoto 从 base64 编码的图片打开应用内编辑器,并在 outputImage 中返回编辑后的图片(也是 base64 字符串)。
import { Camera } from '@capacitor/camera';
const editFromBase64 = async (base64Image: string) => {
try {
const { outputImage } = await Camera.editPhoto({
inputImage: base64Image, // 纯 base64,不含 data URL 前缀
});
// outputImage 是编辑后的图片,base64 编码
imageElement.src = `data:image/jpeg;base64,${outputImage}`;
} catch (e) {
const error = e as any;
const message = error.code ? `[${error.code}] ${error.message}` : error.message;
console.error('editPhoto failed:', message);
}
};
编辑来自 URI 的照片
editURIPhoto 从文件 URI(例如从 takePhoto 或 Filesystem API 获取)打开应用内编辑器,并返回 MediaResult。
import { Camera } from '@capacitor/camera';
const editFromURI = async (uri: string) => {
try {
const result = await Camera.editURIPhoto({
uri,
saveToGallery: false,
includeMetadata: true,
});
// result.webPath 可以直接用作图片的 src
imageElement.src = result.webPath;
console.log('Format:', result.metadata?.format);
console.log('Size:', result.metadata?.size);
console.log('Saved to gallery:', result.saved);
} catch (e) {
const error = e as any;
const message = error.code ? `[${error.code}] ${error.message}` : error.message;
console.error('editURIPhoto failed:', message);
}
};
迁移到新版 API
版本 8.1.0 引入了新的改进 API,并弃用了 getPhoto 和 pickImages。
替换 getPhoto
getPhoto 通过 CameraSource 处理三种来源:Camera、Photos 和 Prompt。Camera 和 Photos 现在映射到不同的方法,而 Prompt 已被移除。
CameraSource.Camera 改为 takePhoto
新版 API 不 支持 CameraResultType.Base64 和 CameraResultType.DataUrl。替代方案请参阅结果类型的变化。
// 之前
const photo = await Camera.getPhoto({
source: CameraSource.Camera,
quality: 90,
allowEditing: true,
resultType: CameraResultType.Uri,
direction: CameraDirection.Rear,
width: 1280,
height: 720,
});
const imageUrl = photo.webPath;
// 之后
const result = await Camera.takePhoto({
quality: 90,
editable: 'in-app', // 替代 allowEditing: true
cameraDirection: CameraDirection.Rear, // 替代 direction
targetWidth: 1280, // 替代 width (1)
targetHeight: 720, // 替代 height (1)
});
const imageUrl = result.webPath;
(1) width/height 各自独立工作,设置一个最大尺寸的同时保持宽高比。targetWidth/targetHeight 必须同时使用——只设置其中一个无效。
CameraSource.Photos 改为 chooseFromGallery
// 之前
const photo = await Camera.getPhoto({
source: CameraSource.Photos,
quality: 90,
resultType: CameraResultType.Uri,
});
const imageUrl = photo.webPath;
// 之后
const { results } = await Camera.chooseFromGallery({
quality: 90,
});
const imageUrl = results[0].webPath;
CameraSource.Prompt(或默认情况)
getPhoto 之前会显示一个原生提示框,让用户在相机和相册之间选择。这个提示框不再是插件的一部分。你应该使用自己的 UI 来构建选择提示(例如使用 @capacitor/action-sheet),然后根据用户的选择调用 takePhoto 或 chooseFromGallery。
// 之前
const photo = await Camera.getPhoto({
// source 默认为 CameraSource.Prompt
quality: 90,
resultType: CameraResultType.Uri,
});
// 之后:用你自己的 UI 确定来源,然后调用对应的方法
const result = await Camera.takePhoto({ quality: 90 });
// 或者
const { results } = await Camera.chooseFromGallery({ quality: 90 });
结果类型的变化
getPhoto 返回一个 Photo 对象,其中可用的字段取决于 resultType。新版 API 完全移除了 resultType——无论照片如何拍摄,MediaResult 都有一组固定的字段。
Photo 字段 | MediaResult 对应项 |
|---|---|
path | uri |
webPath | webPath |
base64String | thumbnail(在 Web 上,包含完整的 base64 编码图片;在原生端,包含缩略图) |
dataUrl | 无直接对应项——参见下方说明 |
saved | saved |
format | metadata.format(需要 includeMetadata: true) |
exif | metadata.exif(需要 includeMetadata: true) |
构建 data URL —— 根据你的需求有两种方式可选:
在所有平台上,你可以结合 thumbnail 和 metadata.format(需要 includeMetadata: true)。在原生端,thumbnail 是较低分辨率的:
const dataUrl = `data:image/${result.metadata.format};base64,${result.thumbnail}`;
在原生端,如果你需要完整分辨率的 base64,可以通过 Filesystem API 读取 uri,并从中构建 data URL:
import { Filesystem } from '@capacitor/filesystem';
const { data } = await Filesystem.readFile({ path: result.uri });
const dataUrl = `data:image/${result.metadata.format};base64,${data}`;
替换 pickImages → chooseFromGallery
pickImages 允许从相册中选择多张照片。向 chooseFromGallery 传入 allowMultipleSelection: true 即可获得相同行为。
// 之前
const { photos } = await Camera.pickImages({
quality: 90,
limit: 5,
width: 1280,
height: 720,
});
for (const photo of photos) {
console.log(photo.webPath);
}
// 之后
const { results } = await Camera.chooseFromGallery({
allowMultipleSelection: true,
quality: 90,
limit: 5,
targetWidth: 1280, // 替换 width (1)
targetHeight: 720, // 替换 height (1)
});
for (const result of results) {
console.log(result.webPath);
}
(1) width/height 各自独立工作,设置的是最大 尺寸并保持宽高比。targetWidth/targetHeight 必须同时使用——仅设置一个无效。
chooseFromGallery 也可以通过设置 mediaType 为 MediaTypeSelection.Video 或 MediaTypeSelection.All 来选择视频或混合媒体。
选项重命名对照表
| 旧选项 | 新选项 | 适用范围 |
|---|---|---|
width | targetWidth (1) | takePhoto, chooseFromGallery |
height | targetHeight (1) | takePhoto, chooseFromGallery |
direction | cameraDirection | takePhoto |
allowEditing | editable: 'in-app' | takePhoto, chooseFromGallery |
resultType | — (已移除,见结果类型变更) | — |
source | — (已移除,请使用独立方法) | — |
promptLabel* | — (已移除,请自行构建 UI) | — |
(1) width/height 各自独立工作,设置的是最大尺寸并保持宽高比。targetWidth/targetHeight 必须同时使用——仅设置一个无效。
API
有关现有错误代码的列表,请参阅错误。
takePhoto(...)
takePhoto(options: TakePhotoOptions) => Promise<MediaResult>
打开设备的相机,允许用户拍照。
| 参数 | 类型 |
|---|---|
options | |
返回:
Promise<MediaResult>
自版本: 8.1.0
recordVideo(...)
recordVideo(options: RecordVideoOptions) => Promise<MediaResult>
打开设备的相机,允许用户录制视频。 Web 平台不可用。
| 参数 | 类型 |
|---|---|
options | |
返回:
Promise<MediaResult>
自版本: 8.1.0
playVideo(...)
playVideo(options: PlayVideoOptions) => Promise<void>
打开原生视频播放器。 Web 平台不可用。
| 参数 | 类型 |
|---|---|
options | |
自版本: 8.1.0
chooseFromGallery(...)
chooseFromGallery(options: ChooseFromGalleryOptions) => Promise<MediaResults>
允许用户直接从相册中选择图片、视频或两者同时。
| 参数 | 类型 |
|---|---|
options | |
返回:
Promise<MediaResults>
自版本: 8.1.0
editPhoto(...)
editPhoto(options: EditPhotoOptions) => Promise<EditPhotoResult>
打开应用内编辑界面,使用提供的 base64 字符串编辑图片。 Web 平台不可用。
| 参数 | 类型 |
|---|---|
options | |
返回:
Promise<EditPhotoResult>
自版本: 8.1.0
editURIPhoto(...)
editURIPhoto(options: EditURIPhotoOptions) => Promise<MediaResult>
打开应用内编辑界面,使用提供的 URI 编辑图片。 Web 平台不可用。
| 参数 | 类型 |
|---|---|
options | |
返回:
Promise<MediaResult>
自版本: 8.1.0
pickLimitedLibraryPhotos()
pickLimitedLibraryPhotos() => Promise<GalleryPhotos>
允许用户更新其受限的相册选择。 返回选择器关闭后的所有受限照片。 如果用户改为授予相册完整访问权限,则返回空数组。
返回:
Promise<GalleryPhotos>
自版本: 4.1.0
getLimitedLibraryPhotos()
getLimitedLibraryPhotos() => Promise<GalleryPhotos>
返回从受限相册库中选择的照片数组。
返回:
Promise<GalleryPhotos>
自版本: 4.1.0
checkPermissions()
checkPermissions() => Promise<PermissionStatus>
检查相机和相册权限
返回:
Promise<PermissionStatus>
自版本: 1.0.0
requestPermissions(...)
requestPermissions(permissions?: CameraPluginPermissions | undefined) => Promise<PermissionStatus>
请求相机和相册权限