From f839bc23accb0e4f0e9538701fd49af347d2e013 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Tue, 11 May 2021 12:59:05 +0200 Subject: [PATCH] chore: Cleanup codebase (#137) * Remove `useCachedState` * Add pressable opacity * Update Media.tsx * f * Update FormatFilter.ts * update * App -> CameraPage, Media -> MediaPage * Update CameraPage.tsx * Create 60 FPS switch * Update CameraPage.tsx --- example/index.js | 24 ++--- example/package.json | 2 +- example/src/{App.tsx => CameraPage.tsx} | 72 +++++++-------- example/src/{Media.tsx => MediaPage.tsx} | 10 +-- example/src/Settings.tsx | 110 ----------------------- example/src/hooks/useCachedState.ts | 26 ------ example/src/hooks/useIsForeground.ts | 4 +- example/src/hooks/useIsScreenFocused.ts | 58 +++++------- example/src/state/atoms.ts | 11 --- example/src/state/selectors.ts | 13 --- example/src/views/PressableOpacity.tsx | 50 ----------- example/yarn.lock | 10 +-- src/utils/FormatFilter.ts | 18 ++-- 13 files changed, 86 insertions(+), 322 deletions(-) rename example/src/{App.tsx => CameraPage.tsx} (85%) rename example/src/{Media.tsx => MediaPage.tsx} (94%) delete mode 100644 example/src/Settings.tsx delete mode 100644 example/src/hooks/useCachedState.ts delete mode 100644 example/src/state/atoms.ts delete mode 100644 example/src/state/selectors.ts delete mode 100644 example/src/views/PressableOpacity.tsx diff --git a/example/index.js b/example/index.js index fb47f9d..eb6dfce 100644 --- a/example/index.js +++ b/example/index.js @@ -1,10 +1,9 @@ import 'react-native-gesture-handler'; import { Navigation } from 'react-native-navigation'; import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; -import { App } from './src/App'; -import { Settings } from './src/Settings'; +import { CameraPage } from './src/CameraPage'; import { Splash } from './src/Splash'; -import { Media } from './src/Media'; +import { MediaPage } from './src/MediaPage'; import { Camera } from 'react-native-vision-camera'; Navigation.setDefaultOptions({ @@ -42,19 +41,14 @@ Navigation.registerComponent( () => Splash, ); Navigation.registerComponent( - 'Home', - () => gestureHandlerRootHOC(App), - () => App, + 'CameraPage', + () => gestureHandlerRootHOC(CameraPage), + () => CameraPage, ); Navigation.registerComponent( - 'Media', - () => gestureHandlerRootHOC(Media), - () => Media, -); -Navigation.registerComponent( - 'Settings', - () => gestureHandlerRootHOC(Settings), - () => Settings, + 'MediaPage', + () => gestureHandlerRootHOC(MediaPage), + () => MediaPage, ); Navigation.events().registerNavigationButtonPressedListener((event) => { @@ -67,7 +61,7 @@ Navigation.events().registerAppLaunchedListener(async () => { Camera.getMicrophonePermissionStatus(), ]); let rootName = 'Splash'; - if (cameraPermission === 'authorized' && microphonePermission === 'authorized') rootName = 'Home'; + if (cameraPermission === 'authorized' && microphonePermission === 'authorized') rootName = 'CameraPage'; Navigation.setRoot({ root: { diff --git a/example/package.json b/example/package.json index a436819..042724a 100644 --- a/example/package.json +++ b/example/package.json @@ -14,11 +14,11 @@ "@react-native-community/blur": "^3.6.0", "@react-native-community/cameraroll": "^4.0.2", "@react-native-community/slider": "^3.0.3", - "pipestate": "^1.0.2", "react": "17.0.2", "react-native": "0.64", "react-native-gesture-handler": "^1.10.3", "react-native-navigation": "7.8.4-snapshot.1439", + "react-native-pressable-opacity": "^1.0.4", "react-native-reanimated": "^2.1.0", "react-native-static-safe-area-insets": "^2.1.1", "react-native-vector-icons": "^8.0.0", diff --git a/example/src/App.tsx b/example/src/CameraPage.tsx similarity index 85% rename from example/src/App.tsx rename to example/src/CameraPage.tsx index 8040990..36b649e 100644 --- a/example/src/App.tsx +++ b/example/src/CameraPage.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useRef, useState, useMemo, useCallback } from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet, Text, View } from 'react-native'; import { PinchGestureHandler, PinchGestureHandlerGestureEvent, @@ -11,18 +11,16 @@ import { import { Navigation, NavigationFunctionComponent } from 'react-native-navigation'; import type { CameraDeviceFormat, CameraRuntimeError, PhotoFile, VideoFile } from 'react-native-vision-camera'; import { Camera, frameRateIncluded, sortFormatsByResolution, filterFormatsByAspectRatio } from 'react-native-vision-camera'; -import { useIsScreenFocused } from './hooks/useIsScreenFocused'; +import { useIsScreenFocussed } from './hooks/useIsScreenFocused'; import { CONTENT_SPACING, MAX_ZOOM_FACTOR, SAFE_AREA_PADDING } from './Constants'; import Reanimated, { Extrapolate, interpolate, useAnimatedGestureHandler, useAnimatedProps, useSharedValue } from 'react-native-reanimated'; import { useEffect } from 'react'; import { useIsForeground } from './hooks/useIsForeground'; import { StatusBarBlurBackground } from './views/StatusBarBlurBackground'; import { CaptureButton } from './views/CaptureButton'; -import { PressableOpacity } from './views/PressableOpacity'; +import { PressableOpacity } from 'react-native-pressable-opacity'; import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons'; import IonIcon from 'react-native-vector-icons/Ionicons'; -import { useSelector } from 'pipestate'; -import { FpsSelector } from './state/selectors'; import { useCameraDevice } from './hooks/useCameraDevice'; const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera); @@ -33,14 +31,14 @@ Reanimated.addWhitelistedNativeProps({ const SCALE_FULL_ZOOM = 3; const BUTTON_SIZE = 40; -export const App: NavigationFunctionComponent = ({ componentId }) => { +export const CameraPage: NavigationFunctionComponent = ({ componentId }) => { const camera = useRef(null); const [isCameraInitialized, setIsCameraInitialized] = useState(false); const zoom = useSharedValue(0); const isPressingButton = useSharedValue(false); // check if camera page is active - const isFocussed = useIsScreenFocused(componentId); + const isFocussed = useIsScreenFocussed(componentId); const isForeground = useIsForeground(); const isActive = isFocussed && isForeground; @@ -59,32 +57,34 @@ export const App: NavigationFunctionComponent = ({ componentId }) => { }, [device?.formats]); //#region Memos - const [targetFps] = useSelector(FpsSelector); - console.log(`Target FPS: ${targetFps}`); + const [is60Fps, setIs60Fps] = useState(true); const fps = useMemo(() => { + if (!is60Fps) return 30; + if (enableNightMode && !device?.supportsLowLightBoost) { // User has enabled Night Mode, but Night Mode is not natively supported, so we simulate it by lowering the frame rate. return 30; } - const supportsHdrAtHighFps = formats.some((f) => f.supportsVideoHDR && f.frameRateRanges.some((r) => frameRateIncluded(r, targetFps))); - if (enableHdr && !supportsHdrAtHighFps) { - // User has enabled HDR, but HDR is not supported at targetFps. + const supportsHdrAt60Fps = formats.some((f) => f.supportsVideoHDR && f.frameRateRanges.some((r) => frameRateIncluded(r, 60))); + if (enableHdr && !supportsHdrAt60Fps) { + // User has enabled HDR, but HDR is not supported at 60 FPS. return 30; } - const supportsHighFps = formats.some((f) => f.frameRateRanges.some((r) => frameRateIncluded(r, targetFps))); - if (!supportsHighFps) { - // targetFps is not supported by any format. + const supports60Fps = formats.some((f) => f.frameRateRanges.some((r) => frameRateIncluded(r, 60))); + if (!supports60Fps) { + // 60 FPS is not supported by any format. return 30; } - // If nothing blocks us from using it, we default to targetFps. - return targetFps; - }, [device?.supportsLowLightBoost, enableHdr, enableNightMode, formats, targetFps]); + // If nothing blocks us from using it, we default to 60 FPS. + return 60; + }, [device?.supportsLowLightBoost, enableHdr, enableNightMode, formats, is60Fps]); const supportsCameraFlipping = useMemo(() => devices.back != null && devices.front != null, [devices.back, devices.front]); const supportsFlash = device?.hasFlash ?? false; const supportsHdr = useMemo(() => formats.some((f) => f.supportsVideoHDR), [formats]); + const supports60Fps = useMemo(() => formats.some((f) => f.frameRateRanges.some((rate) => frameRateIncluded(rate, 60))), [formats]); const canToggleNightMode = enableNightMode ? true // it's enabled so you have to be able to turn it off again : (device?.supportsLowLightBoost ?? false) || fps > 30; // either we have native support, or we can lower the FPS @@ -136,7 +136,7 @@ export const App: NavigationFunctionComponent = ({ componentId }) => { console.log(`Media captured! ${JSON.stringify(media)}`); await Navigation.showModal({ component: { - name: 'Media', + name: 'MediaPage', passProps: { type: type, path: media.path, @@ -147,18 +147,9 @@ export const App: NavigationFunctionComponent = ({ componentId }) => { const onFlipCameraPressed = useCallback(() => { setCameraPosition((p) => (p === 'back' ? 'front' : 'back')); }, []); - const onHdrSwitchPressed = useCallback(() => { - setEnableHdr((h) => !h); - }, []); const onFlashPressed = useCallback(() => { setFlash((f) => (f === 'off' ? 'on' : 'off')); }, []); - const onNightModePressed = useCallback(() => { - setEnableNightMode((n) => !n); - }, []); - const onSettingsPressed = useCallback(() => { - Navigation.push(componentId, { component: { name: 'Settings' } }); - }, [componentId]); //#endregion //#region Tap Gesture @@ -269,19 +260,24 @@ export const App: NavigationFunctionComponent = ({ componentId }) => { )} - {canToggleNightMode && ( - - + {supports60Fps && ( + setIs60Fps(!is60Fps)}> + + {is60Fps ? '60' : '30'} + {'\n'}FPS + )} {supportsHdr && ( - + setEnableHdr((h) => !h)}> )} - - - + {canToggleNightMode && ( + setEnableNightMode(!enableNightMode)} disabledOpacity={0.4}> + + + )} ); @@ -311,4 +307,10 @@ const styles = StyleSheet.create({ right: SAFE_AREA_PADDING.paddingRight, top: SAFE_AREA_PADDING.paddingTop, }, + text: { + color: 'white', + fontSize: 11, + fontWeight: 'bold', + textAlign: 'center', + }, }); diff --git a/example/src/Media.tsx b/example/src/MediaPage.tsx similarity index 94% rename from example/src/Media.tsx rename to example/src/MediaPage.tsx index ed4978f..17f0cf5 100644 --- a/example/src/Media.tsx +++ b/example/src/MediaPage.tsx @@ -4,8 +4,8 @@ import { Navigation, NavigationFunctionComponent, OptionsModalPresentationStyle import Video, { LoadError, OnLoadData } from 'react-native-video'; import { SAFE_AREA_PADDING } from './Constants'; import { useIsForeground } from './hooks/useIsForeground'; -import { useIsScreenFocused } from './hooks/useIsScreenFocused'; -import { PressableOpacity } from './views/PressableOpacity'; +import { useIsScreenFocussed } from './hooks/useIsScreenFocused'; +import { PressableOpacity } from 'react-native-pressable-opacity'; import IonIcon from 'react-native-vector-icons/Ionicons'; import { Alert } from 'react-native'; import CameraRoll from '@react-native-community/cameraroll'; @@ -34,10 +34,10 @@ const requestSavePermission = async (): Promise => { const isVideoOnLoadEvent = (event: OnLoadData | NativeSyntheticEvent): event is OnLoadData => 'duration' in event && 'naturalSize' in event; -export const Media: NavigationFunctionComponent = ({ componentId, type, path }) => { +export const MediaPage: NavigationFunctionComponent = ({ componentId, type, path }) => { const [hasMediaLoaded, setHasMediaLoaded] = useState(false); const isForeground = useIsForeground(); - const isScreenFocused = useIsScreenFocused(componentId); + const isScreenFocused = useIsScreenFocussed(componentId); const isVideoPaused = !isForeground || !isScreenFocused; const [savingState, setSavingState] = useState<'none' | 'saving' | 'saved'>('none'); @@ -126,7 +126,7 @@ export const Media: NavigationFunctionComponent = ({ componentId, ty ); }; -Media.options = { +MediaPage.options = { modal: { swipeToDismiss: false, }, diff --git a/example/src/Settings.tsx b/example/src/Settings.tsx deleted file mode 100644 index 3c07d52..0000000 --- a/example/src/Settings.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, { useCallback } from 'react'; -import { StyleSheet, View, Text, Linking } from 'react-native'; -import { Navigation, NavigationFunctionComponent } from 'react-native-navigation'; -import Slider from '@react-native-community/slider'; -import { useState } from 'react'; -import { useEffect } from 'react'; -import { Camera } from 'react-native-vision-camera'; -import { SAFE_AREA_PADDING } from './Constants'; -import { useSelector } from 'pipestate'; -import { FpsSelector } from './state/selectors'; - -export const Settings: NavigationFunctionComponent = ({ componentId }) => { - const [fpsSelector, setFpsSelector] = useSelector(FpsSelector); - const [fps, setFps] = useState(fpsSelector); - - const [minFps, setMinFps] = useState(); - const [maxFps, setMaxFps] = useState(); - - const onCuventPressed = useCallback(() => { - Linking.openURL('https://cuvent.com'); - }, []); - - useEffect(() => { - const loadFormats = async (): Promise => { - const devices = await Camera.getAvailableCameraDevices(); - const formats = devices.flatMap((d) => d.formats); - let max = 0, - min = 0; - formats.forEach((format) => { - const frameRates = format.frameRateRanges.map((f) => f.maxFrameRate).sort((left, right) => left - right); - const highest = frameRates[frameRates.length - 1] as number; - const lowest = frameRates[0] as number; - if (highest > max) max = highest; - if (lowest < min) min = lowest; - }); - setMaxFps(max); - setMinFps(min); - }; - loadFormats(); - }, []); - - useEffect(() => { - const listener = Navigation.events().registerScreenPoppedListener((event) => { - if (event.componentId === componentId) setFpsSelector(Math.round(fps)); - }); - return () => { - listener.remove(); - }; - }, [componentId, fps, setFpsSelector]); - - return ( - - - Frame Rate (FPS): {Math.round(fps)} - {minFps != null && maxFps != null && } - - - - - - Vision Camera is powered by{' '} - - Cuvent - - . - - - ); -}; - -Settings.options = { - topBar: { - visible: true, - title: { - text: 'Settings', - }, - backButton: { - id: 'back', - showTitle: true, - }, - }, -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'stretch', - justifyContent: 'center', - backgroundColor: 'white', - ...SAFE_AREA_PADDING, - }, - aboutText: { - alignSelf: 'center', - fontSize: 14, - fontWeight: 'bold', - color: '#A9A9A9', - maxWidth: '50%', - textAlign: 'center', - }, - hyperlink: { - color: '#007aff', - fontWeight: 'bold', - }, - vControl: { - width: '100%', - }, - spacer: { - flex: 1, - }, -}); diff --git a/example/src/hooks/useCachedState.ts b/example/src/hooks/useCachedState.ts deleted file mode 100644 index 9be298e..0000000 --- a/example/src/hooks/useCachedState.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useCallback, useRef, useState } from 'react'; - -/** - * Same as `useState`, but swallow all calls to `setState` if the value didn't change (uses `===` comparator per default) - * @param initialValue The initial state - * @param comparator A custom comparator, useful if you want to only round numbers or use string locale for comparison. Make sure this function is memoized! - */ -export const useCachedState = (initialValue: T, comparator?: (oldState: T, newState: T) => boolean): [T, (newState: T) => void] => { - const [state, setState] = useState(initialValue); - const cachedState = useRef(initialValue); - - const dispatchState = useCallback( - (newState: T) => { - const areEqual = comparator == null ? cachedState.current === newState : comparator(cachedState.current, newState); - if (areEqual) { - return; - } else { - cachedState.current = newState; - setState(newState); - } - }, - [comparator], - ); - - return [state, dispatchState]; -}; diff --git a/example/src/hooks/useIsForeground.ts b/example/src/hooks/useIsForeground.ts index afaffab..1c1fe90 100644 --- a/example/src/hooks/useIsForeground.ts +++ b/example/src/hooks/useIsForeground.ts @@ -1,9 +1,9 @@ +import { useState } from 'react'; import { useEffect } from 'react'; import { AppState, AppStateStatus } from 'react-native'; -import { useCachedState } from './useCachedState'; export const useIsForeground = (): boolean => { - const [isForeground, setIsForeground] = useCachedState(true); + const [isForeground, setIsForeground] = useState(true); useEffect(() => { const onChange = (state: AppStateStatus): void => { diff --git a/example/src/hooks/useIsScreenFocused.ts b/example/src/hooks/useIsScreenFocused.ts index 3c2296b..dff72b7 100644 --- a/example/src/hooks/useIsScreenFocused.ts +++ b/example/src/hooks/useIsScreenFocused.ts @@ -1,57 +1,39 @@ -import { useEffect, useMemo, useReducer } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Navigation } from 'react-native-navigation'; -type Action = - | { - action: 'push'; - componentId: string; - } - | { - action: 'pop'; - componentId: string; - }; +export const useIsScreenFocussed = (componentId: string): boolean => { + const componentStack = useRef(['componentId']); + const [isFocussed, setIsFocussed] = useState(true); -const reducer = (stack: string[], action: Action): string[] => { - switch (action.action) { - case 'push': { - stack.push(action.componentId); - break; - } - case 'pop': { - const index = stack.indexOf(action.componentId); - if (index > -1) stack.splice(index, 1); - break; - } - } - return [...stack]; -}; - -export const useIsScreenFocused = (componentId: string): boolean => { - const [componentStack, dispatch] = useReducer(reducer, [componentId]); - const isFocussed = useMemo(() => componentStack[componentStack.length - 1] === componentId, [componentStack, componentId]); + const invalidate = useCallback(() => { + const last = componentStack.current[componentStack.current.length - 1]; + setIsFocussed(last === componentId); + }, [componentId, setIsFocussed]); useEffect(() => { const listener = Navigation.events().registerComponentDidAppearListener((event) => { if (event.componentType !== 'Component') return; - dispatch({ - action: 'push', - componentId: event.componentId, - }); + componentStack.current.push(event.componentId); + invalidate(); }); return () => listener.remove(); - }, []); + }, [invalidate]); useEffect(() => { const listener = Navigation.events().registerComponentDidDisappearListener((event) => { if (event.componentType !== 'Component') return; - dispatch({ - action: 'pop', - componentId: event.componentId, - }); + // we can't simply use .pop() here because the component might be popped deeper down in the hierarchy. + for (let i = componentStack.current.length - 1; i >= 0; i--) { + if (componentStack.current[i] === event.componentId) { + componentStack.current.splice(i, 1); + break; + } + } + invalidate(); }); return () => listener.remove(); - }, []); + }, [invalidate]); return isFocussed; }; diff --git a/example/src/state/atoms.ts b/example/src/state/atoms.ts deleted file mode 100644 index 3d0030f..0000000 --- a/example/src/state/atoms.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { atom } from 'pipestate'; - -interface FormatSettings { - fps: number; -} - -export const FormatSettingsAtom = atom({ - default: { - fps: 60, - }, -}); diff --git a/example/src/state/selectors.ts b/example/src/state/selectors.ts deleted file mode 100644 index fa4d4d8..0000000 --- a/example/src/state/selectors.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { selector } from 'pipestate'; -import { FormatSettingsAtom } from './atoms'; - -export const FpsSelector = selector({ - get: ({ get }) => { - return get(FormatSettingsAtom).fps; - }, - set: ({ set, get }, newFps) => { - const formatSettings = get(FormatSettingsAtom); - set(FormatSettingsAtom, { ...formatSettings, fps: newFps }); - }, - dependencies: [FormatSettingsAtom], -}); diff --git a/example/src/views/PressableOpacity.tsx b/example/src/views/PressableOpacity.tsx deleted file mode 100644 index f181a31..0000000 --- a/example/src/views/PressableOpacity.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useCallback } from 'react'; -import { PressableProps, Pressable, PressableStateCallbackType, StyleProp, ViewStyle } from 'react-native'; - -export interface PressableOpacityProps extends PressableProps { - /** - * The opacity to use when `disabled={true}` - * - * @default 1 - */ - disabledOpacity?: number; - /** - * The opacity to animate to when the user presses the button - * - * @default 0.2 - */ - activeOpacity?: number; -} - -export type StyleType = (state: PressableStateCallbackType) => StyleProp; - -/** - * A Pressable component that lowers opacity when in pressed state. Uses the JS Pressability API. - */ -export const PressableOpacity = ({ - style, - disabled = false, - disabledOpacity = 1, - activeOpacity = 0.2, - ...passThroughProps -}: PressableOpacityProps): React.ReactElement => { - const getOpacity = useCallback( - (pressed: boolean) => { - if (disabled) { - return disabledOpacity; - } else { - if (pressed) return activeOpacity; - else return 1; - } - }, - [activeOpacity, disabled, disabledOpacity], - ); - const _style = useCallback(({ pressed }) => [style as ViewStyle, { opacity: getOpacity(pressed) }], [getOpacity, style]); - - return ; -}; - -// Fallback implementation using TouchableOpacity: -// export default function PressableOpacity(props: TouchableOpacityProps & { children?: React.ReactNode }): React.ReactElement { -// return ; -// } diff --git a/example/yarn.lock b/example/yarn.lock index 63e8e70..6f512ad 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -4598,11 +4598,6 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pipestate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pipestate/-/pipestate-1.0.2.tgz#5b859ce947eb9180395199a44e7622b5700a0151" - integrity sha512-1ZaIMyWESR2cb76AZfUosRBSWPQ/2s29naiV72N1iKixzLY1qdPUgZYV990L/pEt/9T/nZDQU7zXSy/aq1ttYw== - pirates@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" @@ -4783,6 +4778,11 @@ react-native-navigation@7.8.4-snapshot.1439: react-lifecycles-compat "2.0.0" tslib "1.9.3" +react-native-pressable-opacity@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.4.tgz#391f33fdc25cb84551f2743a25eced892b9f30f7" + integrity sha512-DBIg7UoRiuBYiFEvx+XNMqH0OEx64WrSksXhT6Kq9XuyyKsThMNDqZ9G5QV7vfu7dU2/IctwIz5c0Xwkp4K3tA== + react-native-reanimated@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.1.0.tgz#b9ad04aee490e1e030d0a6cdaa43a14895d9a54d" diff --git a/src/utils/FormatFilter.ts b/src/utils/FormatFilter.ts index 1436d1e..fc3ae5c 100644 --- a/src/utils/FormatFilter.ts +++ b/src/utils/FormatFilter.ts @@ -43,10 +43,12 @@ export type Size = { */ height: number; }; + const SCREEN_SIZE: Size = { width: Dimensions.get('window').width, height: Dimensions.get('window').height, }; + const applyScaledMask = ( clippedElementDimensions: Size, // 12 x 12 maskDimensions: Size, // 6 x 12 @@ -54,17 +56,11 @@ const applyScaledMask = ( const wScale = maskDimensions.width / clippedElementDimensions.width; // 0.5 const hScale = maskDimensions.height / clippedElementDimensions.height; // 1.0 - if (wScale > hScale) { - return { - width: maskDimensions.width / hScale, - height: maskDimensions.height / hScale, - }; - } else { - return { - width: maskDimensions.width / wScale, - height: maskDimensions.height / wScale, - }; - } + const scale = Math.min(wScale, hScale); + return { + width: maskDimensions.width / scale, + height: maskDimensions.height / scale, + }; }; const getFormatAspectRatioOverflow = (format: CameraDeviceFormat, size: Size): number => {