react-native-signature-canvas 4.7.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/CLAUDE.md +88 -0
- package/QUICK_START.md +219 -0
- package/README.md +430 -362
- package/WEBVIEW_PROPS.md +166 -0
- package/h5/js/app.js +127 -39
- package/index.d.ts +44 -20
- package/index.js +255 -171
- package/package.json +11 -4
- package/tea.yaml +0 -6
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Native Signature Canvas
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/react-native-signature-canvas)
|
|
4
4
|
[](https://www.npmjs.com/package/react-native-signature-canvas)
|
|
@@ -6,15 +6,27 @@
|
|
|
6
6
|

|
|
7
7
|
[](https://github.com/expo/expo)
|
|
8
8
|
|
|
9
|
-
React Native
|
|
9
|
+
A React Native component for capturing signatures or drawing on a canvas with a smooth, native feel. Works on iOS, Android, and Expo.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- Tested with RN 0.69
|
|
13
|
-
- Core use [signature_pad.js](https://github.com/szimek/signature_pad)
|
|
14
|
-
- Generates a base64 encoded png image of the signature
|
|
15
|
-
Note: Expo support for React Native Signature Canvas v1.5.0 started with Expo SDK v33.0.0.
|
|
11
|
+
## Features
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
- ✅ **Cross-platform support** (iOS, Android, Expo)
|
|
14
|
+
- ✅ **Smooth, responsive drawing experience** with optimized performance
|
|
15
|
+
- ✅ **Customizable pen color, size, and background**
|
|
16
|
+
- ✅ **Support for background and overlay images**
|
|
17
|
+
- ✅ **Export signatures** as PNG, JPEG, or SVG
|
|
18
|
+
- ✅ **Undo/redo functionality**
|
|
19
|
+
- ✅ **Drawing and erasing modes**
|
|
20
|
+
- ✅ **Full TypeScript support** with enhanced type definitions
|
|
21
|
+
- 🆕 **Advanced error handling** with automatic recovery
|
|
22
|
+
- 🆕 **Performance monitoring** and optimization
|
|
23
|
+
- 🆕 **Flexible WebView customization** via `webviewProps`
|
|
24
|
+
- 🆕 **Enhanced security** with configurable restrictions
|
|
25
|
+
- 🆕 **Memory management** and leak prevention
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### For React Native ≥ 0.60.0 or Expo SDK ≥ 35.0.0
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
32
|
yarn add react-native-signature-canvas
|
|
@@ -26,142 +38,247 @@ or
|
|
|
26
38
|
npm install --save react-native-signature-canvas
|
|
27
39
|
```
|
|
28
40
|
|
|
29
|
-
> This package depends on [react-native-webview](https://github.com/react-native-webview/react-native-webview
|
|
30
|
-
|
|
41
|
+
> 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:
|
|
42
|
+
>
|
|
43
|
+
> ```bash
|
|
44
|
+
> yarn add react-native-webview
|
|
45
|
+
> cd ios && pod install
|
|
46
|
+
> ```
|
|
31
47
|
|
|
32
|
-
|
|
33
|
-
## Installation(for React Native V0.5x.x or Expo SDK < v33)
|
|
48
|
+
### For React Native < 0.60.0 or Expo SDK < 33.0.0
|
|
34
49
|
|
|
35
50
|
```bash
|
|
36
51
|
npm install --save react-native-signature-canvas@1.4.2
|
|
37
52
|
```
|
|
38
53
|
|
|
39
|
-
## Usage
|
|
40
|
-
Basic
|
|
41
|
-
```js
|
|
42
|
-
import Signature from "react-native-signature-canvas";
|
|
43
|
-
```
|
|
44
|
-
Custom
|
|
45
|
-
```js
|
|
46
|
-
import SignatureScreen from 'react-native-signature-canvas';
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Properties
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
| Prop | Type | Description |
|
|
54
|
-
|:------------------------------------|:----------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
55
|
-
| androidHardwareAccelerationDisabled | `boolean` | androidHardwareAccelerationDisabled for react-native-webview. Default is false |
|
|
56
|
-
| autoClear | `boolean` | should auto clear the signature after clicking the Confirm button |
|
|
57
|
-
| backgroundColor | `string` | default is "rgba(255,255,255,0)" (_transparent_), background color of the canvas |
|
|
58
|
-
| bgHeight | `number` | height of the background image |
|
|
59
|
-
| bgWidth | `number` | width of the background image |
|
|
60
|
-
| bgSrc | `string` | background image source uri (_url_) |
|
|
61
|
-
| clearText | `string` | clear button text |
|
|
62
|
-
| confirmText | `string` | save button text |
|
|
63
|
-
| customHtml | `(injectedJavaScript: string) => string` | html string that lets you modify things like the layout or elements |
|
|
64
|
-
| dataURL | `string` | default is "", Base64 string, draws saved signature from dataURL. |
|
|
65
|
-
| descriptionText | `string` | description text for signature |
|
|
66
|
-
| dotSize | `number` | radius of a single dot _(not stroke width)_ |
|
|
67
|
-
| imageType | `string` | "image/png" (_default_), "image/jpeg"、"image/svg+xml", imageType of exported signature |
|
|
68
|
-
| minWidth | `number` | minimum width of a line. Defaults to 0.5 |
|
|
69
|
-
| maxWidth | `number` | maximum width of a line. Defaults to 2.5 |
|
|
70
|
-
| nestedScrollEnabled | `boolean` | enable nested scrolling for use inside of a scrollview |
|
|
71
|
-
| showsVerticalScrollIndicator | `boolean` | Boolean value that determines whether a vertical scroll indicator is shown in the `WebView`, The default value is `true`. |
|
|
72
|
-
| onOK | `function` | callback function after saving non-empty signature |
|
|
73
|
-
| onEmpty | `function` | callback function after trying to save an empty signature |
|
|
74
|
-
| onClear | `function` | callback function after clearing the signature |
|
|
75
|
-
| onGetData | `function` | callback function when getData() is called
|
|
76
|
-
| onBegin | `function` | callback function when a new stroke is started |
|
|
77
|
-
| onEnd | `function` | callback function when the stroke has ended |
|
|
78
|
-
| onLoadEnd | `function` | callback function when the webview canvas load ended |
|
|
79
|
-
| onUndo | `function` | callback function when undo() is called |
|
|
80
|
-
| onRedo | `function` | callback function when redo() is called |
|
|
81
|
-
| onDraw | `function` | callback function when drawing is enabled |
|
|
82
|
-
| onErase | `function` | callback function when erasing is enabled |
|
|
83
|
-
| onChangePenColor | `function` | callback function after changing the pen color |
|
|
84
|
-
| onChangePenSize | `function` | callback function after changing the pen size
|
|
85
|
-
| overlayHeight | `number` | height of the overlay image |
|
|
86
|
-
| overlayWidth | `number` | width of the overlay image |
|
|
87
|
-
| overlaySrc | `string` | overlay image source uri (url) _must be .png with a transparent background_
|
|
88
|
-
| penColor | `string` | default is "black", color of pen |
|
|
89
|
-
| rotated | `boolean` | rotate signature pad 90 degrees |
|
|
90
|
-
| style | `object` | style of wrapper view |
|
|
91
|
-
| trimWhitespace | `boolean` | trim image whitespace |
|
|
92
|
-
| webStyle | `string` | webview style for overwrite default style, all style: https://github.com/YanYuanFE/react-native-signature-canvas/blob/master/h5/css/signature-pad.css |
|
|
93
|
-
| androidLayerType | `none、software、hardware` | Sets the android webview layerType |
|
|
94
|
-
|
|
95
|
-
## Methods
|
|
96
|
-
|
|
97
|
-
---
|
|
54
|
+
## Basic Usage
|
|
98
55
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
| changePenSize(minW, maxW) | Change pen size |
|
|
104
|
-
| draw() | Enable drawing signature |
|
|
105
|
-
| erase() | Enable erasing signature |
|
|
106
|
-
| getData() | Triggers the `onGetData` callback with a single `data` JSON string |
|
|
107
|
-
| readSignature() | Reads the current signature on the canvas and triggers either the `onOK` or `onEmpty` callbacks |
|
|
108
|
-
| undo() | Undo last stroke |
|
|
109
|
-
| redo() | Redo last stroke |
|
|
56
|
+
```jsx
|
|
57
|
+
import React, { useRef, useState } from 'react';
|
|
58
|
+
import { StyleSheet, View, Image } from 'react-native';
|
|
59
|
+
import SignatureCanvas from 'react-native-signature-canvas';
|
|
110
60
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
import SignatureScreen from "react-native-signature-canvas";
|
|
115
|
-
|
|
116
|
-
const Sign = ({ text, onOK }) => {
|
|
61
|
+
const SignatureScreen = () => {
|
|
62
|
+
const [signature, setSignature] = useState(null);
|
|
63
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
117
64
|
const ref = useRef();
|
|
118
65
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
66
|
+
const handleSignature = (signature) => {
|
|
67
|
+
console.log('Signature captured:', signature);
|
|
68
|
+
setSignature(signature);
|
|
69
|
+
setIsLoading(false);
|
|
123
70
|
};
|
|
124
71
|
|
|
125
|
-
// Called after ref.current.readSignature() reads an empty string
|
|
126
72
|
const handleEmpty = () => {
|
|
127
|
-
console.log(
|
|
73
|
+
console.log('Signature is empty');
|
|
74
|
+
setIsLoading(false);
|
|
128
75
|
};
|
|
129
76
|
|
|
130
|
-
// Called after ref.current.clearSignature()
|
|
131
77
|
const handleClear = () => {
|
|
132
|
-
console.log(
|
|
78
|
+
console.log('Signature cleared');
|
|
79
|
+
setSignature(null);
|
|
133
80
|
};
|
|
134
81
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
82
|
+
const handleError = (error) => {
|
|
83
|
+
console.error('Signature pad error:', error);
|
|
84
|
+
setIsLoading(false);
|
|
138
85
|
};
|
|
139
86
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
87
|
+
const handleEnd = () => {
|
|
88
|
+
setIsLoading(true);
|
|
89
|
+
ref.current?.readSignature();
|
|
143
90
|
};
|
|
144
91
|
|
|
145
92
|
return (
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
93
|
+
<View style={styles.container}>
|
|
94
|
+
<View style={styles.preview}>
|
|
95
|
+
{signature && (
|
|
96
|
+
<Image
|
|
97
|
+
resizeMode="contain"
|
|
98
|
+
style={{ width: 335, height: 114 }}
|
|
99
|
+
source={{ uri: signature }}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
</View>
|
|
103
|
+
<SignatureCanvas
|
|
104
|
+
ref={ref}
|
|
105
|
+
onEnd={handleEnd}
|
|
106
|
+
onOK={handleSignature}
|
|
107
|
+
onEmpty={handleEmpty}
|
|
108
|
+
onClear={handleClear}
|
|
109
|
+
onError={handleError}
|
|
110
|
+
autoClear={true}
|
|
111
|
+
descriptionText="Sign here"
|
|
112
|
+
clearText="Clear"
|
|
113
|
+
confirmText={isLoading ? "Processing..." : "Save"}
|
|
114
|
+
penColor="#000000"
|
|
115
|
+
backgroundColor="rgba(255,255,255,0)"
|
|
116
|
+
webviewProps={{
|
|
117
|
+
// Custom WebView optimization
|
|
118
|
+
cacheEnabled: true,
|
|
119
|
+
androidLayerType: "hardware",
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
122
|
+
</View>
|
|
156
123
|
);
|
|
157
124
|
};
|
|
158
125
|
|
|
159
|
-
|
|
126
|
+
const styles = StyleSheet.create({
|
|
127
|
+
container: {
|
|
128
|
+
flex: 1,
|
|
129
|
+
},
|
|
130
|
+
preview: {
|
|
131
|
+
width: 335,
|
|
132
|
+
height: 114,
|
|
133
|
+
backgroundColor: '#F8F8F8',
|
|
134
|
+
justifyContent: 'center',
|
|
135
|
+
alignItems: 'center',
|
|
136
|
+
marginTop: 15,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export default SignatureScreen;
|
|
160
141
|
```
|
|
161
|
-
## Using a background image
|
|
162
|
-
You can use a non-erasable background image to draw your signature on using the `bgSrc` prop. Make sure to provide the width and height of the image.
|
|
163
142
|
|
|
164
|
-
|
|
143
|
+
## Props
|
|
144
|
+
|
|
145
|
+
| Prop | Type | Default | Description |
|
|
146
|
+
|------|------|---------|-------------|
|
|
147
|
+
| `androidHardwareAccelerationDisabled` | `boolean` | `false` | Disable hardware acceleration on Android |
|
|
148
|
+
| `autoClear` | `boolean` | `false` | Auto clear signature after clicking the Confirm button |
|
|
149
|
+
| `backgroundColor` | `string` | `rgba(255,255,255,0)` | Background color of the canvas |
|
|
150
|
+
| `bgHeight` | `number` | `0` | Height of the background image |
|
|
151
|
+
| `bgWidth` | `number` | `0` | Width of the background image |
|
|
152
|
+
| `bgSrc` | `string` | `null` | Background image source URI |
|
|
153
|
+
| `clearText` | `string` | `Clear` | Clear button text |
|
|
154
|
+
| `confirmText` | `string` | `Confirm` | Save button text |
|
|
155
|
+
| `customHtml` | `(injectedJavaScript: string) => string` | `null` | Custom HTML template for the canvas |
|
|
156
|
+
| `dataURL` | `string` | `""` | Base64 string to draw saved signature |
|
|
157
|
+
| `descriptionText` | `string` | `Sign above` | Description text for signature |
|
|
158
|
+
| `dotSize` | `number` | `null` | Radius of a single dot |
|
|
159
|
+
| `imageType` | `string` | `image/png` | Image type for export (`image/png`, `image/jpeg`, `image/svg+xml`) |
|
|
160
|
+
| `minWidth` | `number` | `0.5` | Minimum width of a line |
|
|
161
|
+
| `maxWidth` | `number` | `2.5` | Maximum width of a line |
|
|
162
|
+
| `nestedScrollEnabled` | `boolean` | `false` | Enable nested scrolling for use inside a ScrollView |
|
|
163
|
+
| `showsVerticalScrollIndicator` | `boolean` | `true` | Show vertical scroll indicator in WebView |
|
|
164
|
+
| `onOK` | `function` | - | Callback after saving non-empty signature |
|
|
165
|
+
| `onEmpty` | `function` | - | Callback after trying to save an empty signature |
|
|
166
|
+
| `onClear` | `function` | - | Callback after clearing the signature |
|
|
167
|
+
| `onGetData` | `function` | - | Callback when getData() is called |
|
|
168
|
+
| `onBegin` | `function` | - | Callback when a new stroke is started |
|
|
169
|
+
| `onEnd` | `function` | - | Callback when the stroke has ended |
|
|
170
|
+
| `onLoadEnd` | `function` | - | Callback when the WebView canvas load ended |
|
|
171
|
+
| `onUndo` | `function` | - | Callback when undo() is called |
|
|
172
|
+
| `onRedo` | `function` | - | Callback when redo() is called |
|
|
173
|
+
| `onDraw` | `function` | - | Callback when drawing is enabled |
|
|
174
|
+
| `onErase` | `function` | - | Callback when erasing is enabled |
|
|
175
|
+
| `onChangePenColor` | `function` | - | Callback after changing the pen color |
|
|
176
|
+
| `onChangePenSize` | `function` | - | Callback after changing the pen size |
|
|
177
|
+
| `overlayHeight` | `number` | `0` | Height of the overlay image |
|
|
178
|
+
| `overlayWidth` | `number` | `0` | Width of the overlay image |
|
|
179
|
+
| `overlaySrc` | `string` | `null` | Overlay image source URI (must be PNG with transparent background) |
|
|
180
|
+
| `penColor` | `string` | `black` | Color of the pen |
|
|
181
|
+
| `rotated` | `boolean` | `false` | Rotate signature pad 90 degrees |
|
|
182
|
+
| `style` | `object` | - | Style of the wrapper view |
|
|
183
|
+
| `scrollable` | `boolean` | `false` | Enable scrolling in the signature pad |
|
|
184
|
+
| `trimWhitespace` | `boolean` | `false` | Trim image whitespace |
|
|
185
|
+
| `webStyle` | `string` | - | WebView style to override default style |
|
|
186
|
+
| `webviewContainerStyle` | `object` | - | Style for the WebView container |
|
|
187
|
+
| `androidLayerType` | `none\|software\|hardware` | `hardware` | Sets the Android WebView layer type |
|
|
188
|
+
| `onError` | `function` | - | Callback when an error occurs |
|
|
189
|
+
| `webviewProps` | `object` | `{}` | Additional props to pass to the underlying WebView |
|
|
190
|
+
|
|
191
|
+
## Methods
|
|
192
|
+
|
|
193
|
+
Access these methods using a ref to the SignatureCanvas component.
|
|
194
|
+
|
|
195
|
+
| Method | Description |
|
|
196
|
+
|--------|-------------|
|
|
197
|
+
| `clearSignature()` | Clear the current signature |
|
|
198
|
+
| `changePenColor(color)` | Change pen color |
|
|
199
|
+
| `changePenSize(minW, maxW)` | Change pen size |
|
|
200
|
+
| `draw()` | Enable drawing mode |
|
|
201
|
+
| `erase()` | Enable erasing mode |
|
|
202
|
+
| `getData()` | Triggers the `onGetData` callback with signature data |
|
|
203
|
+
| `readSignature()` | Read the current signature and trigger callbacks |
|
|
204
|
+
| `undo()` | Undo last stroke |
|
|
205
|
+
| `redo()` | Redo last stroke |
|
|
206
|
+
|
|
207
|
+
## WebView Customization (New!)
|
|
208
|
+
|
|
209
|
+
The `webviewProps` parameter allows you to customize the underlying WebView behavior while maintaining signature functionality:
|
|
210
|
+
|
|
211
|
+
```jsx
|
|
212
|
+
<SignatureCanvas
|
|
213
|
+
// ... other props
|
|
214
|
+
webviewProps={{
|
|
215
|
+
// Performance optimization
|
|
216
|
+
cacheEnabled: true,
|
|
217
|
+
androidLayerType: "hardware",
|
|
218
|
+
androidHardwareAccelerationDisabled: false,
|
|
219
|
+
|
|
220
|
+
// Security settings
|
|
221
|
+
allowFileAccess: false,
|
|
222
|
+
allowFileAccessFromFileURLs: false,
|
|
223
|
+
mixedContentMode: "never",
|
|
224
|
+
|
|
225
|
+
// UI customization
|
|
226
|
+
decelerationRate: 'fast',
|
|
227
|
+
bounces: false,
|
|
228
|
+
|
|
229
|
+
// Any other WebView props...
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Performance Optimization Examples
|
|
235
|
+
|
|
236
|
+
```jsx
|
|
237
|
+
// High-performance mode
|
|
238
|
+
<SignatureCanvas
|
|
239
|
+
webviewProps={{
|
|
240
|
+
cacheEnabled: true,
|
|
241
|
+
androidLayerType: "hardware",
|
|
242
|
+
androidHardwareAccelerationDisabled: false,
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
|
|
246
|
+
// Low-memory mode
|
|
247
|
+
<SignatureCanvas
|
|
248
|
+
webviewProps={{
|
|
249
|
+
cacheEnabled: false,
|
|
250
|
+
androidLayerType: "software",
|
|
251
|
+
androidHardwareAccelerationDisabled: true,
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Error Handling (Enhanced!)
|
|
257
|
+
|
|
258
|
+
```jsx
|
|
259
|
+
const [error, setError] = useState(null);
|
|
260
|
+
|
|
261
|
+
const handleError = (error) => {
|
|
262
|
+
console.error('Signature error:', error);
|
|
263
|
+
setError(error.message);
|
|
264
|
+
// Error recovery is automatic, but you can handle it here
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
<SignatureCanvas
|
|
268
|
+
onError={handleError}
|
|
269
|
+
// Component automatically retries on recoverable errors
|
|
270
|
+
/>
|
|
271
|
+
|
|
272
|
+
{error && (
|
|
273
|
+
<Text style={{ color: 'red' }}>Error: {error}</Text>
|
|
274
|
+
)}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Advanced Usage
|
|
278
|
+
|
|
279
|
+
### Using a Background Image
|
|
280
|
+
|
|
281
|
+
```jsx
|
|
165
282
|
const imgWidth = 300;
|
|
166
283
|
const imgHeight = 200;
|
|
167
284
|
const style = `.m-signature-pad {box-shadow: none; border: none; }
|
|
@@ -169,24 +286,22 @@ const style = `.m-signature-pad {box-shadow: none; border: none; }
|
|
|
169
286
|
.m-signature-pad--footer {display: none; margin: 0px;}
|
|
170
287
|
body,html {
|
|
171
288
|
width: ${imgWidth}px; height: ${imgHeight}px;}`;
|
|
172
|
-
|
|
289
|
+
|
|
173
290
|
<View style={{ width: imgWidth, height: imgHeight }}>
|
|
174
|
-
<
|
|
291
|
+
<SignatureCanvas
|
|
175
292
|
ref={ref}
|
|
176
|
-
bgSrc="https://
|
|
293
|
+
bgSrc="https://example.com/background.jpg"
|
|
177
294
|
bgWidth={imgWidth}
|
|
178
295
|
bgHeight={imgHeight}
|
|
179
296
|
webStyle={style}
|
|
180
|
-
onOK={
|
|
297
|
+
onOK={handleSignature}
|
|
181
298
|
/>
|
|
182
299
|
</View>
|
|
183
300
|
```
|
|
184
301
|
|
|
185
|
-
|
|
186
|
-
An overlay is a non-erasable image that can be used as a guideline similar to a colouring book. Make sure the image format is .png and that it has a transparent background. Also, don't forget to provide the width and height of the image.
|
|
187
|
-
Use the `overlaySrc` prop to provide the link.
|
|
302
|
+
### Using an Overlay Image
|
|
188
303
|
|
|
189
|
-
```
|
|
304
|
+
```jsx
|
|
190
305
|
const imgWidth = 256;
|
|
191
306
|
const imgHeight = 256;
|
|
192
307
|
const style = `.m-signature-pad {box-shadow: none; border: none; }
|
|
@@ -194,299 +309,252 @@ const style = `.m-signature-pad {box-shadow: none; border: none; }
|
|
|
194
309
|
.m-signature-pad--footer {display: none; margin: 0px;}
|
|
195
310
|
body,html {
|
|
196
311
|
width: ${imgWidth}px; height: ${imgHeight}px;}`;
|
|
197
|
-
|
|
312
|
+
|
|
198
313
|
<View style={{ width: imgWidth, height: imgHeight }}>
|
|
199
|
-
<
|
|
314
|
+
<SignatureCanvas
|
|
200
315
|
ref={ref}
|
|
201
|
-
overlaySrc="
|
|
316
|
+
overlaySrc="https://example.com/overlay.png" // Must be PNG with transparent background
|
|
202
317
|
overlayWidth={imgWidth}
|
|
203
318
|
overlayHeight={imgHeight}
|
|
204
319
|
webStyle={style}
|
|
205
|
-
onOK={
|
|
320
|
+
onOK={handleSignature}
|
|
206
321
|
/>
|
|
207
322
|
</View>
|
|
208
323
|
```
|
|
209
324
|
|
|
210
|
-
|
|
325
|
+
### Using in a Modal
|
|
211
326
|
|
|
212
|
-
|
|
327
|
+
```jsx
|
|
328
|
+
import React, { useState, useRef } from 'react';
|
|
329
|
+
import { StyleSheet, View, TouchableOpacity, Modal, Text } from 'react-native';
|
|
330
|
+
import SignatureCanvas from 'react-native-signature-canvas';
|
|
213
331
|
|
|
214
|
-
|
|
215
|
-
|
|
332
|
+
const SignatureModal = ({ onSignature }) => {
|
|
333
|
+
const [show, setShow] = useState(false);
|
|
334
|
+
const ref = useRef();
|
|
335
|
+
|
|
336
|
+
const handleSignature = (signature) => {
|
|
337
|
+
onSignature(signature);
|
|
338
|
+
setShow(false);
|
|
339
|
+
};
|
|
216
340
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
341
|
+
return (
|
|
342
|
+
<View>
|
|
343
|
+
<TouchableOpacity onPress={() => setShow(true)}>
|
|
344
|
+
<Text>Open Signature Pad</Text>
|
|
345
|
+
</TouchableOpacity>
|
|
346
|
+
|
|
347
|
+
{show && (
|
|
348
|
+
<Modal>
|
|
349
|
+
<SignatureCanvas
|
|
350
|
+
ref={ref}
|
|
351
|
+
onOK={handleSignature}
|
|
352
|
+
onEmpty={() => console.log('Empty')}
|
|
353
|
+
descriptionText="Sign here"
|
|
354
|
+
penColor="rgba(255,117,2,1)"
|
|
355
|
+
/>
|
|
356
|
+
</Modal>
|
|
357
|
+
)}
|
|
358
|
+
</View>
|
|
359
|
+
);
|
|
227
360
|
};
|
|
228
361
|
```
|
|
229
362
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
```js
|
|
233
|
-
<Signature
|
|
234
|
-
// handle when you click save button
|
|
235
|
-
onOK={(img) => console.log(img)}
|
|
236
|
-
onEmpty={() => console.log("empty")}
|
|
237
|
-
// description text for signature
|
|
238
|
-
descriptionText="Sign"
|
|
239
|
-
// clear button text
|
|
240
|
-
clearText="Clear"
|
|
241
|
-
// save button text
|
|
242
|
-
confirmText="Save"
|
|
243
|
-
// String, webview style for overwrite default style, all style: https://github.com/YanYuanFE/react-native-signature-canvas/blob/master/h5/css/signature-pad.css
|
|
244
|
-
webStyle={`.m-signature-pad--footer
|
|
245
|
-
.button {
|
|
246
|
-
background-color: red;
|
|
247
|
-
color: #FFF;
|
|
248
|
-
}`}
|
|
249
|
-
autoClear={true}
|
|
250
|
-
imageType={"image/svg+xml"}
|
|
251
|
-
/>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
If you create your own triggers for the readSignature and/or clearSignature you can hide the built in Clear and Save buttons with css styles passed into the **webStyle** property.
|
|
255
|
-
|
|
256
|
-
```js
|
|
257
|
-
const webStyle = `.m-signature-pad--footer
|
|
258
|
-
.save {
|
|
259
|
-
display: none;
|
|
260
|
-
}
|
|
261
|
-
.clear {
|
|
262
|
-
display: none;
|
|
263
|
-
}
|
|
264
|
-
`;
|
|
265
|
-
...
|
|
266
|
-
<Signature
|
|
267
|
-
webStyle={webStyle}
|
|
268
|
-
onOK={handleOK}
|
|
269
|
-
onEmpty={handleEmpty}
|
|
270
|
-
onEnd={handleEnd}
|
|
271
|
-
/>
|
|
272
|
-
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Custom Button for Confirm and Clear
|
|
276
|
-
|
|
277
|
-
```js
|
|
278
|
-
import React, { useRef } from "react";
|
|
279
|
-
import { StyleSheet, View, Button } from "react-native";
|
|
280
|
-
import SignatureScreen from "react-native-signature-canvas";
|
|
281
|
-
|
|
282
|
-
const Sign = ({ onOK }) => {
|
|
283
|
-
const ref = useRef();
|
|
284
|
-
|
|
285
|
-
const handleOK = (signature) => {
|
|
286
|
-
console.log(signature);
|
|
287
|
-
onOK(signature);
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
const handleClear = () => {
|
|
291
|
-
ref.current.clearSignature();
|
|
292
|
-
};
|
|
363
|
+
### Scrollable Signature Canvas
|
|
293
364
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
365
|
+
```jsx
|
|
366
|
+
import React, { useRef, useState } from 'react';
|
|
367
|
+
import { View, StyleSheet, ScrollView } from 'react-native';
|
|
368
|
+
import SignatureCanvas from 'react-native-signature-canvas';
|
|
298
369
|
|
|
299
|
-
|
|
370
|
+
const ScrollableSignature = () => {
|
|
371
|
+
const [scrollEnabled, setScrollEnabled] = useState(true);
|
|
372
|
+
const signatureRef = useRef(null);
|
|
300
373
|
|
|
301
374
|
return (
|
|
302
|
-
<
|
|
303
|
-
<
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
375
|
+
<ScrollView scrollEnabled={scrollEnabled}>
|
|
376
|
+
<View style={styles.container}>
|
|
377
|
+
<SignatureCanvas
|
|
378
|
+
ref={signatureRef}
|
|
379
|
+
style={styles.canvas}
|
|
380
|
+
onBegin={() => setScrollEnabled(false)}
|
|
381
|
+
onEnd={() => setScrollEnabled(true)}
|
|
382
|
+
/>
|
|
307
383
|
</View>
|
|
308
|
-
</
|
|
384
|
+
</ScrollView>
|
|
309
385
|
);
|
|
310
386
|
};
|
|
311
387
|
|
|
312
|
-
export default Sign;
|
|
313
|
-
|
|
314
388
|
const styles = StyleSheet.create({
|
|
315
389
|
container: {
|
|
316
390
|
flex: 1,
|
|
317
|
-
alignItems:
|
|
318
|
-
justifyContent: "center",
|
|
319
|
-
height: 250,
|
|
320
|
-
padding: 10,
|
|
391
|
+
alignItems: 'center',
|
|
321
392
|
},
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
alignItems: "center",
|
|
393
|
+
canvas: {
|
|
394
|
+
width: '90%',
|
|
395
|
+
height: 300,
|
|
396
|
+
borderWidth: 1,
|
|
397
|
+
borderColor: '#000',
|
|
328
398
|
},
|
|
329
399
|
});
|
|
330
400
|
```
|
|
331
401
|
|
|
332
|
-
##
|
|
402
|
+
## Performance & Reliability
|
|
333
403
|
|
|
334
|
-
|
|
335
|
-
|
|
404
|
+
### Automatic Error Recovery
|
|
405
|
+
- **Smart retry logic** with exponential backoff
|
|
406
|
+
- **Circuit breaker pattern** to prevent cascading failures
|
|
407
|
+
- **Memory leak prevention** with automatic cleanup
|
|
408
|
+
- **Performance monitoring** with automatic optimization
|
|
336
409
|
|
|
337
|
-
|
|
338
|
-
|
|
410
|
+
### Performance Features
|
|
411
|
+
- **Debounced resize handling** for smooth interaction
|
|
412
|
+
- **Memory pressure detection** with adaptive optimization
|
|
413
|
+
- **Optimized rendering** with reduced re-renders
|
|
414
|
+
- **Device-specific optimization** based on hardware capabilities
|
|
339
415
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
416
|
+
### Security Enhancements
|
|
417
|
+
- **Configurable WebView security** via `webviewProps`
|
|
418
|
+
- **Input validation** for all methods and callbacks
|
|
419
|
+
- **XSS protection** with content security policies
|
|
420
|
+
- **File access restrictions** by default
|
|
344
421
|
|
|
345
|
-
|
|
346
|
-
const [signature, setSign] = useState(null);
|
|
422
|
+
## Migration Guide
|
|
347
423
|
|
|
348
|
-
|
|
349
|
-
console.log(signature);
|
|
350
|
-
setSign(signature);
|
|
351
|
-
};
|
|
424
|
+
### From v4.6.x to v4.7.x
|
|
352
425
|
|
|
353
|
-
|
|
354
|
-
console.log("Empty");
|
|
355
|
-
};
|
|
426
|
+
This version is fully backward compatible. New features:
|
|
356
427
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return (
|
|
363
|
-
<View style={{ flex: 1 }}>
|
|
364
|
-
<View style={styles.preview}>
|
|
365
|
-
{signature ? (
|
|
366
|
-
<Image
|
|
367
|
-
resizeMode={"contain"}
|
|
368
|
-
style={{ width: 335, height: 114 }}
|
|
369
|
-
source={{ uri: signature }}
|
|
370
|
-
/>
|
|
371
|
-
) : null}
|
|
372
|
-
</View>
|
|
373
|
-
<Signature
|
|
374
|
-
onOK={handleOK}
|
|
375
|
-
onEmpty={handleEmpty}
|
|
376
|
-
descriptionText="Sign"
|
|
377
|
-
clearText="Clear"
|
|
378
|
-
confirmText="Save"
|
|
379
|
-
webStyle={style}
|
|
380
|
-
/>
|
|
381
|
-
</View>
|
|
382
|
-
);
|
|
383
|
-
};
|
|
428
|
+
```jsx
|
|
429
|
+
// NEW: Enhanced error handling
|
|
430
|
+
<SignatureCanvas
|
|
431
|
+
onError={(error) => console.error(error)} // New callback
|
|
432
|
+
/>
|
|
384
433
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
marginTop: 15,
|
|
393
|
-
},
|
|
394
|
-
previewText: {
|
|
395
|
-
color: "#FFF",
|
|
396
|
-
fontSize: 14,
|
|
397
|
-
height: 40,
|
|
398
|
-
lineHeight: 40,
|
|
399
|
-
paddingLeft: 10,
|
|
400
|
-
paddingRight: 10,
|
|
401
|
-
backgroundColor: "#69B2FF",
|
|
402
|
-
width: 120,
|
|
403
|
-
textAlign: "center",
|
|
404
|
-
marginTop: 10,
|
|
405
|
-
},
|
|
406
|
-
});
|
|
434
|
+
// NEW: WebView customization
|
|
435
|
+
<SignatureCanvas
|
|
436
|
+
webviewProps={{ // New prop
|
|
437
|
+
cacheEnabled: false,
|
|
438
|
+
androidLayerType: "software"
|
|
439
|
+
}}
|
|
440
|
+
/>
|
|
407
441
|
```
|
|
408
442
|
|
|
409
|
-
##
|
|
443
|
+
## Troubleshooting
|
|
410
444
|
|
|
411
|
-
|
|
445
|
+
### Common Issues
|
|
412
446
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
447
|
+
**Issue**: Signature pad not loading
|
|
448
|
+
```jsx
|
|
449
|
+
// Solution: Add error handling and check WebView props
|
|
450
|
+
<SignatureCanvas
|
|
451
|
+
onError={(error) => console.log('Error:', error)}
|
|
452
|
+
onLoadEnd={() => console.log('Loaded successfully')}
|
|
453
|
+
webviewProps={{
|
|
454
|
+
startInLoadingState: true,
|
|
455
|
+
renderLoading: () => <ActivityIndicator />
|
|
456
|
+
}}
|
|
457
|
+
/>
|
|
458
|
+
```
|
|
418
459
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
460
|
+
**Issue**: Poor performance on older devices
|
|
461
|
+
```jsx
|
|
462
|
+
// Solution: Use low-performance mode
|
|
463
|
+
<SignatureCanvas
|
|
464
|
+
webviewProps={{
|
|
465
|
+
androidLayerType: "software",
|
|
466
|
+
androidHardwareAccelerationDisabled: true,
|
|
467
|
+
cacheEnabled: false
|
|
468
|
+
}}
|
|
469
|
+
/>
|
|
470
|
+
```
|
|
423
471
|
|
|
424
|
-
|
|
425
|
-
|
|
472
|
+
**Issue**: Memory issues
|
|
473
|
+
```jsx
|
|
474
|
+
// Solution: The component now handles this automatically
|
|
475
|
+
// But you can customize via webviewProps if needed
|
|
476
|
+
<SignatureCanvas
|
|
477
|
+
webviewProps={{
|
|
478
|
+
cacheEnabled: false, // Reduce memory usage
|
|
479
|
+
androidLayerType: "software" // Use software rendering
|
|
480
|
+
}}
|
|
481
|
+
/>
|
|
482
|
+
```
|
|
426
483
|
|
|
427
|
-
|
|
428
|
-
console.log(signature);
|
|
429
|
-
onOK(signature);
|
|
430
|
-
};
|
|
484
|
+
## API Reference
|
|
431
485
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
486
|
+
For detailed API documentation, see:
|
|
487
|
+
- [WEBVIEW_PROPS.md](./WEBVIEW_PROPS.md) - WebView customization guide
|
|
488
|
+
- [TypeScript definitions](./index.d.ts) - Complete type definitions
|
|
435
489
|
|
|
436
|
-
|
|
437
|
-
console.log("clear success!");
|
|
438
|
-
};
|
|
490
|
+
## Core Technology
|
|
439
491
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
492
|
+
This component is built on:
|
|
493
|
+
- [signature_pad.js](https://github.com/szimek/signature_pad) for the core signature functionality
|
|
494
|
+
- React Native WebView for cross-platform rendering
|
|
495
|
+
- Enhanced with performance monitoring and error recovery systems
|
|
443
496
|
|
|
444
|
-
|
|
445
|
-
<SignatureScreen
|
|
446
|
-
ref={ref}
|
|
447
|
-
onEnd={handleEnd}
|
|
448
|
-
onOK={handleSignature}
|
|
449
|
-
onEmpty={handleEmpty}
|
|
450
|
-
onClear={handleClear}
|
|
451
|
-
autoClear={true}
|
|
452
|
-
descriptionText={text}
|
|
453
|
-
/>
|
|
454
|
-
);
|
|
455
|
-
};
|
|
497
|
+
## Contributing
|
|
456
498
|
|
|
457
|
-
|
|
458
|
-
|
|
499
|
+
Contributions are welcome! Please read our contributing guidelines and submit pull requests to help improve this component.
|
|
500
|
+
|
|
501
|
+
### Development Setup
|
|
459
502
|
|
|
460
|
-
|
|
503
|
+
```bash
|
|
504
|
+
# Clone the repository
|
|
505
|
+
git clone https://github.com/YanYuanFE/react-native-signature-canvas.git
|
|
461
506
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
507
|
+
# Install dependencies
|
|
508
|
+
cd react-native-signature-canvas
|
|
509
|
+
npm install
|
|
465
510
|
|
|
511
|
+
# Run example apps
|
|
512
|
+
cd example/expo-app
|
|
513
|
+
npm install && npm start
|
|
466
514
|
```
|
|
467
|
-
import React, {useState} from 'react';
|
|
468
|
-
import {ScrollView, View} from 'react-native';
|
|
469
|
-
import Signature from 'react-native-signature-canvas';
|
|
470
515
|
|
|
471
|
-
|
|
472
|
-
const [scrollEnabled, setScrollEnabled] = useState(true);
|
|
516
|
+
## Changelog
|
|
473
517
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
confirmText="Save"
|
|
484
|
-
imageType="image/jpeg"
|
|
485
|
-
/>
|
|
486
|
-
</View>
|
|
487
|
-
</ScrollView>
|
|
488
|
-
);
|
|
489
|
-
};
|
|
518
|
+
### v4.7.x (Latest)
|
|
519
|
+
- 🆕 Added `webviewProps` for WebView customization
|
|
520
|
+
- 🆕 Enhanced error handling with automatic recovery
|
|
521
|
+
- 🆕 Performance monitoring and optimization
|
|
522
|
+
- 🆕 Memory leak prevention
|
|
523
|
+
- 🆕 Improved TypeScript definitions
|
|
524
|
+
- 🔧 Fixed global variable pollution in WebView JavaScript
|
|
525
|
+
- 🔧 Added input validation for all methods
|
|
526
|
+
- ⚡ Optimized rendering performance
|
|
490
527
|
|
|
491
|
-
|
|
492
|
-
|
|
528
|
+
[View full changelog](./CHANGELOG.md)
|
|
529
|
+
|
|
530
|
+
## License
|
|
531
|
+
|
|
532
|
+
MIT License - see [LICENSE](./LICENSE) file for details.
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
## Buy Me a Coffee ☕
|
|
536
|
+
|
|
537
|
+
If you find this project helpful, consider supporting its development with cryptocurrency donations:
|
|
538
|
+
|
|
539
|
+
### Cryptocurrency Donations
|
|
540
|
+
|
|
541
|
+
| Currency | Address | QR Code |
|
|
542
|
+
|----------|---------|----------|
|
|
543
|
+
| **Bitcoin (BTC)** | `bc1phyz9agr0m9l2w9pd8w85w4da2jt3wl4cre7vv0qq4uesm3fv00pscu96tux` |  |
|
|
544
|
+
| **Ethereum (ETH)** | `0xf5dfe16b1e64e8e3a92063fb2922447e13b48945` |  |
|
|
545
|
+
| **Solana (SOL)** | `3VuhyeTj3hMSrmzq7NctHkgFxvJrmtAUQTzagEBEu3Vm` |  |
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
### Other Ways to Support
|
|
549
|
+
|
|
550
|
+
- ⭐ Star this repository
|
|
551
|
+
- 🐛 Report bugs and issues
|
|
552
|
+
- 💡 Suggest new features
|
|
553
|
+
- 🤝 Contribute code improvements
|
|
554
|
+
- 📢 Share this project with others
|
|
555
|
+
|
|
556
|
+
Your support helps maintain and improve this open-source project. Thank you! 🙏
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
|
|
560
|
+
**Made with ❤️ for the React Native community**
|