@rick427/react-native-liveness 0.1.8 → 0.1.9
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/lib/module/LivenessCamera.js +19 -16
- package/lib/module/LivenessCamera.js.map +1 -1
- package/lib/module/LivenessCameraModal.js +117 -0
- package/lib/module/LivenessCameraModal.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/LivenessCamera.d.ts +1 -1
- package/lib/typescript/src/LivenessCamera.d.ts.map +1 -1
- package/lib/typescript/src/LivenessCameraModal.d.ts +15 -0
- package/lib/typescript/src/LivenessCameraModal.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +35 -3
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/LivenessCamera.tsx +23 -15
- package/src/LivenessCameraModal.tsx +116 -0
- package/src/index.ts +2 -0
- package/src/types.ts +42 -3
|
@@ -6,6 +6,8 @@ import { Camera, useCameraDevice, useCameraFormat, useCameraPermission } from 'r
|
|
|
6
6
|
import { Circle, Path, Svg } from 'react-native-svg';
|
|
7
7
|
import { useLivenessCamera } from "./useLivenessCamera.js";
|
|
8
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
const DEFAULT_FONT = 'Baloo-Medium';
|
|
10
|
+
|
|
9
11
|
// Circle diameter = 82 % of container width — large enough to fit any face
|
|
10
12
|
// comfortably without the user needing to fiddle with distance.
|
|
11
13
|
const CIRCLE_DIAMETER_RATIO = 0.82;
|
|
@@ -51,13 +53,9 @@ function CircleOverlay({
|
|
|
51
53
|
}) {
|
|
52
54
|
if (width === 0 || height === 0) return null;
|
|
53
55
|
const cx = width / 2;
|
|
54
|
-
// Centre the circle slightly above midpoint so the face sits naturally
|
|
55
56
|
const cy = height * 0.42;
|
|
56
57
|
const r = width * CIRCLE_DIAMETER_RATIO / 2;
|
|
57
58
|
const color = getCircleColor(state, score);
|
|
58
|
-
|
|
59
|
-
// Compound path: full-screen rect + circle cutout.
|
|
60
|
-
// evenodd fill rule makes the circle area transparent.
|
|
61
59
|
const scrimD = `M0 0H${width}V${height}H0Z ${circlePath(cx, cy, r)}`;
|
|
62
60
|
return /*#__PURE__*/_jsxs(Svg, {
|
|
63
61
|
style: StyleSheet.absoluteFill,
|
|
@@ -78,10 +76,9 @@ function CircleOverlay({
|
|
|
78
76
|
});
|
|
79
77
|
}
|
|
80
78
|
function CountdownBubble({
|
|
81
|
-
value
|
|
79
|
+
value,
|
|
80
|
+
fontFamily
|
|
82
81
|
}) {
|
|
83
|
-
// key={countdown} in the parent remounts this component on every tick,
|
|
84
|
-
// so [] deps are correct — each mount runs a fresh animation.
|
|
85
82
|
const scale = useRef(new Animated.Value(0)).current;
|
|
86
83
|
const opacity = useRef(new Animated.Value(0)).current;
|
|
87
84
|
useEffect(() => {
|
|
@@ -117,7 +114,9 @@ function CountdownBubble({
|
|
|
117
114
|
}]
|
|
118
115
|
}],
|
|
119
116
|
children: /*#__PURE__*/_jsx(Text, {
|
|
120
|
-
style: styles.countdownText,
|
|
117
|
+
style: [styles.countdownText, {
|
|
118
|
+
fontFamily
|
|
119
|
+
}],
|
|
121
120
|
children: value
|
|
122
121
|
})
|
|
123
122
|
});
|
|
@@ -130,6 +129,7 @@ export function LivenessCamera({
|
|
|
130
129
|
livenessThreshold = 0.75,
|
|
131
130
|
confirmFrames = 10,
|
|
132
131
|
soundEnabled = true,
|
|
132
|
+
fontFamily = DEFAULT_FONT,
|
|
133
133
|
style
|
|
134
134
|
}) {
|
|
135
135
|
const {
|
|
@@ -137,8 +137,6 @@ export function LivenessCamera({
|
|
|
137
137
|
requestPermission
|
|
138
138
|
} = useCameraPermission();
|
|
139
139
|
const device = useCameraDevice('front');
|
|
140
|
-
// Pick the best format that supports up to 60 fps. Falls back gracefully
|
|
141
|
-
// to whatever the device offers if 60 fps isn't available.
|
|
142
140
|
const format = useCameraFormat(device, [{
|
|
143
141
|
fps: 60
|
|
144
142
|
}]);
|
|
@@ -185,7 +183,9 @@ export function LivenessCamera({
|
|
|
185
183
|
return /*#__PURE__*/_jsx(View, {
|
|
186
184
|
style: [styles.root, style, styles.centered],
|
|
187
185
|
children: /*#__PURE__*/_jsx(Text, {
|
|
188
|
-
style: styles.permissionText,
|
|
186
|
+
style: [styles.permissionText, {
|
|
187
|
+
fontFamily
|
|
188
|
+
}],
|
|
189
189
|
children: "Camera permission required"
|
|
190
190
|
})
|
|
191
191
|
});
|
|
@@ -194,7 +194,9 @@ export function LivenessCamera({
|
|
|
194
194
|
return /*#__PURE__*/_jsx(View, {
|
|
195
195
|
style: [styles.root, style, styles.centered],
|
|
196
196
|
children: /*#__PURE__*/_jsx(Text, {
|
|
197
|
-
style: styles.permissionText,
|
|
197
|
+
style: [styles.permissionText, {
|
|
198
|
+
fontFamily
|
|
199
|
+
}],
|
|
198
200
|
children: "No front camera found"
|
|
199
201
|
})
|
|
200
202
|
});
|
|
@@ -220,13 +222,16 @@ export function LivenessCamera({
|
|
|
220
222
|
}), livenessState !== 'done' && /*#__PURE__*/_jsx(View, {
|
|
221
223
|
style: styles.feedbackContainer,
|
|
222
224
|
children: /*#__PURE__*/_jsx(Text, {
|
|
223
|
-
style: styles.feedbackText,
|
|
225
|
+
style: [styles.feedbackText, {
|
|
226
|
+
fontFamily
|
|
227
|
+
}],
|
|
224
228
|
children: feedback
|
|
225
229
|
})
|
|
226
230
|
}), livenessState === 'countdown' && countdown !== null && /*#__PURE__*/_jsx(View, {
|
|
227
231
|
style: styles.countdownContainer,
|
|
228
232
|
children: /*#__PURE__*/_jsx(CountdownBubble, {
|
|
229
|
-
value: countdown
|
|
233
|
+
value: countdown,
|
|
234
|
+
fontFamily: fontFamily
|
|
230
235
|
}, countdown)
|
|
231
236
|
}), livenessState === 'capturing' && /*#__PURE__*/_jsx(View, {
|
|
232
237
|
style: styles.captureFlash,
|
|
@@ -261,7 +266,6 @@ const styles = StyleSheet.create({
|
|
|
261
266
|
feedbackText: {
|
|
262
267
|
color: '#fff',
|
|
263
268
|
fontSize: 16,
|
|
264
|
-
fontWeight: '600',
|
|
265
269
|
textAlign: 'center',
|
|
266
270
|
textShadowColor: 'rgba(0,0,0,0.8)',
|
|
267
271
|
textShadowOffset: {
|
|
@@ -288,7 +292,6 @@ const styles = StyleSheet.create({
|
|
|
288
292
|
countdownText: {
|
|
289
293
|
color: '#fff',
|
|
290
294
|
fontSize: 52,
|
|
291
|
-
fontWeight: '700',
|
|
292
295
|
lineHeight: 60
|
|
293
296
|
},
|
|
294
297
|
captureFlash: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraFormat","useCameraPermission","Circle","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","CIRCLE_DIAMETER_RATIO","STROKE_WIDTH","K","getCircleColor","state","score","circlePath","cx","cy","r","join","CircleOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","format","fps","Math","min","maxFps","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraFormat","useCameraPermission","Circle","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","DEFAULT_FONT","CIRCLE_DIAMETER_RATIO","STROKE_WIDTH","K","getCircleColor","state","score","circlePath","cx","cy","r","join","CircleOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","fontFamily","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","format","fps","Math","min","maxFps","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Modal, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
4
|
+
import { Path, Svg } from 'react-native-svg';
|
|
5
|
+
import { LivenessCamera } from "./LivenessCamera.js";
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
/** × icon drawn with SVG — no external icon library required. */
|
|
8
|
+
function CloseIcon({
|
|
9
|
+
color = '#fff',
|
|
10
|
+
size = 18
|
|
11
|
+
}) {
|
|
12
|
+
const pad = size * 0.1;
|
|
13
|
+
const end = size - pad;
|
|
14
|
+
return /*#__PURE__*/_jsx(Svg, {
|
|
15
|
+
width: size,
|
|
16
|
+
height: size,
|
|
17
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
18
|
+
children: /*#__PURE__*/_jsx(Path, {
|
|
19
|
+
d: `M${pad} ${pad} L${end} ${end} M${end} ${pad} L${pad} ${end}`,
|
|
20
|
+
stroke: color,
|
|
21
|
+
strokeWidth: size * 0.14,
|
|
22
|
+
strokeLinecap: "round"
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Drop-in liveness detection modal.
|
|
29
|
+
*
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <LivenessCameraModal
|
|
32
|
+
* visible={showLiveness}
|
|
33
|
+
* onClose={() => setShowLiveness(false)}
|
|
34
|
+
* onCapture={(result) => console.log(result.photo.path)}
|
|
35
|
+
* animationType="slide"
|
|
36
|
+
* />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function LivenessCameraModal({
|
|
40
|
+
visible,
|
|
41
|
+
onClose,
|
|
42
|
+
animationType = 'slide',
|
|
43
|
+
closeButtonStyle,
|
|
44
|
+
closeButtonIconColor = '#fff',
|
|
45
|
+
closeButtonIconSize = 18,
|
|
46
|
+
// forward all LivenessCamera props
|
|
47
|
+
onCapture,
|
|
48
|
+
onLivenessConfirmed,
|
|
49
|
+
onError,
|
|
50
|
+
countdownFrom,
|
|
51
|
+
livenessThreshold,
|
|
52
|
+
confirmFrames,
|
|
53
|
+
soundEnabled,
|
|
54
|
+
fontFamily
|
|
55
|
+
}) {
|
|
56
|
+
const handleCapture = result => {
|
|
57
|
+
onCapture(result);
|
|
58
|
+
// Modal stays open — consumer decides when to close via onCapture callback
|
|
59
|
+
};
|
|
60
|
+
return /*#__PURE__*/_jsx(Modal, {
|
|
61
|
+
visible: visible,
|
|
62
|
+
animationType: animationType,
|
|
63
|
+
statusBarTranslucent: true,
|
|
64
|
+
onRequestClose: onClose,
|
|
65
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
66
|
+
style: styles.container,
|
|
67
|
+
children: [/*#__PURE__*/_jsx(LivenessCamera, {
|
|
68
|
+
style: styles.camera,
|
|
69
|
+
onCapture: handleCapture,
|
|
70
|
+
onLivenessConfirmed: onLivenessConfirmed,
|
|
71
|
+
onError: onError,
|
|
72
|
+
countdownFrom: countdownFrom,
|
|
73
|
+
livenessThreshold: livenessThreshold,
|
|
74
|
+
confirmFrames: confirmFrames,
|
|
75
|
+
soundEnabled: soundEnabled,
|
|
76
|
+
fontFamily: fontFamily
|
|
77
|
+
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
78
|
+
style: [styles.closeButton, closeButtonStyle],
|
|
79
|
+
onPress: onClose,
|
|
80
|
+
activeOpacity: 0.7,
|
|
81
|
+
hitSlop: {
|
|
82
|
+
top: 8,
|
|
83
|
+
bottom: 8,
|
|
84
|
+
left: 8,
|
|
85
|
+
right: 8
|
|
86
|
+
},
|
|
87
|
+
children: /*#__PURE__*/_jsx(CloseIcon, {
|
|
88
|
+
color: closeButtonIconColor,
|
|
89
|
+
size: closeButtonIconSize
|
|
90
|
+
})
|
|
91
|
+
})]
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const styles = StyleSheet.create({
|
|
96
|
+
container: {
|
|
97
|
+
flex: 1,
|
|
98
|
+
backgroundColor: '#000'
|
|
99
|
+
},
|
|
100
|
+
camera: {
|
|
101
|
+
flex: 1
|
|
102
|
+
},
|
|
103
|
+
closeButton: {
|
|
104
|
+
position: 'absolute',
|
|
105
|
+
top: 52,
|
|
106
|
+
left: 16,
|
|
107
|
+
width: 48,
|
|
108
|
+
height: 48,
|
|
109
|
+
borderRadius: 24,
|
|
110
|
+
justifyContent: 'center',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
113
|
+
borderWidth: 1,
|
|
114
|
+
borderColor: 'rgba(255,255,255,0.3)'
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=LivenessCameraModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Modal","StyleSheet","TouchableOpacity","View","Path","Svg","LivenessCamera","jsx","_jsx","jsxs","_jsxs","CloseIcon","color","size","pad","end","width","height","viewBox","children","d","stroke","strokeWidth","strokeLinecap","LivenessCameraModal","visible","onClose","animationType","closeButtonStyle","closeButtonIconColor","closeButtonIconSize","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","fontFamily","handleCapture","result","statusBarTranslucent","onRequestClose","style","styles","container","camera","closeButton","onPress","activeOpacity","hitSlop","top","bottom","left","right","create","flex","backgroundColor","position","borderRadius","justifyContent","alignItems","borderWidth","borderColor"],"sourceRoot":"../../src","sources":["LivenessCameraModal.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["LivenessCamera","useLivenessCamera"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,qBAAkB;AACjD,SAASC,iBAAiB,QAAQ,wBAAqB","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["LivenessCamera","LivenessCameraModal","useLivenessCamera"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,qBAAkB;AACjD,SAASC,mBAAmB,QAAQ,0BAAuB;AAC3D,SAASC,iBAAiB,QAAQ,wBAAqB","ignoreList":[]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { LivenessCameraProps } from './types';
|
|
2
|
-
export declare function LivenessCamera({ onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, style, }: LivenessCameraProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function LivenessCamera({ onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, fontFamily, style, }: LivenessCameraProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=LivenessCamera.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAuIlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,UAAyB,EACzB,KAAK,GACN,EAAE,mBAAmB,2CA8FrB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LivenessCameraModalProps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Drop-in liveness detection modal.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <LivenessCameraModal
|
|
7
|
+
* visible={showLiveness}
|
|
8
|
+
* onClose={() => setShowLiveness(false)}
|
|
9
|
+
* onCapture={(result) => console.log(result.photo.path)}
|
|
10
|
+
* animationType="slide"
|
|
11
|
+
* />
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function LivenessCameraModal({ visible, onClose, animationType, closeButtonStyle, closeButtonIconColor, closeButtonIconSize, onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, fontFamily, }: LivenessCameraModalProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=LivenessCameraModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LivenessCameraModal.d.ts","sourceRoot":"","sources":["../../../src/LivenessCameraModal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAwBxD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,EACP,aAAuB,EACvB,gBAAgB,EAChB,oBAA6B,EAC7B,mBAAwB,EAExB,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,UAAU,GACX,EAAE,wBAAwB,2CAqC1B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { LivenessCamera } from './LivenessCamera';
|
|
2
|
+
export { LivenessCameraModal } from './LivenessCameraModal';
|
|
2
3
|
export { useLivenessCamera } from './useLivenessCamera';
|
|
3
|
-
export type { CaptureResult, FaceData, FeedbackMessage, LivenessCameraProps, LivenessState, } from './types';
|
|
4
|
+
export type { CaptureResult, FaceData, FeedbackMessage, LivenessCameraModalProps, LivenessCameraProps, LivenessState, } from './types';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,QAAQ,EACR,eAAe,EACf,wBAAwB,EACxB,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ViewStyle } from 'react-native';
|
|
1
|
+
import type { ModalProps, ViewStyle } from 'react-native';
|
|
2
2
|
import type { PhotoFile } from 'react-native-vision-camera';
|
|
3
3
|
export type FaceData = {
|
|
4
4
|
detected: boolean;
|
|
@@ -53,9 +53,41 @@ export type LivenessCameraProps = {
|
|
|
53
53
|
*/
|
|
54
54
|
style?: ViewStyle;
|
|
55
55
|
/**
|
|
56
|
-
* Whether to play a shutter sound on capture.
|
|
57
|
-
* to be installed. Defaults to true.
|
|
56
|
+
* Whether to play a shutter sound on capture. Defaults to true.
|
|
58
57
|
*/
|
|
59
58
|
soundEnabled?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Font family applied to all text inside the component.
|
|
61
|
+
* Defaults to 'Baloo-Medium'. Set to undefined to use the system font.
|
|
62
|
+
*/
|
|
63
|
+
fontFamily?: string;
|
|
64
|
+
};
|
|
65
|
+
export type LivenessCameraModalProps = Omit<LivenessCameraProps, 'style'> & {
|
|
66
|
+
/**
|
|
67
|
+
* Controls modal visibility — pass your own boolean state.
|
|
68
|
+
*/
|
|
69
|
+
visible: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Called when the close button is pressed or the Android back button fires.
|
|
72
|
+
* Use this to set your visible state to false.
|
|
73
|
+
*/
|
|
74
|
+
onClose: () => void;
|
|
75
|
+
/**
|
|
76
|
+
* Modal entrance/exit animation. Defaults to 'slide'.
|
|
77
|
+
*/
|
|
78
|
+
animationType?: ModalProps['animationType'];
|
|
79
|
+
/**
|
|
80
|
+
* Override styles on the close button container.
|
|
81
|
+
* Useful for adjusting position or size to match your design system.
|
|
82
|
+
*/
|
|
83
|
+
closeButtonStyle?: ViewStyle;
|
|
84
|
+
/**
|
|
85
|
+
* Colour of the × icon inside the close button. Defaults to '#fff'.
|
|
86
|
+
*/
|
|
87
|
+
closeButtonIconColor?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Size of the × icon in dp. Defaults to 18.
|
|
90
|
+
*/
|
|
91
|
+
closeButtonIconSize?: number;
|
|
60
92
|
};
|
|
61
93
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,kCAAkC,GAClC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG;IAC1E;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IAE5C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rick427/react-native-liveness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Liveness detection library for React Native using Vision Camera v4 and ML Kit",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
package/src/LivenessCamera.tsx
CHANGED
|
@@ -10,6 +10,8 @@ import { Circle, Path, Svg } from 'react-native-svg';
|
|
|
10
10
|
import { useLivenessCamera } from './useLivenessCamera';
|
|
11
11
|
import type { LivenessCameraProps, LivenessState } from './types';
|
|
12
12
|
|
|
13
|
+
const DEFAULT_FONT = 'Baloo-Medium';
|
|
14
|
+
|
|
13
15
|
// Circle diameter = 82 % of container width — large enough to fit any face
|
|
14
16
|
// comfortably without the user needing to fiddle with distance.
|
|
15
17
|
const CIRCLE_DIAMETER_RATIO = 0.82;
|
|
@@ -69,13 +71,10 @@ function CircleOverlay({
|
|
|
69
71
|
if (width === 0 || height === 0) return null;
|
|
70
72
|
|
|
71
73
|
const cx = width / 2;
|
|
72
|
-
// Centre the circle slightly above midpoint so the face sits naturally
|
|
73
74
|
const cy = height * 0.42;
|
|
74
75
|
const r = (width * CIRCLE_DIAMETER_RATIO) / 2;
|
|
75
76
|
const color = getCircleColor(state, score);
|
|
76
77
|
|
|
77
|
-
// Compound path: full-screen rect + circle cutout.
|
|
78
|
-
// evenodd fill rule makes the circle area transparent.
|
|
79
78
|
const scrimD = `M0 0H${width}V${height}H0Z ${circlePath(cx, cy, r)}`;
|
|
80
79
|
|
|
81
80
|
return (
|
|
@@ -93,9 +92,13 @@ function CircleOverlay({
|
|
|
93
92
|
);
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
function CountdownBubble({
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
function CountdownBubble({
|
|
96
|
+
value,
|
|
97
|
+
fontFamily,
|
|
98
|
+
}: {
|
|
99
|
+
value: number;
|
|
100
|
+
fontFamily: string;
|
|
101
|
+
}) {
|
|
99
102
|
const scale = useRef(new Animated.Value(0)).current;
|
|
100
103
|
const opacity = useRef(new Animated.Value(0)).current;
|
|
101
104
|
|
|
@@ -135,7 +138,7 @@ function CountdownBubble({ value }: { value: number }) {
|
|
|
135
138
|
<Animated.View
|
|
136
139
|
style={[styles.countdownBubble, { opacity, transform: [{ scale }] }]}
|
|
137
140
|
>
|
|
138
|
-
<Text style={styles.countdownText}>{value}</Text>
|
|
141
|
+
<Text style={[styles.countdownText, { fontFamily }]}>{value}</Text>
|
|
139
142
|
</Animated.View>
|
|
140
143
|
);
|
|
141
144
|
}
|
|
@@ -148,12 +151,11 @@ export function LivenessCamera({
|
|
|
148
151
|
livenessThreshold = 0.75,
|
|
149
152
|
confirmFrames = 10,
|
|
150
153
|
soundEnabled = true,
|
|
154
|
+
fontFamily = DEFAULT_FONT,
|
|
151
155
|
style,
|
|
152
156
|
}: LivenessCameraProps) {
|
|
153
157
|
const { hasPermission, requestPermission } = useCameraPermission();
|
|
154
158
|
const device = useCameraDevice('front');
|
|
155
|
-
// Pick the best format that supports up to 60 fps. Falls back gracefully
|
|
156
|
-
// to whatever the device offers if 60 fps isn't available.
|
|
157
159
|
const format = useCameraFormat(device, [{ fps: 60 }]);
|
|
158
160
|
const fps = Math.min(format?.maxFps ?? 30, 60);
|
|
159
161
|
const cameraRef = useRef<Camera>(null);
|
|
@@ -190,7 +192,9 @@ export function LivenessCamera({
|
|
|
190
192
|
if (!hasPermission) {
|
|
191
193
|
return (
|
|
192
194
|
<View style={[styles.root, style, styles.centered]}>
|
|
193
|
-
<Text style={styles.permissionText
|
|
195
|
+
<Text style={[styles.permissionText, { fontFamily }]}>
|
|
196
|
+
Camera permission required
|
|
197
|
+
</Text>
|
|
194
198
|
</View>
|
|
195
199
|
);
|
|
196
200
|
}
|
|
@@ -198,7 +202,9 @@ export function LivenessCamera({
|
|
|
198
202
|
if (!device) {
|
|
199
203
|
return (
|
|
200
204
|
<View style={[styles.root, style, styles.centered]}>
|
|
201
|
-
<Text style={styles.permissionText
|
|
205
|
+
<Text style={[styles.permissionText, { fontFamily }]}>
|
|
206
|
+
No front camera found
|
|
207
|
+
</Text>
|
|
202
208
|
</View>
|
|
203
209
|
);
|
|
204
210
|
}
|
|
@@ -224,12 +230,16 @@ export function LivenessCamera({
|
|
|
224
230
|
/>
|
|
225
231
|
{livenessState !== 'done' && (
|
|
226
232
|
<View style={styles.feedbackContainer}>
|
|
227
|
-
<Text style={styles.feedbackText}>{feedback}</Text>
|
|
233
|
+
<Text style={[styles.feedbackText, { fontFamily }]}>{feedback}</Text>
|
|
228
234
|
</View>
|
|
229
235
|
)}
|
|
230
236
|
{livenessState === 'countdown' && countdown !== null && (
|
|
231
237
|
<View style={styles.countdownContainer}>
|
|
232
|
-
<CountdownBubble
|
|
238
|
+
<CountdownBubble
|
|
239
|
+
key={countdown}
|
|
240
|
+
value={countdown}
|
|
241
|
+
fontFamily={fontFamily}
|
|
242
|
+
/>
|
|
233
243
|
</View>
|
|
234
244
|
)}
|
|
235
245
|
{livenessState === 'capturing' && (
|
|
@@ -266,7 +276,6 @@ const styles = StyleSheet.create({
|
|
|
266
276
|
feedbackText: {
|
|
267
277
|
color: '#fff',
|
|
268
278
|
fontSize: 16,
|
|
269
|
-
fontWeight: '600',
|
|
270
279
|
textAlign: 'center',
|
|
271
280
|
textShadowColor: 'rgba(0,0,0,0.8)',
|
|
272
281
|
textShadowOffset: { width: 0, height: 1 },
|
|
@@ -290,7 +299,6 @@ const styles = StyleSheet.create({
|
|
|
290
299
|
countdownText: {
|
|
291
300
|
color: '#fff',
|
|
292
301
|
fontSize: 52,
|
|
293
|
-
fontWeight: '700',
|
|
294
302
|
lineHeight: 60,
|
|
295
303
|
},
|
|
296
304
|
captureFlash: {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Modal, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
2
|
+
import { Path, Svg } from 'react-native-svg';
|
|
3
|
+
import { LivenessCamera } from './LivenessCamera';
|
|
4
|
+
import type { LivenessCameraModalProps } from './types';
|
|
5
|
+
|
|
6
|
+
/** × icon drawn with SVG — no external icon library required. */
|
|
7
|
+
function CloseIcon({
|
|
8
|
+
color = '#fff',
|
|
9
|
+
size = 18,
|
|
10
|
+
}: {
|
|
11
|
+
color?: string;
|
|
12
|
+
size?: number;
|
|
13
|
+
}) {
|
|
14
|
+
const pad = size * 0.1;
|
|
15
|
+
const end = size - pad;
|
|
16
|
+
return (
|
|
17
|
+
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
|
18
|
+
<Path
|
|
19
|
+
d={`M${pad} ${pad} L${end} ${end} M${end} ${pad} L${pad} ${end}`}
|
|
20
|
+
stroke={color}
|
|
21
|
+
strokeWidth={size * 0.14}
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
/>
|
|
24
|
+
</Svg>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Drop-in liveness detection modal.
|
|
30
|
+
*
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <LivenessCameraModal
|
|
33
|
+
* visible={showLiveness}
|
|
34
|
+
* onClose={() => setShowLiveness(false)}
|
|
35
|
+
* onCapture={(result) => console.log(result.photo.path)}
|
|
36
|
+
* animationType="slide"
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function LivenessCameraModal({
|
|
41
|
+
visible,
|
|
42
|
+
onClose,
|
|
43
|
+
animationType = 'slide',
|
|
44
|
+
closeButtonStyle,
|
|
45
|
+
closeButtonIconColor = '#fff',
|
|
46
|
+
closeButtonIconSize = 18,
|
|
47
|
+
// forward all LivenessCamera props
|
|
48
|
+
onCapture,
|
|
49
|
+
onLivenessConfirmed,
|
|
50
|
+
onError,
|
|
51
|
+
countdownFrom,
|
|
52
|
+
livenessThreshold,
|
|
53
|
+
confirmFrames,
|
|
54
|
+
soundEnabled,
|
|
55
|
+
fontFamily,
|
|
56
|
+
}: LivenessCameraModalProps) {
|
|
57
|
+
const handleCapture: LivenessCameraModalProps['onCapture'] = (result) => {
|
|
58
|
+
onCapture(result);
|
|
59
|
+
// Modal stays open — consumer decides when to close via onCapture callback
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Modal
|
|
64
|
+
visible={visible}
|
|
65
|
+
animationType={animationType}
|
|
66
|
+
statusBarTranslucent
|
|
67
|
+
onRequestClose={onClose}
|
|
68
|
+
>
|
|
69
|
+
<View style={styles.container}>
|
|
70
|
+
<LivenessCamera
|
|
71
|
+
style={styles.camera}
|
|
72
|
+
onCapture={handleCapture}
|
|
73
|
+
onLivenessConfirmed={onLivenessConfirmed}
|
|
74
|
+
onError={onError}
|
|
75
|
+
countdownFrom={countdownFrom}
|
|
76
|
+
livenessThreshold={livenessThreshold}
|
|
77
|
+
confirmFrames={confirmFrames}
|
|
78
|
+
soundEnabled={soundEnabled}
|
|
79
|
+
fontFamily={fontFamily}
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
<TouchableOpacity
|
|
83
|
+
style={[styles.closeButton, closeButtonStyle]}
|
|
84
|
+
onPress={onClose}
|
|
85
|
+
activeOpacity={0.7}
|
|
86
|
+
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
87
|
+
>
|
|
88
|
+
<CloseIcon color={closeButtonIconColor} size={closeButtonIconSize} />
|
|
89
|
+
</TouchableOpacity>
|
|
90
|
+
</View>
|
|
91
|
+
</Modal>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const styles = StyleSheet.create({
|
|
96
|
+
container: {
|
|
97
|
+
flex: 1,
|
|
98
|
+
backgroundColor: '#000',
|
|
99
|
+
},
|
|
100
|
+
camera: {
|
|
101
|
+
flex: 1,
|
|
102
|
+
},
|
|
103
|
+
closeButton: {
|
|
104
|
+
position: 'absolute',
|
|
105
|
+
top: 52,
|
|
106
|
+
left: 16,
|
|
107
|
+
width: 48,
|
|
108
|
+
height: 48,
|
|
109
|
+
borderRadius: 24,
|
|
110
|
+
justifyContent: 'center',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
113
|
+
borderWidth: 1,
|
|
114
|
+
borderColor: 'rgba(255,255,255,0.3)',
|
|
115
|
+
},
|
|
116
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export { LivenessCamera } from './LivenessCamera';
|
|
2
|
+
export { LivenessCameraModal } from './LivenessCameraModal';
|
|
2
3
|
export { useLivenessCamera } from './useLivenessCamera';
|
|
3
4
|
export type {
|
|
4
5
|
CaptureResult,
|
|
5
6
|
FaceData,
|
|
6
7
|
FeedbackMessage,
|
|
8
|
+
LivenessCameraModalProps,
|
|
7
9
|
LivenessCameraProps,
|
|
8
10
|
LivenessState,
|
|
9
11
|
} from './types';
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ViewStyle } from 'react-native';
|
|
1
|
+
import type { ModalProps, ViewStyle } from 'react-native';
|
|
2
2
|
import type { PhotoFile } from 'react-native-vision-camera';
|
|
3
3
|
|
|
4
4
|
export type FaceData = {
|
|
@@ -81,8 +81,47 @@ export type LivenessCameraProps = {
|
|
|
81
81
|
style?: ViewStyle;
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* Whether to play a shutter sound on capture.
|
|
85
|
-
* to be installed. Defaults to true.
|
|
84
|
+
* Whether to play a shutter sound on capture. Defaults to true.
|
|
86
85
|
*/
|
|
87
86
|
soundEnabled?: boolean;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Font family applied to all text inside the component.
|
|
90
|
+
* Defaults to 'Baloo-Medium'. Set to undefined to use the system font.
|
|
91
|
+
*/
|
|
92
|
+
fontFamily?: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type LivenessCameraModalProps = Omit<LivenessCameraProps, 'style'> & {
|
|
96
|
+
/**
|
|
97
|
+
* Controls modal visibility — pass your own boolean state.
|
|
98
|
+
*/
|
|
99
|
+
visible: boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Called when the close button is pressed or the Android back button fires.
|
|
103
|
+
* Use this to set your visible state to false.
|
|
104
|
+
*/
|
|
105
|
+
onClose: () => void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Modal entrance/exit animation. Defaults to 'slide'.
|
|
109
|
+
*/
|
|
110
|
+
animationType?: ModalProps['animationType'];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Override styles on the close button container.
|
|
114
|
+
* Useful for adjusting position or size to match your design system.
|
|
115
|
+
*/
|
|
116
|
+
closeButtonStyle?: ViewStyle;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Colour of the × icon inside the close button. Defaults to '#fff'.
|
|
120
|
+
*/
|
|
121
|
+
closeButtonIconColor?: string;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Size of the × icon in dp. Defaults to 18.
|
|
125
|
+
*/
|
|
126
|
+
closeButtonIconSize?: number;
|
|
88
127
|
};
|