UNPKG

react-native-signature-canvas

Version:

A performant, customizable React Native signature canvas with advanced error handling, WebView optimization, and TypeScript support for iOS, Android, and Expo

555 lines (455 loc) 16.9 kB
# React Native Signature Canvas [![](https://img.shields.io/npm/l/react-native-signature-canvas.svg)](https://www.npmjs.com/package/react-native-signature-canvas) [![](https://img.shields.io/npm/v/react-native-signature-canvas)](https://www.npmjs.com/package/react-native-signature-canvas) ![npm](https://img.shields.io/npm/dt/react-native-signature-canvas) ![GitHub last commit](https://img.shields.io/github/last-commit/yanyuanfe/react-native-signature-canvas) [![runs with expo](https://img.shields.io/badge/Runs%20with%20Expo-4630EB.svg?style=flat&logo=EXPO&labelColor=f3f3f3&logoColor=000)](https://github.com/expo/expo) A React Native component for capturing signatures or drawing on a canvas with a smooth, native feel. Works on iOS, Android, and Expo. ## Features -**Cross-platform support** (iOS, Android, Expo) -**Smooth, responsive drawing experience** with optimized performance -**Customizable pen color, size, and background** -**Support for background and overlay images** -**Export signatures** as PNG, JPEG, or SVG -**Undo/redo functionality** -**Drawing and erasing modes** -**Full TypeScript support** with enhanced type definitions - 🆕 **Advanced error handling** with automatic recovery - 🆕 **Performance monitoring** and optimization - 🆕 **Flexible WebView customization** via `webviewProps` - 🆕 **Enhanced security** with configurable restrictions - 🆕 **Memory management** and leak prevention ## Installation ### For React Native ≥ 0.60.0 or Expo SDK ≥ 35.0.0 ```bash yarn add react-native-signature-canvas ``` or ```bash npm install --save react-native-signature-canvas ``` > This package depends on [react-native-webview](https://github.com/react-native-webview/react-native-webview). If you're using React Native CLI (not Expo), you'll need to install react-native-webview separately: > > ```bash > yarn add react-native-webview > cd ios && pod install > ``` ### For React Native < 0.60.0 or Expo SDK < 33.0.0 ```bash npm install --save react-native-signature-canvas@1.4.2 ``` ## Basic Usage ```jsx import React, { useRef, useState } from 'react'; import { StyleSheet, View, Image } from 'react-native'; import SignatureCanvas from 'react-native-signature-canvas'; const SignatureScreen = () => { const [signature, setSignature] = useState(null); const [isLoading, setIsLoading] = useState(false); const ref = useRef(); const handleSignature = (signature) => { console.log('Signature captured:', signature); setSignature(signature); setIsLoading(false); }; const handleEmpty = () => { console.log('Signature is empty'); setIsLoading(false); }; const handleClear = () => { console.log('Signature cleared'); setSignature(null); }; const handleError = (error) => { console.error('Signature pad error:', error); setIsLoading(false); }; const handleEnd = () => { setIsLoading(true); ref.current?.readSignature(); }; return ( <View style={styles.container}> <View style={styles.preview}> {signature && ( <Image resizeMode="contain" style={{ width: 335, height: 114 }} source={{ uri: signature }} /> )} </View> <SignatureCanvas ref={ref} onEnd={handleEnd} onOK={handleSignature} onEmpty={handleEmpty} onClear={handleClear} onError={handleError} autoClear={true} descriptionText="Sign here" clearText="Clear" confirmText={isLoading ? "Processing..." : "Save"} penColor="#000000" backgroundColor="rgba(255,255,255,0)" webviewProps={{ // Custom WebView optimization cacheEnabled: true, androidLayerType: "hardware", }} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, }, preview: { width: 335, height: 114, backgroundColor: '#F8F8F8', justifyContent: 'center', alignItems: 'center', marginTop: 15, }, }); export default SignatureScreen; ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `androidHardwareAccelerationDisabled` | `boolean` | `false` | Disable hardware acceleration on Android | | `autoClear` | `boolean` | `false` | Auto clear signature after clicking the Confirm button | | `backgroundColor` | `string` | `rgba(255,255,255,0)` | Background color of the canvas | | `bgHeight` | `number` | `0` | Height of the background image | | `bgWidth` | `number` | `0` | Width of the background image | | `bgSrc` | `string` | `null` | Background image source URI | | `clearText` | `string` | `Clear` | Clear button text | | `confirmText` | `string` | `Confirm` | Save button text | | `customHtml` | `(injectedJavaScript: string) => string` | `null` | Custom HTML template for the canvas | | `dataURL` | `string` | `""` | Base64 string to draw saved signature | | `descriptionText` | `string` | `Sign above` | Description text for signature | | `dotSize` | `number` | `null` | Radius of a single dot | | `imageType` | `string` | `image/png` | Image type for export (`image/png`, `image/jpeg`, `image/svg+xml`) | | `minWidth` | `number` | `0.5` | Minimum width of a line | | `maxWidth` | `number` | `2.5` | Maximum width of a line | | `nestedScrollEnabled` | `boolean` | `false` | Enable nested scrolling for use inside a ScrollView | | `showsVerticalScrollIndicator` | `boolean` | `true` | Show vertical scroll indicator in WebView | | `onOK` | `function` | - | Callback after saving non-empty signature | | `onEmpty` | `function` | - | Callback after trying to save an empty signature | | `onClear` | `function` | - | Callback after clearing the signature | | `onGetData` | `function` | - | Callback when getData() is called | | `onBegin` | `function` | - | Callback when a new stroke is started | | `onEnd` | `function` | - | Callback when the stroke has ended | | `onLoadEnd` | `function` | - | Callback when the WebView canvas load ended | | `onUndo` | `function` | - | Callback when undo() is called | | `onRedo` | `function` | - | Callback when redo() is called | | `onDraw` | `function` | - | Callback when drawing is enabled | | `onErase` | `function` | - | Callback when erasing is enabled | | `onChangePenColor` | `function` | - | Callback after changing the pen color | | `onChangePenSize` | `function` | - | Callback after changing the pen size | | `overlayHeight` | `number` | `0` | Height of the overlay image | | `overlayWidth` | `number` | `0` | Width of the overlay image | | `overlaySrc` | `string` | `null` | Overlay image source URI (must be PNG with transparent background) | | `penColor` | `string` | `black` | Color of the pen | | `rotated` | `boolean` | `false` | Rotate signature pad 90 degrees | | `style` | `object` | - | Style of the wrapper view | | `scrollable` | `boolean` | `false` | Enable scrolling in the signature pad | | `trimWhitespace` | `boolean` | `false` | Trim image whitespace | | `webStyle` | `string` | - | WebView style to override default style | | `webviewContainerStyle` | `object` | - | Style for the WebView container | | `androidLayerType` | `none\|software\|hardware` | `hardware` | Sets the Android WebView layer type | | `onError` | `function` | - | Callback when an error occurs | | `webviewProps` | `object` | `{}` | Additional props to pass to the underlying WebView | ## Methods Access these methods using a ref to the SignatureCanvas component. | Method | Description | |--------|-------------| | `clearSignature()` | Clear the current signature | | `changePenColor(color)` | Change pen color | | `changePenSize(minW, maxW)` | Change pen size | | `draw()` | Enable drawing mode | | `erase()` | Enable erasing mode | | `getData()` | Triggers the `onGetData` callback with signature data | | `readSignature()` | Read the current signature and trigger callbacks | | `undo()` | Undo last stroke | | `redo()` | Redo last stroke | ## WebView Customization (New!) The `webviewProps` parameter allows you to customize the underlying WebView behavior while maintaining signature functionality: ```jsx <SignatureCanvas // ... other props webviewProps={{ // Performance optimization cacheEnabled: true, androidLayerType: "hardware", androidHardwareAccelerationDisabled: false, // Security settings allowFileAccess: false, allowFileAccessFromFileURLs: false, mixedContentMode: "never", // UI customization decelerationRate: 'fast', bounces: false, // Any other WebView props... }} /> ``` ### Performance Optimization Examples ```jsx // High-performance mode <SignatureCanvas webviewProps={{ cacheEnabled: true, androidLayerType: "hardware", androidHardwareAccelerationDisabled: false, }} /> // Low-memory mode <SignatureCanvas webviewProps={{ cacheEnabled: false, androidLayerType: "software", androidHardwareAccelerationDisabled: true, }} /> ``` ## Error Handling (Enhanced!) ```jsx const [error, setError] = useState(null); const handleError = (error) => { console.error('Signature error:', error); setError(error.message); // Error recovery is automatic, but you can handle it here }; <SignatureCanvas onError={handleError} // Component automatically retries on recoverable errors /> {error && ( <Text style={{ color: 'red' }}>Error: {error}</Text> )} ``` ## Advanced Usage ### Using a Background Image ```jsx const imgWidth = 300; const imgHeight = 200; const style = `.m-signature-pad {box-shadow: none; border: none; } .m-signature-pad--body {border: none;} .m-signature-pad--footer {display: none; margin: 0px;} body,html { width: ${imgWidth}px; height: ${imgHeight}px;}`; <View style={{ width: imgWidth, height: imgHeight }}> <SignatureCanvas ref={ref} bgSrc="https://example.com/background.jpg" bgWidth={imgWidth} bgHeight={imgHeight} webStyle={style} onOK={handleSignature} /> </View> ``` ### Using an Overlay Image ```jsx const imgWidth = 256; const imgHeight = 256; const style = `.m-signature-pad {box-shadow: none; border: none; } .m-signature-pad--body {border: none;} .m-signature-pad--footer {display: none; margin: 0px;} body,html { width: ${imgWidth}px; height: ${imgHeight}px;}`; <View style={{ width: imgWidth, height: imgHeight }}> <SignatureCanvas ref={ref} overlaySrc="https://example.com/overlay.png" // Must be PNG with transparent background overlayWidth={imgWidth} overlayHeight={imgHeight} webStyle={style} onOK={handleSignature} /> </View> ``` ### Using in a Modal ```jsx import React, { useState, useRef } from 'react'; import { StyleSheet, View, TouchableOpacity, Modal, Text } from 'react-native'; import SignatureCanvas from 'react-native-signature-canvas'; const SignatureModal = ({ onSignature }) => { const [show, setShow] = useState(false); const ref = useRef(); const handleSignature = (signature) => { onSignature(signature); setShow(false); }; return ( <View> <TouchableOpacity onPress={() => setShow(true)}> <Text>Open Signature Pad</Text> </TouchableOpacity> {show && ( <Modal> <SignatureCanvas ref={ref} onOK={handleSignature} onEmpty={() => console.log('Empty')} descriptionText="Sign here" penColor="rgba(255,117,2,1)" /> </Modal> )} </View> ); }; ``` ### Scrollable Signature Canvas ```jsx import React, { useRef, useState } from 'react'; import { View, StyleSheet, ScrollView } from 'react-native'; import SignatureCanvas from 'react-native-signature-canvas'; const ScrollableSignature = () => { const [scrollEnabled, setScrollEnabled] = useState(true); const signatureRef = useRef(null); return ( <ScrollView scrollEnabled={scrollEnabled}> <View style={styles.container}> <SignatureCanvas ref={signatureRef} style={styles.canvas} onBegin={() => setScrollEnabled(false)} onEnd={() => setScrollEnabled(true)} /> </View> </ScrollView> ); }; const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', }, canvas: { width: '90%', height: 300, borderWidth: 1, borderColor: '#000', }, }); ``` ## Performance & Reliability ### Performance Features - **Debounced resize handling** for smooth interaction - **Memory pressure detection** with adaptive optimization - **Optimized rendering** with reduced re-renders - **Device-specific optimization** based on hardware capabilities ### Security Enhancements - **Configurable WebView security** via `webviewProps` - **Input validation** for all methods and callbacks - **XSS protection** with content security policies - **File access restrictions** by default ## Migration Guide ### From v4.x to v5.x This version is fully backward compatible. New features: ```jsx // NEW: Enhanced error handling <SignatureCanvas onError={(error) => console.error(error)} // New callback /> // NEW: WebView customization <SignatureCanvas webviewProps={{ // New prop cacheEnabled: false, androidLayerType: "software" }} /> ``` ## Troubleshooting ### Common Issues **Issue**: Signature pad not loading ```jsx // Solution: Add error handling and check WebView props <SignatureCanvas onError={(error) => console.log('Error:', error)} onLoadEnd={() => console.log('Loaded successfully')} webviewProps={{ startInLoadingState: true, renderLoading: () => <ActivityIndicator /> }} /> ``` **Issue**: Poor performance on older devices ```jsx // Solution: Use low-performance mode <SignatureCanvas webviewProps={{ androidLayerType: "software", androidHardwareAccelerationDisabled: true, cacheEnabled: false }} /> ``` **Issue**: Memory issues ```jsx // Solution: The component now handles this automatically // But you can customize via webviewProps if needed <SignatureCanvas webviewProps={{ cacheEnabled: false, // Reduce memory usage androidLayerType: "software" // Use software rendering }} /> ``` ## API Reference For detailed API documentation, see: - [WEBVIEW_PROPS.md](./WEBVIEW_PROPS.md) - WebView customization guide - [TypeScript definitions](./index.d.ts) - Complete type definitions ## Core Technology This component is built on: - [signature_pad.js](https://github.com/szimek/signature_pad) for the core signature functionality - React Native WebView for cross-platform rendering - Enhanced with performance monitoring and error recovery systems ## Contributing Contributions are welcome! Please read our contributing guidelines and submit pull requests to help improve this component. ### Development Setup ```bash # Clone the repository git clone https://github.com/YanYuanFE/react-native-signature-canvas.git # Install dependencies cd react-native-signature-canvas npm install # Run example apps cd example/expo-app npm install && npm start ``` ## Changelog ### v5.0.1 (Latest) - 🆕 Added `webviewProps` for WebView customization - 🆕 Enhanced error handling with automatic recovery - 🆕 Performance monitoring and optimization - 🆕 Memory leak prevention - 🆕 Improved TypeScript definitions - 🔧 Fixed global variable pollution in WebView JavaScript - 🔧 Added input validation for all methods - ⚡ Optimized rendering performance [View full changelog](./CHANGELOG.md) ## License MIT License - see [LICENSE](./LICENSE) file for details. ## Buy Me a Coffee ☕ If you find this project helpful, consider supporting its development with cryptocurrency donations: ### Cryptocurrency Donations | Currency | Address | QR Code | |----------|---------|----------| | **Bitcoin (BTC)** | `bc1phyz9agr0m9l2w9pd8w85w4da2jt3wl4cre7vv0qq4uesm3fv00pscu96tux` | ![BTC QR](https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=bc1phyz9agr0m9l2w9pd8w85w4da2jt3wl4cre7vv0qq4uesm3fv00pscu96tux) | | **Ethereum (ETH)** | `0xf5dfe16b1e64e8e3a92063fb2922447e13b48945` | ![ETH QR](https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=0xf5dfe16b1e64e8e3a92063fb2922447e13b48945) | | **Solana (SOL)** | `3VuhyeTj3hMSrmzq7NctHkgFxvJrmtAUQTzagEBEu3Vm` | ![SOL QR](https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=3VuhyeTj3hMSrmzq7NctHkgFxvJrmtAUQTzagEBEu3Vm) | ### Other Ways to Support - ⭐ Star this repository - 🐛 Report bugs and issues - 💡 Suggest new features - 🤝 Contribute code improvements - 📢 Share this project with others Your support helps maintain and improve this open-source project. Thank you! 🙏 --- **Made with ❤️ for the React Native community**