Bài 06: Taking Photos with the Camera,Saving Photo

Bài 06: Taking Photos with the Camera,Saving Photo

  

Bây giờ là phần thú vị - thêm khả năng chụp ảnh bằng máy ảnh của thiết bị bằng API máy ảnh tụ điện . Chúng tôi sẽ bắt đầu với việc xây dựng nó cho web, sau đó thực hiện một số chỉnh sửa nhỏ để làm cho nó hoạt động trên thiết bị di động (iOS và Android).

Để làm như vậy, chúng tôi sẽ tạo móc React tùy chỉnh của riêng mình để quản lý ảnh cho thư viện.

Tạo một tệp mới tại src/hooks/usePhotoGallery.tsvà mở nó lên.

Một hook tùy chỉnh chỉ là một hàm sử dụng các hook React khác. Và đó là những gì chúng tôi sẽ làm! Chúng ta sẽ bắt đầu bằng cách nhập các hook và tiện ích khác nhau mà chúng ta sẽ sử dụng từ React core, dự án Ionic React Hooks và Capacitor:

  import { useStateuseEffect } from "react";
  import { useCamera } from '@ionic/react-hooks/camera';
  import { useFilesystembase64FromPath } from '@ionic/react-hooks
                                                    /filesystem';
  import { useStorage } from '@ionic/react-hooks/storage';
  import { isPlatform } from '@ionic/react';
  import { CameraResultTypeCameraSourceCameraPhotoCapacitor,
 FilesystemDirectory } from "@capacitor/core";

Tiếp theo, tạo một hàm có tên usePhotoGallery:


  export function usePhotoGallery() {

    const { getPhoto } = useCamera();
  
    const takePhoto = async () => {
      const cameraPhoto = await getPhoto({
        resultTypeCameraResultType.Uri,
        sourceCameraSource.Camera,
        quality100
      });
    };
  
    return {
      takePhoto
    };
  }

usePhotoGalleryHook của chúng tôi cho thấy một phương thức gọi là takePhoto, phương thức này lần lượt gọi vào phương thức getPhoto của Capactior.

Lưu ý điều kỳ diệu ở đây: không có mã dành riêng cho nền tảng (web, iOS hoặc Android)! Plugin Capacitor Camera sẽ tóm tắt điều đó cho chúng tôi, chỉ để lại một lệnh gọi phương thức - getPhoto()- sẽ mở máy ảnh của thiết bị và cho phép chúng tôi chụp ảnh.

Bước cuối cùng chúng ta cần làm là sử dụng hook mới từ trang Tab2. Quay lại Tab2.tsx và nhập hook:

   import { usePhotoGallery } from '../hooks/usePhotoGallery';

Và ngay trước câu lệnh return trong thành phần chức năng, hãy truy cập vào takePhoto phương thức bằng cách sử dụng hook:

  const Tab2React.FC = () => {
    const { takePhoto } = usePhotoGallery();
  
    // snip - rest of code

Lưu tệp và nếu bạn chưa lưu, hãy khởi động lại máy chủ phát triển trong trình duyệt của bạn bằng cách chạy ionic serve. Trên tab Thư viện ảnh, nhấp vào nút Máy ảnh. Nếu máy tính của bạn có bất kỳ loại webcam nào, một cửa sổ phương thức sẽ xuất hiện. Chụp ảnh tự sướng!

Sau khi chụp ảnh, nó biến mất. Chúng tôi vẫn cần hiển thị nó trong ứng dụng của mình và lưu nó để truy cập trong tương lai.

Hiển thị ảnh

Đầu tiên, chúng tôi sẽ tạo một loại mới để xác định Ảnh của chúng tôi, loại này sẽ chứa siêu dữ liệu cụ thể. Thêm giao diện Ảnh sau vào usePhotoGallery.tstệp, ở đâu đó bên ngoài chức năng chính:

  export interface Photo {
    filepathstring;
    webviewPath?string;
  }

Quay lại đầu hàm (ngay sau lệnh gọi tới useCamera, chúng ta sẽ xác định một biến trạng thái để lưu trữ mảng của từng ảnh được chụp bằng Máy ảnh.

  const [photossetPhotos= useState<Photo[]>([]);

Khi máy ảnh chụp ảnh xong, CameraPhoto kết quả trả về từ Tụ sẽ được lưu trữ trong photo biến. Chúng tôi muốn tạo một đối tượng ảnh mới và thêm nó vào mảng trạng thái ảnh. Chúng tôi đảm bảo rằng chúng tôi không vô tình làm thay đổi mảng ảnh hiện tại bằng cách tạo một mảng mới, sau đó gọi setPhotosđể lưu trữ mảng ở trạng thái. Cập nhật takePhotophương thức và thêm mã này sau lệnh gọi getPhoto:

  const fileName = new Date().getTime() + '.jpeg';
  const newPhotos = [{
    filepathfileName,
    webviewPathcameraPhoto.webPath
  }, ...photos];
  setPhotos(newPhotos)

Tiếp theo, hãy phơi bày mảng ảnh từ móc của chúng ta. Cập nhật báo cáo trả hàng để bao gồm các ảnh:

  return {
    photos,
    takePhoto
  };

Và trở lại thành phần Tab2, có quyền truy cập vào ảnh:

  const { photostakePhoto } = usePhotoGallery();

Với (các) ảnh được lưu trữ vào mảng chính, chúng ta có thể hiển thị ảnh trên màn hình. Thêm thành phần Lưới để mỗi ảnh sẽ hiển thị đẹp khi ảnh được thêm vào thư viện và lặp qua từng ảnh trong mảng Ảnh, thêm thành phần Ảnh ( <IonImg>) cho mỗi ảnh. Trỏ src(nguồn) vào đường dẫn của ảnh:

   <IonContent>

  <IonGrid>
    <IonRow>
      {photos.map((photoindex=> (
        <IonCol size="6" key={index}>
          <IonImg src={photo.webviewPath/>
        </IonCol>
      ))}
    </IonRow>
  </IonGrid>
   <!-- <IonFab> markup  -->
</IonContent>

Lưu tất cả các tệp. Trong trình duyệt web, nhấp vào nút Máy ảnh và chụp một ảnh khác. Lần này, ảnh được hiển thị trong Thư viện ảnh!

Tiếp theo, chúng tôi sẽ bổ sung hỗ trợ lưu ảnh vào hệ thống tệp để chúng có thể được truy xuất và hiển thị trong ứng dụng của chúng tôi sau này.

IPA hệ thống tệp

May mắn thay, việc lưu chúng vào hệ thống tập tin chỉ mất một vài bước. Bắt đầu bằng cách mở usePhotoGalleryhook ( src/hooks/usePhotoGallery.ts) và truy cập vào writeFilephương thức từ useFileSystemhook:

const { deleteFilegetUrireadFilewriteFile } = useFilesystem();

Tiếp theo, tạo một vài hàm mới trong usePhotoGallery:

 const savePicture = async (photoCameraPhotofileNamestring): Promise<Photo=> {
    const base64Data = await base64FromPath(photo.webPath!);
    const savedFile = await writeFile({
      pathfileName,
      database64Data,
      directoryFilesystemDirectory.Data
    });
  
    // Use webPath to display the new image instead of base64 since it's
    // already loaded into memory
    return {
      filepathfileName,
      webviewPathphoto.webPath
    };
  };

Chúng tôi truyền vào cameraPhotođối tượng, đại diện cho ảnh thiết bị mới được chụp, cũng như tên tệp, sẽ cung cấp đường dẫn cho tệp được lưu trữ.

Tiếp theo, chúng tôi sử dụng API hệ thống tệp tụ điện để lưu ảnh vào hệ thống tệp. Chúng tôi bắt đầu bằng cách chuyển đổi ảnh sang định dạng base64, sau đó cung cấp dữ liệu vào writeFilechức năng của Hệ thống tập tin .

Cuối cùng, gọi savePicturevà chuyển đối tượng cameraPhoto và tên tệp ngay bên dưới lệnh gọi đến setPhotostrong takePhotophương thức. Đây là phương pháp đầy đủ:

 



 const takePhoto = async () => {
  const cameraPhoto = await getPhoto({
    resultTypeCameraResultType.Uri,
    sourceCameraSource.Camera,
    quality100
  });

  const fileName = new Date().getTime() + '.jpeg';
  const savedFileImage = await savePicture(cameraPhotofileName);
  const newPhotos = [savedFileImage, ...photos];
  setPhotos(newPhotos);
};

Bắt đầu! Mỗi khi một bức ảnh mới được chụp, ảnh sẽ tự động được lưu vào hệ thống tệp.

Lưu ảnh vào hệ thống tệp

Giờ đây, chúng tôi có thể chụp nhiều ảnh và hiển thị chúng trong thư viện ảnh trên tab thứ hai của ứng dụng. Tuy nhiên, những bức ảnh này hiện không được lưu trữ vĩnh viễn, vì vậy khi ứng dụng đóng lại, chúng sẽ bị mất.

May mắn thay, điều này rất dễ dàng: chúng tôi sẽ tận dụng API lưu trữ tụ điện để lưu trữ mảng Ảnh của chúng tôi trong kho khóa-giá trị.

 API hệ thống tệp

May mắn thay, việc lưu chúng vào hệ thống tập tin chỉ mất một vài bước. Bắt đầu bằng cách mở usePhotoGalleryhook ( src/hooks/usePhotoGallery.ts) và truy cập vào phương thức từ hook:


  const PHOTO_STORAGE = "photos";
  export function usePhotoGallery() {}

Sau đó, sử dụng useStoragehook để truy cập vào các phương thức get và set tiện dụng để đọc và ghi vào bộ nhớ thiết bị:

  const { getset } = useStorage ();

Tiếp theo, ở cuối takePhotohàm, hãy thêm lệnh gọi đến set()để lưu mảng Ảnh. Bằng cách thêm nó vào đây, mảng Ảnh được lưu trữ mỗi khi chụp ảnh mới. Bằng cách này, không thành vấn đề khi người dùng ứng dụng đóng hoặc chuyển sang một ứng dụng khác - tất cả dữ liệu ảnh đều được lưu.

  set(PHOTO_STORAGEJSON.stringify(newPhotos));

Với dữ liệu mảng ảnh đã được lưu, chúng ta sẽ tạo một phương thức lấy dữ liệu khi hook tải. Chúng tôi sẽ làm như vậy bằng cách sử dụng useEffecthook của React . Chèn cái này phía trên takePhototờ khai. Đây là mã, và chúng tôi sẽ chia nhỏ nó:


  useEffect(() => {
    const loadSaved = async () => {
      const photosString = await get(PHOTO_STORAGE);
      const photos = (photosString ? JSON.parse(photosString: []) as Photo[];
      for (let photo of photos) {
        const file = await readFile({
          pathphoto.filepath,
          directoryFilesystemDirectory.Data
        });
        photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
      }
      setPhotos(photos);
    };
    loadSaved();
  }, [getreadFile]);

Điều này có vẻ hơi đáng sợ lúc đầu, vì vậy chúng ta hãy đi qua nó, đầu tiên bằng cách xem tham số thứ hai mà chúng ta truyền vào hook: mảng phụ thuộc [get, readFile].

Theo mặc định, hook useEffect được gọi mỗi khi một thành phần hiển thị, trừ khi, chúng ta truyền vào một mảng phụ thuộc. Trong trường hợp đó, nó sẽ chỉ chạy khi một phần phụ thuộc được cập nhật. Trong trường hợp của chúng tôi, chúng tôi chỉ muốn nó được gọi một lần. Các phương thức getvà readFilesẽ không bao giờ thay đổi nên lệnh gọi lại sẽ chỉ được chạy một lần.

Chúng tôi truyền vào getđối tượng, đại diện cho ảnh thiết bị mới được chụp, cũng như tên tệp, sẽ cung cấp đường dẫn cho tệp được lưu trữ.readFile các phương pháp.

Tiếp theo, chúng tôi sử dụng hệ thống tệp tụ điện để lưu ảnh vào hệ thống tệp. Chúng tôi bắt đầu bằng cách chuyển đổi ảnh sang định dạng base64, sau đó cung cấp dữ liệu vào useEffectchức năng của Hệ thống tập tin .

Cuối cùng, gọi <img src=”x” />và chuyển đối tượng cameraPhoto và tên tệp ngay bên dưới lệnh gọi đến IndexedDB  trong phương thức. Đây là phương pháp đầy đủ:

Bắt đầu! Mỗi khi một bức ảnh mới được chụp, ảnh sẽ tự động được lưu vào hệ thống tệp.

 Adding Mobile

Ứng dụng thư viện ảnh của chúng tôi sẽ không hoàn chỉnh cho đến khi nó chạy trên iOS, Android và web - tất cả đều sử dụng một cơ sở mã. Tất cả những gì cần làm là một số thay đổi logic nhỏ để hỗ trợ nền tảng di động, cài đặt một số công cụ gốc, sau đó chạy ứng dụng trên thiết bị. Đi nào!

Hãy bắt đầu với việc thực hiện một số thay đổi mã nhỏ - sau đó ứng dụng của chúng tôi sẽ "chỉ hoạt động" khi chúng tôi triển khai nó cho một thiết bị.

 Platform-specific Logic

Đầu tiên, chúng tôi sẽ cập nhật chức năng lưu ảnh để hỗ trợ thiết bị di động. Trong savePicturechức năng, hãy kiểm tra xem ứng dụng đang chạy trên nền tảng nào. Nếu đó là "kết hợp" (Tụ điện hoặc Cordova, hai thời gian chạy riêng), thì hãy đọc tệp ảnh ở định dạng base64 bằng readFilephương pháp này. Ngoài ra, hãy trả lại đường dẫn tệp hoàn chỉnh cho ảnh bằng API hệ thống tệp. Khi thiết lập webviewPath, hãy sử dụng Capacitor.convertFileSrc phương pháp đặc biệt  Nếu không, hãy sử dụng logic tương tự như trước đây khi chạy ứng dụng trên web.

  const savePicture = async (photoCameraPhotofileNamestring): Promise<Photo=> {

    let base64Datastring;
    // "hybrid" will detect Cordova or Capacitor;
    if (isPlatform('hybrid')) {
      const file = await readFile({
        pathphoto.path!
      });
      base64Data = file.data;
    } else {
      base64Data = await base64FromPath(photo.webPath!);
    }
    const savedFile = await writeFile({
      pathfileName,
      database64Data,
      directoryFilesystemDirectory.Data
    });
  
    if (isPlatform('hybrid')) {
      // Display the new image by rewriting the 'file://' path to HTTP
      // Details: https://ionicframework.com/docs/building/webview#file-protocol
      return {
        filepathsavedFile.uri,
        webviewPathCapacitor.convertFileSrc(savedFile.uri),
      };
    }
    else {
      // Use webPath to display the new image instead of base64 since it's
      // already loaded into memory
      return {
        filepathfileName,
        webviewPathphoto.webPath
      };
    }
  };

Tiếp theo, thêm một chút logic mới trong loadSavedhàm. Trên thiết bị di động, chúng tôi có thể trỏ trực tiếp đến từng tệp ảnh trên Hệ thống tệp và hiển thị chúng tự động. Tuy nhiên, trên web, chúng ta phải đọc từng hình ảnh từ Hệ thống tập tin ở định dạng base64. Điều này là do API hệ thống tệp sử dụng IndexedDB bên dưới. Cập nhật loadSaved chức năng bên trong của useEffect thành:

  const loadSaved = async () => {
    const photosString = await get('photos');
    const photosInStorage = (photosString ? JSON.parse(photosString: []) as Photo[];
    // If running on the web...
    if (!isPlatform('hybrid')) {
      for (let photo of photosInStorage) {
        const file = await readFile({
          pathphoto.filepath,
          directoryFilesystemDirectory.Data
        });
        // Web platform only: Load photo as base64 data
        photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
      }
    }
    setPhotos(photosInStorage);
  };

thư viện ảnh của chúng tôi hiện bao gồm một cơ sở mã chạy trên web, Android và iOS. Tiếp theo, phần bạn đã chờ đợi - triển khai ứng dụng cho một thiết bị.

Xem thêm tạ đây:  https://ionicframework.com/docs/react/your-first-app/5-adding-mobile

tìm hiểu về Vue : https://ionicframework.com/docs/vue/overview



Đăng nhận xét

0 Nhận xét

myadcash