Skip to main content

用相机拍照

现在进入有趣的部分——使用 Capacitor Camera API 为设备的摄像头添加拍照功能。我们将从为网页构建开始,然后进行一些小调整,使其在移动设备(iOS 和 Android)上也能运行。

🌐 Now for the fun part - adding the ability to take photos with the device’s camera using the Capacitor Camera API. We’ll begin with building it for the web, then make some small tweaks to make it work on mobile (iOS and Android).

🌐 Photo Gallery Composable

我们将创建一个独立的组合方法,并与 Vue 的组合 API 配合,以管理图库的照片。

🌐 We will create a standalone composition method paired with Vue's Composition API to manage the photos for the gallery.

src/composables/usePhotoGallery.ts 创建一个新文件并将其打开。

🌐 Create a new file at src/composables/usePhotoGallery.ts and open it up.

接下来,定义一个新方法 usePhotoGallery(),其中将包含拍摄设备照片并将其保存到文件系统的核心逻辑。让我们先从打开设备摄像头开始。

🌐 Next, define a new method, usePhotoGallery(), that will contain the core logic to take a device photo and save it to the filesystem. Let’s start by opening the device camera.

import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';

export const usePhotoGallery = () => {
const addNewToGallery = async () => {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});
};

return {
addNewToGallery,
};
};

注意这里的魔力:没有针对特定平台的代码(Web、iOS 或 Android)!Capacitor Camera 插件为我们抽象了这些,只剩下一个方法调用 - Camera.getPhoto() - 它将打开设备的相机并允许我们拍照。

🌐 Notice the magic here: there's no platform-specific code (web, iOS, or Android)! The Capacitor Camera plugin abstracts that away for us, leaving just one method call - Camera.getPhoto() - that will open up the device's camera and allow us to take photos.

接下来,在 Tab2Page.vue 中,导入 usePhotoGallery() 方法并对其进行解构,以调用其 addNewToGallery() 方法。

🌐 Next, in Tab2Page.vue, import the usePhotoGallery() method and destructure it to call its addNewToGallery() method.

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Photo Gallery</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Photo Gallery</ion-title>
</ion-toolbar>
</ion-header>

<ion-fab vertical="bottom" horizontal="center" slot="fixed">

<ion-fab-button @click="addNewToGallery()">
<ion-icon :icon="camera"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-page>
</template>

<script setup lang="ts">
import { camera } from 'ionicons/icons';
import { IonPage, IonHeader, IonFab, IonFabButton, IonIcon, IonToolbar, IonTitle, IonContent } from '@ionic/vue';

// CHANGE: Add `usePhotoGallery` import
import { usePhotoGallery } from '@/composables/usePhotoGallery';

// CHANGE: Destructure `addNewToGallery` from `usePhotoGallery()
const { addNewToGallery } = usePhotoGallery();
</script>

如果开发服务器还没有运行,请通过在浏览器中运行 ionic serve 来重新启动。在照片图库标签上,点击相机按钮。如果你的电脑有任何类型的网络摄像头,会出现一个模态窗口。拍一张自拍!

🌐 If it's not running already, restart the development server in your browser by running ionic serve. On the Photo Gallery tab, click the Camera button. If your computer has a webcam of any sort, a modal window appears. Take a selfie!

A photo gallery app displaying a webcam selfie.

(你的自拍可能比我的好多了)

🌐 (Your selfie is probably much better than mine)

拍照后,它会立即消失。我们需要在应用内显示它,并保存以供将来访问。

🌐 After taking a photo, it disappears right away. We need to display it within our app and save it for future access.

显示照片

🌐 Displaying Photos

要为我们的照片元数据定义数据结构,创建一个名为 UserPhoto 的新接口。在 usePhotoGallery.ts 文件的最底部添加此接口,紧接在 usePhotoGallery() 方法定义之后。

🌐 To define the data structure for our photo metadata, create a new interface named UserPhoto. Add this interface at the very bottom of the usePhotoGallery.ts file, immediately after the usePhotoGallery() method definition.

export const usePhotoGallery = () => {
// ...existing code...
};

// CHANGE: Add the `UserPhoto` interface
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}

addNewToGallery() 方法上方,定义一个 UserPhoto 数组,该数组将包含对每张使用相机拍摄的照片的引用。使用 Vue 的 ref 方法 将其设为响应式变量。

🌐 Above the addNewToGallery() method, define an array of UserPhoto, which will contain a reference to each photo captured with the Camera. Make it a reactive variable using Vue's ref method.

export const usePhotoGallery = () => {
// CHANGE: Add the `photos` array
const photos = ref<UserPhoto[]>([]);

// ...existing code...
};

addNewToGallery() 方法中,将新捕获的照片添加到 photos 数组的开头。然后,使用 photos 数组更新 userPhotoGallery() 返回语句。

🌐 Over in the addNewToGallery() method, add the newly captured photo to the beginning of the photos array. Then, update the userPhotoGallery() return statement with the photos array.

// CHANGE: Add import
import { ref } from 'vue';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';

export const usePhotoGallery = () => {
const photos = ref<UserPhoto[]>([]);

const addNewToGallery = async () => {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});

// CHANGE: Create the `fileName` with current timestamp
const fileName = Date.now() + '.jpeg';
// CHANGE: Create `savedImageFile` matching `UserPhoto` interface
const savedImageFile = {
filepath: fileName,
webviewPath: capturedPhoto.webPath,
};

// CHANGE: Update the `photos` array with the new photo
photos.value = [savedImageFile, ...photos.value];
};

return {
addNewToGallery,
// CHANGE: Update return statement to include `photos` array
photos,
};
};

usePhotoGallery.ts 现在应该看起来像这样:

import { ref } from 'vue';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';

export const usePhotoGallery = () => {
const photos = ref<UserPhoto[]>([]);

const addNewToGallery = async () => {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});

const fileName = Date.now() + '.jpeg';
const savedImageFile = {
filepath: fileName,
webviewPath: capturedPhoto.webPath,
};

photos.value = [savedImageFile, ...photos.value];
};

return {
addNewToGallery,
photos,
};
};

export interface UserPhoto {
filepath: string;
webviewPath?: string;
}

接下来,切换到 Tab2Page.vue 以显示图片。我们将添加一个 Grid 组件 来确保照片在添加到图库时整齐显示。在网格内,循环遍历 UserPhotophotos 数组中的每张照片。对于每一项,添加一个 Image 组件,并将其 src 属性设置为照片的路径。

🌐 Next, switch to Tab2Page.vue to display the images. We'll add a Grid component to ensure the photos display neatly as they're added to the gallery. Inside the grid, loop through each photo in the UserPhoto's photos array. For each item, add an Image component and set its src property to the photo's path.

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Photo Gallery</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Photo Gallery</ion-title>
</ion-toolbar>
</ion-header>


<ion-grid>
<ion-row>

<ion-col size="6" :key="photo.filepath" v-for="photo in photos">
<ion-img :src="photo.webviewPath"></ion-img>
</ion-col>
</ion-row>
</ion-grid>

<ion-fab vertical="bottom" horizontal="center" slot="fixed">

<ion-fab-button @click="addNewToGallery()">
<ion-icon :icon="camera"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-page>
</template>

<script setup lang="ts">
import { camera, trash, close } from 'ionicons/icons';
import {
IonPage,
IonHeader,
IonFab,
IonFabButton,
IonIcon,
IonToolbar,
IonTitle,
IonContent,
IonGrid,
IonRow,
IonCol,
IonImg,
} from '@ionic/vue';

import { usePhotoGallery } from '@/composables/usePhotoGallery';

// CHANGE: Add `photos` array to destructure from `usePhotoGallery()`
const { photos, addNewToGallery } = usePhotoGallery();
</script>

在网页浏览器中,点击相机按钮,再拍一张照片。这一次,照片会显示在照片库中!

🌐 Within the web browser, click the camera button and take another photo. This time, the photo is displayed in the Photo Gallery!

接下来,我们将添加对将照片保存到文件系统的支持,以便稍后可以在我们的应用中检索和显示它们。

🌐 Up next, we’ll add support for saving the photos to the filesystem, so they can be retrieved and displayed in our app at a later time.