@@ -42,6 +42,7 @@ import {
4242 Skia ,
4343 SkImage ,
4444} from '@shopify/react-native-skia' ;
45+ import Svg , { Path , Polygon } from 'react-native-svg' ;
4546import { GeneratingContext } from '../../context' ;
4647import Spinner from '../../components/Spinner' ;
4748import ColorPalette from '../../colors' ;
@@ -119,6 +120,9 @@ export default function VisionCameraScreen() {
119120 const [ activeTask , setActiveTask ] = useState < TaskId > ( 'classification' ) ;
120121 const [ activeModel , setActiveModel ] = useState < ModelId > ( 'classification' ) ;
121122 const [ canvasSize , setCanvasSize ] = useState ( { width : 1 , height : 1 } ) ;
123+ const [ cameraPosition , setCameraPosition ] = useState < 'back' | 'front' > (
124+ 'back'
125+ ) ;
122126 const { setGlobalGenerating } = useContext ( GeneratingContext ) ;
123127
124128 const classification = useClassification ( {
@@ -149,7 +153,8 @@ export default function VisionCameraScreen() {
149153 const lastFrameTimeRef = useRef ( Date . now ( ) ) ;
150154 const cameraPermission = useCameraPermission ( ) ;
151155 const devices = useCameraDevices ( ) ;
152- const device = devices . find ( ( d ) => d . position === 'back' ) ?? devices [ 0 ] ;
156+ const device =
157+ devices . find ( ( d ) => d . position === cameraPosition ) ?? devices [ 0 ] ;
153158 const format = useMemo ( ( ) => {
154159 if ( device == null ) return undefined ;
155160 try {
@@ -375,7 +380,10 @@ export default function VisionCameraScreen() {
375380 />
376381
377382 < View
378- style = { StyleSheet . absoluteFill }
383+ style = { [
384+ StyleSheet . absoluteFill ,
385+ cameraPosition === 'front' && { transform : [ { scaleX : - 1 } ] } ,
386+ ] }
379387 pointerEvents = "none"
380388 onLayout = { ( e ) =>
381389 setCanvasSize ( {
@@ -422,6 +430,9 @@ export default function VisionCameraScreen() {
422430 style = { [
423431 styles . bboxLabel ,
424432 { backgroundColor : labelColorBg ( det . label ) } ,
433+ cameraPosition === 'front' && {
434+ transform : [ { scaleX : - 1 } ] ,
435+ } ,
425436 ] }
426437 >
427438 < Text style = { styles . bboxLabelText } >
@@ -518,6 +529,37 @@ export default function VisionCameraScreen() {
518529 ) ) }
519530 </ ScrollView >
520531 </ View >
532+
533+ < View
534+ style = { [ styles . bottomOverlay , { paddingBottom : insets . bottom + 16 } ] }
535+ pointerEvents = "box-none"
536+ >
537+ < TouchableOpacity
538+ style = { styles . flipButton }
539+ onPress = { ( ) =>
540+ setCameraPosition ( ( p ) => ( p === 'back' ? 'front' : 'back' ) )
541+ }
542+ >
543+ < Svg width = { 28 } height = { 28 } viewBox = "0 0 24 24" fill = "none" >
544+ { /* Camera body */ }
545+ < Path
546+ d = "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"
547+ stroke = "white"
548+ strokeWidth = { 1.8 }
549+ strokeLinecap = "round"
550+ strokeLinejoin = "round"
551+ />
552+ { /* Rotate arrows — arc with arrowhead around the lens */ }
553+ < Path
554+ d = "M9 13.5a3 3 0 1 0 3-3"
555+ stroke = "white"
556+ strokeWidth = { 1.8 }
557+ strokeLinecap = "round"
558+ />
559+ < Polygon points = "8,11 9,13.5 11,12" fill = "white" />
560+ </ Svg >
561+ </ TouchableOpacity >
562+ </ View >
521563 </ View >
522564 ) ;
523565}
@@ -662,4 +704,21 @@ const styles = StyleSheet.create({
662704 textShadowOffset : { width : 0 , height : 1 } ,
663705 textShadowRadius : 6 ,
664706 } ,
707+ bottomOverlay : {
708+ position : 'absolute' ,
709+ bottom : 0 ,
710+ left : 0 ,
711+ right : 0 ,
712+ alignItems : 'center' ,
713+ } ,
714+ flipButton : {
715+ width : 56 ,
716+ height : 56 ,
717+ borderRadius : 28 ,
718+ backgroundColor : 'rgba(255,255,255,0.2)' ,
719+ justifyContent : 'center' ,
720+ alignItems : 'center' ,
721+ borderWidth : 1.5 ,
722+ borderColor : 'rgba(255,255,255,0.4)' ,
723+ } ,
665724} ) ;
0 commit comments