Bla*_*lak 8 android reactjs react-native expo
我正在使用来自 expo 包的相机,但我遇到了相机预览失真的问题。预览使图像在横向视图中显得更宽,在纵向视图中显得更薄。我发现的大多数解决方案都没有使用 expo-camera。
相关代码:
相机.page.js:
import React from 'react';
import { View, Text } from 'react-native';
import { Camera } from 'expo-camera';
import * as Permissions from 'expo-permissions'
import { Platform } from 'react-native';
import styles from './styles';
import Toolbar from './toolbar.component';
const DESIRED_RATIO = "18:9";
export default class CameraPage extends React.Component {
camera = null;
state = {
hasCameraPermission: null,
};
async componentDidMount() {
const camera = await Permissions.askAsync(Permissions.CAMERA);
const audio = await Permissions.askAsync(Permissions.AUDIO_RECORDING);
const hasCameraPermission = (camera.status === 'granted' && audio.status === 'granted');
this.setState({ hasCameraPermission });
};
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>Access to camera has been denied.</Text>;
}
return (
<React.Fragment>
<View>
<Camera
ref={camera => this.camera = camera}
style={styles.preview}
/>
</View>
<Toolbar/>
</React.Fragment>
);
};
};
Run Code Online (Sandbox Code Playgroud)
样式.js:
import { StyleSheet, Dimensions } from 'react-native';
const { width: winWidth, height: winHeight } = Dimensions.get('window');
export default StyleSheet.create({
preview: {
height: winHeight,
width: winWidth,
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
paddingBottom: 1000,
},
alignCenter: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
bottomToolbar: {
width: winWidth,
position: 'absolute',
height: 100,
bottom: 0,
},
captureBtn: {
width: 60,
height: 60,
borderWidth: 2,
borderRadius: 60,
borderColor: "#FFFFFF",
},
captureBtnActive: {
width: 80,
height: 80,
},
captureBtnInternal: {
width: 76,
height: 76,
borderWidth: 2,
borderRadius: 76,
backgroundColor: "red",
borderColor: "transparent",
},
});
Run Code Online (Sandbox Code Playgroud)
我能做些什么来解决这个问题?
Ado*_*zis 27
这个有点乏味。
基本上问题在于相机预览与屏幕的宽度/高度比不同。据我所知,这只是 Android 上的一个问题,其中:
解决这个问题的方法主要是:
const { height, width } = Dimensions.get('window');
const screenRatio = height / width;
Run Code Online (Sandbox Code Playgroud)
const [isRatioSet, setIsRatioSet] = useState(false);
// the camera must be loaded in order to
// access the supported ratios
const setCameraReady = async() => {
if (!isRatioSet) {
await prepareRatio();
}
};
return (
<Camera
onCameraReady={setCameraReady}
ref={(ref) => {
setCamera(ref);
}}>
</Camera>
);
Run Code Online (Sandbox Code Playgroud)
const ratios = await camera.getSupportedRatiosAsync();
Run Code Online (Sandbox Code Playgroud)
这将返回一个格式为 ['w:h'] 的字符串数组,因此您可能会看到如下内容:
[ '4:3', '1:1', '16:9' ]
Run Code Online (Sandbox Code Playgroud)
Essentially what you are trying to do here is to loop through the supported camera ratios and determine which of them are the closest in proportion to the screen. Any that are too tall we toss out since in this example we want to the preview to take up the entire width of the screen and we don't mind if the preview is shorter than the screen in portrait mode.
a) Get screen aspect ratio
So let's say that the screen is 480w x 800h, then the aspect ratio of the height / width is 1.666... If we were in landscape mode, we would do width / height.
b) Get supported camera aspect ratios
Then we look at each camera aspect ratio and calculate the width / height. The reason we calculate this and not the height / width like we do the screen is that the camera aspect ratios are always in landscape mode.
So:
4:3 => 1.33331:1 => 116:9 => 1.77777c) Calculate supported camera aspect ratios
For each one, we subtract from the aspect ratio of the screen to find the difference. Any that exceed the aspect ratio of the screen on the long side are discarded:
4:3 => 1.333... => 0.333... (closest without going over!)1:1 => 1 => 0.666... (worst match)16:9 => 1.777... => -0.111... (too wide)d) closest shortest camera aspect ratio matching screen aspect ratio
So we pick the 4:3 aspect ratio for this camera on this screen.
e) Calculate difference between camera aspect ratio and screen aspect ratio for padding and positioning.
To position the preview in the center of the screen, we can calculate half the difference between the screen height and the scaled height of the camera preview.
[ '4:3', '1:1', '16:9' ]
Run Code Online (Sandbox Code Playgroud)
verticalPadding = (screenHeight - bestRatio * screenWidth) / 2
Run Code Online (Sandbox Code Playgroud)
<Camera> component to have the appropriate scaled height to match the applied camera aspect ratio and to be centered or whatever in the screen.let distances = {};
let realRatios = {};
let minDistance = null;
for (const ratio of ratios) {
const parts = ratio.split(':');
const realRatio = parseInt(parts[0]) / parseInt(parts[1]);
realRatios[ratio] = realRatio;
// ratio can't be taller than screen, so we don't want an abs()
const distance = screenRatio - realRatio;
distances[ratio] = realRatio;
if (minDistance == null) {
minDistance = ratio;
} else {
if (distance >= 0 && distance < distances[minDistance]) {
minDistance = ratio;
}
}
}
// set the best match
desiredRatio = minDistance;
// calculate the difference between the camera width and the screen height
const remainder = Math.floor(
(height - realRatios[desiredRatio] * width) / 2
);
// set the preview padding and preview ratio
setImagePadding(remainder / 2);
Run Code Online (Sandbox Code Playgroud)
Something to note is that the camera aspect ratios are always width:height in landscape mode, but your screen might be in either portrait or landscape.
This example only supports a portrait-mode screen. To support both screen types, you'll have to check the screen orientation and change the calculations based on which orientation the device is in.
<Camera
style={[styles.cameraPreview, {marginTop: imagePadding, marginBottom: imagePadding}]}
onCameraReady={setCameraReady}
ratio={ratio}
ref={(ref) => {
setCamera(ref);
}}
/>
Run Code Online (Sandbox Code Playgroud)
You can play with the Expo Snack here
And finally, a camera preview with preserved proportions, which uses padding on the top and bottom to center the preview:
You can also try this code out online or in your Android on Expo Snack.
纵向模式下的简单解决方案:
import * as React from "react";
import { Camera } from "expo-camera";
import { useWindowDimensions } from "react-native";
const CameraComponent = () => {
const {width} = useWindowDimensions();
const height = Math.round((width * 16) / 9);
return (
<Camera
ratio="16:9"
style={{
height: height,
width: "100%",
}}
></Camera>
);
};
export default CameraComponent;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3732 次 |
| 最近记录: |