Skip to main content

Camara kit js v1

Documentación de Camera Kit JS v1

Camera Kit es el SDK multiplataforma de Snap que permite incorporar experiencias de realidad aumentada en sitios web y aplicaciones móviles (iOS y Android). Con él podrás integrar filtros, efectos y funcionalidades de cámara directamente en tus proyectos.

Páginas oficiales de referencia:


HTML Base

El siguiente HTML establece la estructura básica de la aplicación web. Se incluyen elementos para visualizar la cámara, mostrar la foto capturada, grabar video y cambiar entre dispositivos de captura.

<!doctype html>
<html lang="es">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Camera Kit Web App</title>
<!-- Enlace a la hoja de estilos -->
<link rel="stylesheet" href="style.css">
</head>

<body>
<!-- Canvas principal para renderizar la cámara en vivo -->
<canvas id="canvas"></canvas>

<!-- Contenedor para la foto capturada -->
<div id="container-canvas" class="container-canvas">
<div class="container-foto">
<canvas id="foto-canvas"></canvas>
<div class="container-button-foto">
<button id="guardar-foto">Compartir Foto</button>
</div>
</div>
</div>

<!-- Botón para capturar foto -->
<button id="capture-btn">Tomar foto</button>

<!-- Botones para grabar video -->
<button id="start-recording">Iniciar grabación</button>
<button id="stop-recording" style="display: none;">Detener grabación</button>

<!-- Contenedor para el video grabado -->
<div id="containerVideo" class="container-canvas">
<div class="container-foto">
<video id="videoScreen" src="" controls></video>
<div class="container-button-foto">
<button id="guardar-video">Compartir Video</button>
</div>
</div>
</div>

<!-- Sección para cambiar de cámara y regresar a otra URL -->
<div style="display: flex; flex-direction: column; gap: 8px;">
<div style="background-color: green; text-align: center; padding: 8px; cursor: pointer;"
onclick="window.location.href='https://redirect-filtro-good-demo.netlify.app/'">
Regresar
</div>
<!-- Select para listar y elegir entre las cámaras disponibles -->
<select id="videoSource"></select>
</div>

<!-- Versión mostrada en pantalla -->
<div style="position: fixed; z-index: 50; bottom: 0; background-color: black; color: white; padding: 4px;">
1.19.0
</div>

<!-- Carga del módulo principal -->
<script type="module" src="/main.js"></script>
</body>

</html>

Uso del SDK Web de Camera Kit

Esta guía te muestra los conceptos básicos para inicializar y configurar el SDK.

Inicialización del SDK y Aplicación de un Filtro

Para iniciar, necesitas obtener un apiToken desde el developer portal. Luego, utilizando el método bootstrapCameraKit se inicializa el SDK. Posteriormente, se carga un filtro (lens) específico usando los IDs correspondientes (folder y filtro).

// Inicializa el SDK con tu apiToken
const cameraKit = await bootstrapCameraKit({
apiToken: 'api-token'
});

// Carga el filtro (lens) a partir del folder y el id del filtro
const lens = await cameraKit.lensRepository.loadLens(
'49820200880', // ID del folder de filtros
'48224b69-c485-4b0e-848e-6159b2f95adf' // ID del filtro
);

Nota: Por defecto, el filtro muestra la cámara frontal o trasera. Con las funciones gotDevices y toogleCamare se habilita la posibilidad de cambiar entre las distintas cámaras disponibles, teniendo en cuenta la compatibilidad con los navegadores según MDN Web Docs.

Una vez configurada la cámara, se puede acceder a los botones que permiten tomar fotos y grabar videos.


Archivo: camaraUtils.js

Este archivo contiene funciones auxiliares para obtener los dispositivos de entrada (cámaras) y cambiar entre ellos.

// Obtiene la referencia del elemento select que mostrará las cámaras disponibles
const videoSource = document.getElementById("videoSource");

// Función que devuelve una promesa con la lista de dispositivos de medios disponibles
const getDevices = () => {
return navigator.mediaDevices.enumerateDevices();
}

/**
* Función para listar todos los dispositivos disponibles y
* añadir las cámaras al elemento select para que el usuario pueda elegir.
*/
export const gotDevices = async () => {
const deviceInfos = await getDevices();
// Se guarda la lista de dispositivos globalmente para poder usarlos en otros contextos
window.deviceInfos = deviceInfos;
console.log('Dispositivos disponibles:', deviceInfos);
// Recorre cada dispositivo y agrega las cámaras al select
for (const deviceInfo of deviceInfos) {
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'videoinput') {
option.text = deviceInfo.label || `Cámara ${videoSource.length + 1}`;
videoSource.appendChild(option);
}
}
}

/**
* Función para cambiar la cámara actual.
* Se obtiene el deviceId seleccionado en el select y se establece la nueva fuente de video.
* @param {Object} session - La sesión actual de Camera Kit.
*/
export const toogleCamare = async (session) => {
const deviceId = videoSource.value;

// Define las restricciones para capturar el dispositivo de video seleccionado
const constraints = {
video: { deviceId: { exact: deviceId } }
};

try {
// Solicita acceso al dispositivo de video
const stream = await navigator.mediaDevices.getUserMedia(constraints);
console.log('Stream de video:', stream);
// Actualiza la fuente del stream en la sesión actual
await session.setSource(stream);
console.log('Cámara cambiada a:', stream.getVideoTracks()[0].label);
} catch (error) {
console.error('Error al cambiar de cámara:', error);
}
}

Archivo: main.js

Este archivo es el punto de entrada principal. Inicializa el SDK, configura la sesión, asigna la fuente de video y aplica el filtro. Además, inicializa los botones de acción.

import { bootstrapCameraKit, createMediaStreamSource, Transform2D } from '@snap/camera-kit';
import { initActions } from './actionsButtons';
import { gotDevices, toogleCamare } from './camareUtils';

// Referencia al elemento select para cambiar la cámara
const videoSource = document.getElementById("videoSource");

(async function () {
// Inicializa el SDK con el apiToken
const cameraKit = await bootstrapCameraKit({
apiToken: 'api-token'
});

// Define el canvas donde se renderizará la imagen en vivo
const liveRenderTarget = document.getElementById('canvas');
// Crea una sesión de Camera Kit utilizando el canvas de renderizado
const session = await cameraKit.createSession({ liveRenderTarget });

// Solicita acceso a la cámara (configuración básica)
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
});

// Crea una fuente de media a partir del stream obtenido y aplica una transformación espejo en X
const source = createMediaStreamSource(mediaStream, {
transform: Transform2D.MirrorX,
cameraType: 'front',
});

// Configura la fuente y comienza la reproducción en la sesión
await session.setSource(source);
await session.play();

// Lista los dispositivos de video disponibles y actualiza el select
gotDevices();

// Cuando el usuario cambie el dispositivo seleccionado, se invoca la función para cambiar la cámara
videoSource.onchange = () => toogleCamare(session);

// Carga el filtro (lens) a aplicar en la sesión
const lens = await cameraKit.lensRepository.loadLens(
// Puedes cambiar los IDs según el filtro deseado
'79a2c930-6c69-4725-8adf-6d2fda9334b2',
'220e087f-3422-4bdb-a5b8-416c1d9cda4c'
);

// Aplica el filtro cargado a la sesión
await session.applyLens(lens);

// Inicializa los botones para capturar fotos y videos
initActions();
})();

Archivo: actionsButtons.js

En este archivo se definen los eventos para capturar imágenes y videos, así como para compartirlos. Cada acción actualiza la interfaz de usuario según la operación realizada.

import { updateStyleDisplay } from "./ContainerButtons";
import { getCanvas, saveImages } from "./photoCamare";
import { recordVideo, stopRecord, saveVideoShare } from "./videoCamare";

// Referencias a los botones de acción
const captureBtn = document.getElementById("capture-btn");
const startRecordingBtn = document.getElementById("start-recording");
const stopRecordingBtn = document.getElementById("stop-recording");

// Referencias para la captura de foto
const captureSave = document.getElementById("guardar-foto");
const container = document.getElementById('container-canvas');
const fotoCanvas = document.getElementById("foto-canvas");

// Referencias para la grabación de video
const containerVideo = document.getElementById('containerVideo');
const videoElement = document.getElementById("videoScreen");
const saveVideo = document.getElementById("guardar-video");

/**
* Inicializa los eventos de los botones para capturar fotos y videos.
*/
export const initActions = () => {
// Evento para capturar una foto
captureBtn.addEventListener("click", () => {
// Oculta botones de captura y grabación, y muestra el contenedor de la foto
updateStyleDisplay(captureBtn, false);
updateStyleDisplay(startRecordingBtn, false);
updateStyleDisplay(container, true);
// Captura la imagen del canvas en vivo y la dibuja en el canvas de foto
getCanvas(fotoCanvas);
});

// Evento para guardar y compartir la foto capturada
captureSave.addEventListener("click", () => {
// Oculta el contenedor de la foto y guarda la imagen
updateStyleDisplay(container, false);
saveImages(fotoCanvas);
// Restaura la visibilidad de los botones de captura y grabación
updateStyleDisplay(captureBtn, true);
updateStyleDisplay(startRecordingBtn, true);
});

// Evento para iniciar la grabación de video
startRecordingBtn.addEventListener("click", () => {
recordVideo({
videoElement,
blockStart: () => {
// Durante el inicio de la grabación se ocultan botones no necesarios
updateStyleDisplay(captureBtn, false);
updateStyleDisplay(startRecordingBtn, false);
updateStyleDisplay(stopRecordingBtn, true);
},
});
});

// Evento para detener la grabación de video
stopRecordingBtn.addEventListener("click", () => {
stopRecord(() => {
// Al detener la grabación se muestra el contenedor del video
updateStyleDisplay(containerVideo, true);
updateStyleDisplay(stopRecordingBtn, false);
});
});

// Evento para guardar y compartir el video grabado
saveVideo.addEventListener("click", () => {
updateStyleDisplay(containerVideo, false);
saveVideoShare();
updateStyleDisplay(captureBtn, true);
updateStyleDisplay(startRecordingBtn, true);
});
}

Archivo: ContainerButtons.js

Este helper contiene funciones para actualizar estilos en la interfaz y detectar características del dispositivo (por ejemplo, si es móvil, iOS, Android o desktop).

/**
* Actualiza el estilo de visualización de un elemento HTML.
* @param {HTMLElement} html - Elemento al que se le modificará el display.
* @param {boolean} isVisible - Si es true, se muestra el elemento; de lo contrario, se oculta.
*/
export const updateStyleDisplay = (html, isVisible = false) => {
html.style.display = isVisible ? "block" : "none";
}

/**
* Verifica si el dispositivo actual es móvil.
* @returns {boolean} True si se detecta un dispositivo móvil.
*/
export const isMobileDevice = () => {
return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

/**
* Detecta el tipo de dispositivo en base al userAgent.
* @returns {string} 'ios', 'android' o 'desktop'.
*/
export const detectDevice = () => {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;

if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
return 'ios';
} else if (/android/i.test(userAgent)) {
return 'android';
} else {
return 'desktop';
}
}

/**
* Determina si el navegador es Safari.
* @returns {boolean} True si el navegador es Safari.
*/
export const isSafari = () => {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}

Archivo: videoCamare.js

Este módulo se encarga de la grabación y guardado de video. Se configura la grabación a partir de un canvas oculto que captura el contenido del canvas principal.

import { detectDevice, isMobileDevice } from "./ContainerButtons";

let mediaRecorder = null;
let mimeType = 'video/mp4';
let drawInterval = null;
let blob = null;

/**
* Guarda el video grabado en dispositivos iOS/Android utilizando la API de compartir.
* Si la API no está disponible, se utiliza el método para escritorio.
*/
const saveImagesIphone = async () => {
const file = new File([blob], "video." + mimeType.split('/')[1], { type: mimeType });

if (navigator.canShare && navigator.canShare({ files: [file] })) {
try {
await navigator.share({
title: 'Compartir video',
text: 'Aquí tienes el video.',
files: [file],
});
console.log("Video compartido exitosamente.");
} catch (error) {
console.error("Error al compartir el video:", error);
}
} else {
saveDesktop();
}
}

/**
* Función para guardar el video en dispositivos de escritorio.
*/
const saveDesktop = () => {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'video.' + mimeType.split('/')[1];
link.click();
}

/**
* Función pública para guardar y compartir el video, detectando el tipo de dispositivo.
*/
export const saveVideoShare = () => {
if (!blob) return;

const device = detectDevice();

switch (device) {
case "ios":
case "android":
saveImagesIphone();
break;
default:
saveDesktop();
break;
}
}

/**
* Verifica la compatibilidad del mimeType para la grabación y lo ajusta si es necesario.
*/
const getMineType = () => {
if (!MediaRecorder.isTypeSupported(mimeType)) {
mimeType = 'video/webm;codecs=vp8';
if (!MediaRecorder.isTypeSupported(mimeType)) {
mimeType = 'video/mp4';
if (!MediaRecorder.isTypeSupported(mimeType)) {
alert("No se soporta ningún tipo de video");
return;
}
}
}
}

// Se crea un canvas oculto que se usará para capturar el contenido del canvas principal
const hiddenCanvas = document.createElement('canvas');
// Se establecen las dimensiones del canvas según el tipo de dispositivo
hiddenCanvas.width = isMobileDevice() ? 1080 : 1920;
hiddenCanvas.height = isMobileDevice() ? 1920 : 1080;

/**
* Inicia la grabación del video.
* @param {Object} param0 - Objeto de configuración con el elemento video y una función callback para bloquear botones.
*/
export const recordVideo = ({
videoElement,
blockStart,
}) => {
// Captura el stream del canvas oculto
const stream = hiddenCanvas.captureStream();

if (!stream) {
alert("No se puede grabar");
return;
}

// Ajusta el mimeType a uno soportado
getMineType();

// Crea un nuevo MediaRecorder utilizando el stream capturado
mediaRecorder = new MediaRecorder(stream, { mimeType: mimeType });

// Evento que se ejecuta cuando hay datos disponibles del MediaRecorder
mediaRecorder.ondataavailable = async (event) => {
if (event.data.size > 0) {
blob = new Blob([event.data], { type: mimeType });
// Establece la fuente del elemento video para reproducir el video grabado
videoElement.src = URL.createObjectURL(event.data);
videoElement.onloadedmetadata = async () => {
videoElement.play();
};
}
};

// Ejecuta la función para bloquear botones durante el inicio de grabación
blockStart();

// Dibuja en el canvas oculto a 30 FPS para capturar la imagen en vivo
drawInterval = setInterval(drawOnHiddenCanvas, 1000 / 30);
// Inicia la grabación
mediaRecorder.start();
}

/**
* Detiene la grabación del video y ejecuta una función callback para actualizar la interfaz.
* @param {Function} actionBlock - Callback que se ejecuta tras detener la grabación.
*/
export const stopRecord = (actionBlock) => {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
clearInterval(drawInterval);
}
actionBlock();
}

/**
* Función interna que dibuja el contenido del canvas principal en el canvas oculto.
*/
const drawOnHiddenCanvas = () => {
const ctx = hiddenCanvas.getContext('2d');
const liveRenderTarget = document.getElementById('canvas');
ctx.drawImage(liveRenderTarget, 0, 0, hiddenCanvas.width, hiddenCanvas.height);
}

Archivo: photoCamare.js

Este módulo se encarga de capturar imágenes desde el canvas en vivo y guardarlas o compartirlas, según el tipo de dispositivo.

import { detectDevice, isMobileDevice } from "./ContainerButtons";

/**
* Captura la imagen actual del canvas en vivo y la dibuja en el canvas de foto.
* @param {HTMLCanvasElement} fotoCanvas - El canvas donde se mostrará la foto.
*/
export const getCanvas = (fotoCanvas) => {
const liveRenderTarget = document.getElementById('canvas');
const context = fotoCanvas.getContext("2d");

// Define las dimensiones deseadas según el tipo de dispositivo
let dimension = {
desiredWidth: 1920,
desiredHeight: 1080
};

if (isMobileDevice()) {
dimension.desiredWidth = 1080;
dimension.desiredHeight = 1920;
}

// Ajusta el tamaño del canvas de la foto y dibuja la imagen capturada
fotoCanvas.width = dimension.desiredWidth;
fotoCanvas.height = dimension.desiredHeight;
context.drawImage(liveRenderTarget, 0, 0, fotoCanvas.width, fotoCanvas.height);
}

/**
* Guarda y comparte la imagen capturada. Utiliza la API de compartir en dispositivos móviles
* o descarga el archivo en escritorio.
* @param {HTMLCanvasElement} fotoCanvas - Canvas que contiene la imagen a guardar.
*/
const saveImagesIphone = async (fotoCanvas) => {
// Convierte el canvas a una imagen en formato PNG
const dataURL = fotoCanvas.toDataURL("image/png", 1.0);

// Convierte la dataURL a blob y crea un archivo
const response = await fetch(dataURL);
const blob = await response.blob();
const file = new File([blob], "captura.png", { type: "image/png" });

if (navigator.canShare && navigator.canShare({ files: [file] })) {
try {
await navigator.share({
title: 'Compartir imagen',
text: 'Aquí tienes una imagen capturada.',
files: [file],
});
console.log("Imagen compartida exitosamente.");
} catch (error) {
console.error("Error al compartir la imagen:", error);
}
} else {
// Si la API de compartir no está disponible, descarga la imagen
const blobURL = URL.createObjectURL(file);
const link = document.createElement('a');
link.href = blobURL;
link.download = file.name;
link.click();
URL.revokeObjectURL(blobURL);
}
}

/**
* Función para descargar la imagen en dispositivos de escritorio.
* @param {HTMLCanvasElement} fotoCanvas - Canvas que contiene la imagen a guardar.
*/
const saveDesktop = (fotoCanvas) => {
const dataURL = fotoCanvas.toDataURL("image/png", 1.0);
const link = document.createElement('a');
link.href = dataURL;
link.download = 'captura.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}

/**
* Función pública para guardar la imagen, detectando el dispositivo.
* @param {HTMLCanvasElement} fotoCanvas - Canvas que contiene la imagen a guardar.
*/
export const saveImages = (fotoCanvas) => {
const device = detectDevice();
switch (device) {
case "ios":
case "android":
saveImagesIphone(fotoCanvas);
break;
default:
saveDesktop(fotoCanvas);
break;
}
}