@ninjasumer/react-native-image-annotator
v1.0.2
Published
A powerful React Native component for annotating images with drawing, text, zoom, and pan capabilities. Works on iOS, Android, and Web.
Maintainers
Readme
🖼️ React Native Image Annotator
Un componente potente y flexible para React Native que permite anotar imágenes con dibujos a mano alzada, texto y gestos de zoom/pan. Funciona en iOS, Android y Web.
✨ Características
- 🖌️ Dibujo a mano alzada - Dibuja con el dedo o ratón con colores y grosores personalizables
- 📝 Anotaciones de texto - Añade texto en cualquier posición, arrastra para mover
- 🔍 Zoom y Pan - Pellizca para hacer zoom, arrastra con dos dedos para mover
- ↩️ Historial completo - Deshacer/Rehacer todas las acciones
- 💾 Exportación - Captura la imagen anotada como URI o DataURL
- 🎨 Toolbar personalizable - Activa/desactiva funciones según necesites
- � Botones personalizados - Añade tus propios botones al toolbar
- 📱 Multiplataforma - iOS, Android y Web
- 🎯 Controlado y no controlado - Usa como componente controlado o no controlado
📦 Instalación
npm install @ninjasumer/react-native-image-annotatorDependencias requeridas (peer dependencies)
# Con npm
npm install react-native-gesture-handler react-native-reanimated react-native-svg react-native-view-shot
# Con Expo
npx expo install react-native-gesture-handler react-native-reanimated react-native-svg react-native-view-shotConfiguración adicional
React Native Reanimated
Añade el plugin de Babel en babel.config.js:
module.exports = {
plugins: ["react-native-reanimated/plugin"],
};React Native Gesture Handler
Envuelve tu app con GestureHandlerRootView:
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* Tu app */}
</GestureHandlerRootView>
);
}🚀 Uso básico
import { ImageAnnotator } from "@ninjasumer/react-native-image-annotator";
export default function App() {
return (
<ImageAnnotator
imageUri="https://example.com/image.jpg"
height={400}
showToolBar={true}
/>
);
}📖 Props
| Prop | Tipo | Default | Descripción |
| ---------------------- | --------------------------------- | ------------- | ------------------------------------------ |
| imageUri | string | requerido | URI de la imagen a anotar |
| height | number | 360 | Altura del componente |
| imageResizeMode | "cover" \| "contain" | "cover" | Modo de redimensionado de imagen |
| showToolBar | boolean | false | Muestra el toolbar integrado |
| showDefaultControls | boolean | false | Muestra controles básicos por defecto |
| toolbarConfig | ToolBarConfig | - | Configura qué elementos mostrar en toolbar |
| customToolbarButtons | CustomToolbarButton[] | - | Botones personalizados para el toolbar |
| renderControls | (api: ControlsAPI) => ReactNode | - | Renderiza controles personalizados |
| onExport | (uri: string) => void | - | Callback al exportar (botón guardar) |
| value | DrawingJSON | - | Estado del dibujo (controlado) |
| defaultValue | DrawingJSON | - | Estado inicial (no controlado) |
| onChange | (drawing: DrawingJSON) => void | - | Callback cuando cambia el dibujo |
| strokeColor | string | "#ff0000" | Color de trazo inicial |
| strokeWidth | number | 4 | Grosor de trazo inicial |
| fontSize | number | 20 | Tamaño de fuente inicial |
| minScale | number | 1 | Zoom mínimo |
| maxScale | number | 4 | Zoom máximo |
🎛️ Configuración del Toolbar
Puedes personalizar qué elementos mostrar en el toolbar:
import { ImageAnnotator, ToolBarConfig } from "@ninjasumer/react-native-image-annotator";
const config: ToolBarConfig = {
tools: true, // Herramientas (lápiz, texto, mover)
colors: true, // Selector de color
sizes: true, // Selector de grosor/tamaño
zoom: true, // Controles de zoom (+/-)
history: true, // Botones deshacer/rehacer
clear: true, // Botón limpiar
resetView: true, // Botón resetear vista
save: true, // Botón guardar
};
<ImageAnnotator imageUri="..." showToolBar={true} toolbarConfig={config} />;🔧 Botones personalizados
Añade tus propios botones al toolbar:
import {
ImageAnnotator,
CustomToolbarButton,
} from "@ninjasumer/react-native-image-annotator";
const customButtons: CustomToolbarButton[] = [
{
icon: "🖼️",
onPress: () => pickNewImage(),
tooltip: "Cambiar imagen",
},
{
icon: "📤",
onPress: () => shareImage(),
tooltip: "Compartir",
},
];
<ImageAnnotator
imageUri="..."
showToolBar={true}
customToolbarButtons={customButtons}
/>;� Uso con Ref
Accede a métodos del componente usando ref:
import { useRef } from "react";
import {
ImageAnnotator,
ImageAnnotatorRef,
} from "@ninjasumer/react-native-image-annotator";
export default function App() {
const annotatorRef = useRef<ImageAnnotatorRef>(null);
const handleCapture = async () => {
const uri = await annotatorRef.current?.captureImage("png");
console.log("Imagen capturada:", uri);
// Puedes guardarla, compartirla, subirla, etc.
};
const handleUndo = () => {
annotatorRef.current?.undo();
};
const handleExportJSON = () => {
const json = annotatorRef.current?.exportJSON();
console.log("Dibujo exportado:", json);
};
return (
<ImageAnnotator
ref={annotatorRef}
imageUri="https://example.com/image.jpg"
/>
);
}📤 API del Ref (ImageAnnotatorRef)
| Método | Descripción |
| ----------------------- | -------------------------------------- |
| undo() | Deshace la última acción |
| redo() | Rehace la última acción deshecha |
| clear() | Limpia todas las anotaciones |
| resetView() | Resetea zoom y posición |
| setZoom(zoom) | Establece el nivel de zoom |
| getZoom() | Obtiene el nivel de zoom actual |
| setStrokeColor(color) | Cambia el color del trazo |
| setStrokeWidth(width) | Cambia el grosor del trazo |
| setFontSize(size) | Cambia el tamaño de fuente |
| setTool(tool) | Cambia la herramienta |
| addText(text) | Añade texto en el centro |
| captureImage(format?) | Captura la imagen como URI |
| exportJSON() | Exporta el estado del dibujo como JSON |
| loadJSON(json) | Carga un estado de dibujo desde JSON |
🎨 Herramientas
| Herramienta | Icono | Descripción | | ----------- | ----- | --------------------------------------------- | | Lápiz | ✏️ | Dibujo a mano alzada | | Texto | T | Click para colocar texto, arrastra para mover | | Mover | ✋ | Mueve la vista con un dedo/click |
💾 Exportación de imagen
Usando el callback onExport:
<ImageAnnotator
imageUri="..."
showToolBar={true}
onExport={async (uri) => {
// En iOS/Android: uri es un path local
// En Web: uri es un data URL (base64)
// Ejemplo: Guardar en galería (necesita expo-media-library)
if (Platform.OS !== "web") {
await MediaLibrary.saveToLibraryAsync(uri);
} else {
// Descargar en web
const link = document.createElement("a");
link.href = uri;
link.download = "imagen-anotada.png";
link.click();
}
}}
/>Usando ref programáticamente:
const handleSave = async () => {
const uri = await annotatorRef.current?.captureImage("png");
// Hacer lo que quieras con el URI
};🎮 Controles personalizados
Crea tu propia UI de controles:
import { ImageAnnotator, ControlsAPI } from "@ninjasumer/react-native-image-annotator";
const renderMyControls = (api: ControlsAPI) => (
<View style={styles.myToolbar}>
<Button title="Deshacer" onPress={api.undo} />
<Button title="Rehacer" onPress={api.redo} />
<Button title="Limpiar" onPress={api.clear} />
<Button title="Rojo" onPress={() => api.setStrokeColor("#ff0000")} />
<Button title="Azul" onPress={() => api.setStrokeColor("#0000ff")} />
</View>
);
<ImageAnnotator imageUri="..." renderControls={renderMyControls} />;📱 Soporte de plataformas
| Plataforma | Dibujo | Texto | Zoom/Pan | Exportar | | ---------- | ------ | ----- | -------- | ------------- | | iOS | ✅ | ✅ | ✅ | ✅ (URI) | | Android | ✅ | ✅ | ✅ | ✅ (URI) | | Web | ✅ | ✅ | ✅ | ✅ (Data URL) |
📝 Tipos TypeScript
// Estado del dibujo
type DrawingJSON = {
version: 1;
strokes: Stroke[];
texts?: TextAnnotation[];
};
// Trazo
type Stroke = {
id: string;
tool: "pen";
color: string;
width: number;
points: Point[];
};
// Anotación de texto
type TextAnnotation = {
id: string;
tool: "text";
text: string;
color: string;
fontSize: number;
position: Point;
};
// Punto
type Point = { x: number; y: number };
// Herramienta
type Tool = "pen" | "text" | "pan";
// Configuración del toolbar
type ToolBarConfig = {
tools?: boolean;
colors?: boolean;
sizes?: boolean;
zoom?: boolean;
history?: boolean;
clear?: boolean;
resetView?: boolean;
save?: boolean;
};
// Botón personalizado
type CustomToolbarButton = {
icon: string;
onPress: () => void;
tooltip?: string;
};🔄 Componente controlado vs no controlado
No controlado (más simple):
<ImageAnnotator
imageUri="..."
defaultValue={{ version: 1, strokes: [], texts: [] }}
/>Controlado (control total):
const [drawing, setDrawing] = useState<DrawingJSON>({
version: 1,
strokes: [],
texts: [],
});
<ImageAnnotator imageUri="..." value={drawing} onChange={setDrawing} />;📄 Licencia
MIT
Desarrollado como proyecto del Máster en Ingenieria Web (UNIOVI) para la asignatura "Desarrollo de aplicaciones móviles".
