Présentation
Le but est de tester le plugin suivant : https://github.com/capacitor-community/camera-preview.
Pour commencer, l’idée est de suivre un tuto que j’ai trouvé mais qui semble dater un peu : https://www.youtube.com/watch?v=JA8k738i9jQ.
C’est de l’angular … Ca fait TRES longtemps donc je croise les doigts pour qu’il n’y ait pas trop de différence entre le tuto et la version d’Angular actuell
Tuto Ionic
Init
Alors comme d’hab, la première commande ne fonctionne pas :npx ionic start cameraPreview blank --type=angular --capacitor
J’obtiens une erreur en rapport avec un paramètre qui ne serait plus supporter. Bon, j’ai donc autre chose à faire donc j’essaye de faire autrement :
# Init du projet
npx ionic start cameraPreview blank
#
cd cameraPreview/
# Ajout de capacitor
npm i @capacitor/core
npm i -D @capacitor/cli
# Init
npm cap init
# Ajout du plugin
npm install @capacitor-community/camera-preview
# Ajout d'android
npm install @capacitor/android
npx cap add android
# Sync & build
npx cap sync
npx ionic build
Le tuto indique une mise à jour dans le MainActivity.java présent dans la doc à ce moment là mais qui ne semble plus présent actuellement. La seule note sur Android est l’ajout des permissions :
<uses-permission android:name="android.permission.CAMERA" />
Un changement au niveau des plugins dans Capacitor ? Il semble possible de récupérer le plugin directement
import { CameraPreview, CameraOpacityOptions, CameraPreviewPictureOptions } from '@capacitor-community/camera-preview';
Mise en place
Il faut mettre en place un bouton qui appelle fonction suivante :
openCamera() {
// Création de l'option
const cameraPreviewOptions : CameraPreviewOptions = {
position: 'rear', // On s'embête pas, on prend la camera de derrière. Uniquement pour le web
parent: 'cameraPreview', // Non documenté dans la liste mais présent dans la doc. Correspond au div
className: 'cameraPreview' // Uniquement Web aussi
}
// Démarrage de la camera
CameraPreview.start(cameraPreviewOptions);
// Passe le marqueur à vrai
this.cameraActive = true;
}
Il ne faut pas oublier d’ajouter un div dont l’id est cameraPreview
sinon une belle erreur (mais propre : parent is null).
Et là : incroyable mais ca marche :). En mode web, j’ai bien ma tête qui apparait dans l’écran.
Mode Android
Après plusieurs recherche, (pas tant que cela), j’ai ajouté cela dans le fichier global CSS :
body {
background-color: transparent !important;
}
Sans Ionic
Forcément, svelte
Après Ionic, j’ai voulu essayé autre chose et même de me passer du plugin en me disant qu’il est peut-être possible de passer par le stream ?
Après l’init premier problème
Mes recherches ont confirmés que cela semblait possible. Pour cela, il faut faire un truc du style : mapper le stream récupérer depuis le navigateur vers l’objet video puis prendre une image.
Premier souci : il ne semble pas possible de binder directement sur la propriété srcObject
qui est celle sur laquelle il faudrait mapper le flux (enfin je crois :).
J’ai fait simple :
// -- Démarrage de la caméra
const startCamera = async () => {
if (videoPlayer) {
try {
// Device selected ?
let c : MediaStreamConstraints = selectedDeviceId ? { video: { deviceId : selectedDeviceId }} : { video: true }
stream = await navigator.mediaDevices.getUserMedia(c);
videoPlayer.srcObject = stream;
// Affichage
showVideo = true;
} catch (error: any) {
manageError(error.message);
}
}
};
Après des recherches, j’ai vu que d’autres utilisaient des actions. J’ai pas ce réflexe.
Ensuite l’astuce (fourni par MDN :)) est de remplir un canvas avec le flux vidéo puis de récupérer la photo :
// -- Prise d'une photo
const handleTakePicture = async () => {
// Test
if (!videoPlayer || !canvas) {
manageError("Ni vidéo ni zone pour la capture ...");
return;
}
// Mise à jour du canvas (qui n'est pas visible)
// -- Taille
canvas.width = videoPlayer.videoWidth;
canvas.height = videoPlayer.videoHeight;
// -- Context
const ctx = canvas.getContext("2d");
if (!ctx) {
manageError("Impossible de récupérer le canvas");
return;
}
// -- Mise à jour
ctx.drawImage(videoPlayer, 0, 0, canvas.width, canvas.height);
// Enregistrement de la photo
// -- Récupération du flux
let imageData = canvas.toDataURL();
console.log(imageData);
// -- Ajout dans la liste
imagesList.push({
dataUrl: imageData,
date: new Date(),
});
};
Et les photos apparaissent bien dans la liste.
Un point cependant : la taille de photo est basée sur la taille de la vidéo. Ce qui est quand même limitée. J’ai essayé de jouer avec les résolutions en jouant sur les contraintes :
let c: MediaStreamConstraints = {
audio: false,
video: {
width: selectedResolution.width,
height: selectedResolution.height,
deviceId : selectedDeviceId ? selectedDeviceId : undefined
}
}
mais sans vraiment réussir à obtenir ce que je voulais …
ImageCapture
En passant d’une recherche à l’autre, une page parlait ImageCapture
Cela fonctionne que sur Chrome (au moment de l’écriture de cette page :) :
// -- Récupération de la track
const track = stream!.getTracks()[0];
// -- Création de l'objet Image
const imageCapture = new ImageCapture(track);
const photosCaps = await imageCapture.getPhotoCapabilities();
console.log(photosCaps);
// -- Prise de la photo
const blob : Blob = await imageCapture.takePhoto();
console.log(blob);
// --> Ajout dans la liste
imagesList.push({
dataUrl: URL.createObjectURL(blob),
date: new Date(),
label: "from ImageCapture"
});
J’ai vu que les photos étaient limitées à 640 / 480 … change pas grand chose.
Installation de Capacitor
npm i @capacitor/core
npm i -D @capacitor/cli
npm i @capacitor/android
npx cap add android
npx cap sync
npx cap open android
Et la pour une fois … ca marche du premier coup :)
Maintenant pour faire ce que je veux ce ne serait que du CSS et franchement le CSS …
Svelte - CameraPreview
Incroyable mais …
Allez, on essaye quand même avec CameraPreview. Et encore une fois : CA MARCHE !!!.
Les étapes :
- instal : RAS,
- Mise en place : RAS,
- Reprise du code du proto : RAS.
Bref, une fois n’est pas coutume … Pas de soucis particulier.
Quelques adaptations
Juste pour le plaisir, j’ai fait quelques adaptations qui au final sont nécessaires pour certaines.
La première : couper la caméra quand on sort de l’écran. C’est bête mais sinon l’application crash.
Autre, essayer de faire des boutons jolies. Exemple le bouton qui permet de prendre les photos :
.photo-button-container {
position: relative;
width: 80px; /* Assurez-vous que la largeur et la hauteur sont les mêmes */
height: 80px; /* Assurez-vous que la largeur et la hauteur sont les mêmes */
margin: 0 auto; /* Centre le bouton horizontalement */
}
.photo-button {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #007bff;
border: none;
color: white;
font-size: 14px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
z-index: 1;
padding: 0; /* Supprime le padding par défaut de Bootstrap */
}
.photo-button::before {
content: '';
position: absolute;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
border: 2px solid #007bff;
border-radius: 50%;
z-index: -1;
}