Commit 220e63fe authored by xhx's avatar xhx

二维码及后退实现

parent 2ec347e7
......@@ -6,51 +6,101 @@
* @flow strict-local
*/
import React, { Component } from 'react'
import React, { Component } from 'react';
import {
Modal,
StyleSheet,
Text,
TouchableOpacity,
View,
SafeAreaView,
ScrollView,
StatusBar,
useColorScheme
Text,
Platform,
BackHandler,
ToastAndroid,
TouchableOpacity
} from 'react-native';
import { WebView } from 'react-native-webview';
import UseCamera from './src/component/camera/Camera';
import Camera from './src/component/camera/index';
import SplashScreen from 'react-native-splash-screen';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Qrcode from './src/component/camera/Qrcode';
import UseCamera from './src/component/camera/Camera';
// import {
// Colors,
// DebugInstructions,
// Header,
// LearnMoreLinks,
// ReloadInstructions,
// } from 'react-native/Libraries/NewAppScreen';
const Stack = createStackNavigator();
class App extends Component {
componentDidMount() {
setTimeout(() => {
SplashScreen.hide(); //关闭启动屏幕
}, 4000);
}
componentWillUnmount() {
this.timer && clearTimeout(this.timer); //清除计时器
}
render(){
return (
<NavigationContainer>
<Stack.Navigator headerMode="none" initialRouteName="main">
<Stack.Screen name="Home" component={Main} />
</Stack.Navigator>
</NavigationContainer>
)
}
}
class Main extends Component {
constructor() {
super()
this.state = {
show: false,
showPhoto: true,
type: 1
}
}
componentDidMount() { //只需添加componentDidMount(){}就行
componentDidMount() {
setTimeout(() => {
SplashScreen.hide(); //关闭启动屏幕
}, 2000);
SplashScreen.hide(); //关闭启动屏幕
}, 4000);
if(Platform.OS === "android") {
BackHandler.addEventListener('hardwareBackPress', () => {
return this.handleBackAndroid();
});
}
}
componentWillUnmount() {
this.timer && clearTimeout(this.timer); //清除计时器
//如果当前是Android系统,则移除back键按下事件监听
if(Platform.OS === "android") {
BackHandler.removeEventListener('hardwareBackPress', ()=>{});
}
}
handleBackAndroid() {
if (this.props.navigation) {
const navigation = this.props.navigation
// 判断是否是执行 webview 中网页的的回退
if (this.canGoBack) {
this.webView.goBack()
return true
}
if (!navigation.canGoBack()) {
//连按两次退出应用
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
//最近2秒内按过back键,可以退出应用。
BackHandler.exitApp()
return false;
}
this.lastBackPressed = Date.now();
ToastAndroid.show('再按返回退出应用', ToastAndroid.SHORT);
return true;
} else {
this.props.navigation.goBack();
}
}
}
// 交互传值
handleInjectJavascript = (data) => {
console.log('send message: ', data)
const injectJavascriptStr = `(function() {
window.WebViewBridge.onMessage(${JSON.stringify(data)});
})()`; // 拼接 数据 为 方法
......@@ -63,32 +113,48 @@ class App extends Component {
// 界面初始化传值
initInjectJavascript(data) {
console.log('init')
const injectJavascriptStr = `(function() {
window.WebViewBridge.onMessage(${JSON.stringify(data)});
})()`; // 拼接 数据 为 方法
})()`
if(this.webView) {
this.webView.injectJavaScript(injectJavascriptStr)
} // 通过 injectJavaScript 将 数据 传递给WebView页面,并立即执行为js
}
}
_onMessage = (event) => {
_onMessage = (event, navState) => {
// '接收vue发来的消息onMessage wow,RN!!'
console.log('接收vue发来的消息onMessage', event.nativeEvent.data)
console.log('接收vue发来的消息onMessage', event.nativeEvent.data, event)
const str = event.nativeEvent.data
this.selectType(str)
this.showCamera()
if (str === 'navigationStateChange') {
const url = event.nativeEvent.url
this.canGoBack = event.nativeEvent.canGoBack
if (url.indexOf('Home') > -1 || url.indexOf('Mine') > -1) {
this.canGoBack = false
}
} else {
this.selectType(str)
this.showCamera()
}
}
selectType(str) {
if (str === 'camera') {
this.setState({
showPhoto: true
})
} else {
this.setState({
showPhoto: false
})
switch(str) {
case 'camera':
this.setState({
type: 1,
showPhoto: true
})
break
case 'record':
this.setState({
type: 1,
showPhoto: false
})
break
case 'qrcode':
this.setState({
type: 2
})
}
}
......@@ -104,12 +170,38 @@ class App extends Component {
})
}
_onNavigationStateChange(navState) {
if (navState.canGoBack) {
global.isBack = true
} else {
global.isBack = false
}
}
render() {
return (
<>
<WebView ref={ (webView) => this.webView = webView }
originWhitelist={ ['*'] }
javaScriptEnabled={ true }
injectedJavaScript={`
(function() {
function wrap(fn) {
return function wrapper() {
var res = fn.apply(this, arguments);
window.ReactNativeWebView.postMessage('navigationStateChange');
return res;
}
}
history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);
window.addEventListener('popstate', function() {
window.ReactNativeWebView.postMessage('navigationStateChange'); // web端向APP端发送消息
});
})();
true;
`}
// 开启缓存
// domStorageEnabled={ true }
thirdPartyCookiesEnabled={ true }
......@@ -120,21 +212,30 @@ class App extends Component {
useWebKit={true}
// 加载时强制使用loading转圈视图,注意如果为true,在低性能下,webview可能会加载失败,显示为空白
startInLoadingState={false}
// 页面跳转相关
onNavigationStateChange={this._onNavigationStateChange}
// webview加载错误页面
// source={{uri: 'http://47.114.159.142:8983'}} // 网络路径
source={{uri: 'http://192.168.21.109:8080/'}} //本地路径
// source={{uri: 'http://www.baidu.com/'}}
/>
{ this.state.show ?
(
<Modal>
<UseCamera
<Modal onRequestClose={() => this.setState({show: false})}>
<Camera
showPhoto={this.state.showPhoto}
type={this.state.type}
hideCamera={this.handleInjectJavascript}
back={this.hideCamera.bind(this)} />
</Modal>
)
: null }
{/* <UseCamera
showPhoto={this.state.showPhoto}
type={this.state.type}
hideCamera={this.handleInjectJavascript}
back={this.hideCamera.bind(this)} /> */}
{/* <UseCamera /> */}
{/* <Qrcode /> */}
</>
)
}
......
......@@ -20,7 +20,7 @@ yarn android
### 相机调用
vue 向 rn 传递数据
```
// str: 字符串, camera -> 相机拍照; record -> 相机摄像
// str: 字符串, camera -> 相机拍照; record -> 相机摄像; qrcode -> 二维码
window.ReactNativeWebView.postMessage(str)
```
......@@ -59,6 +59,8 @@ interface RecordResponse {
/** iOS only */
codec: VideoCodec[keyof VideoCodec];
}
// qrcode
QrcodeResponse: string
```
### 安卓打包
......
......@@ -10,9 +10,18 @@
"lint": "eslint ."
},
"dependencies": {
"@react-native-community/masked-view": "^0.1.11",
"@react-navigation/native": "^5.9.4",
"@react-navigation/stack": "^5.14.5",
"react": "17.0.1",
"react-native": "0.64.2",
"react-native-camera": "^4.0.0",
"react-native-gesture-handler": "^1.10.3",
"react-native-image-picker": "^4.0.6",
"react-native-qr-scanner": "^1.3.2",
"react-native-reanimated": "^2.2.0",
"react-native-safe-area-context": "^3.2.0",
"react-native-screens": "^3.4.0",
"react-native-splash-screen": "^3.2.0",
"react-native-webview": "^11.6.5"
},
......
......@@ -105,11 +105,11 @@ class UseCamera extends PureComponent {
render() {
return (
<View style={styles.container}>
<View style={styles.topBar}>
{/* <View style={styles.topBar}>
<TouchableOpacity onPress={this.back.bind(this)} style={styles.topCapture}>
<Text style={{fontFamily: 'iconfont', color: 'white', fontSize: 24}}> {'\ue657'} </Text>
</TouchableOpacity>
</View>
</View> */}
<RNCamera
ref={ref => {
this.camera = ref;
......
import React, { PureComponent } from 'react';
import {
Dimensions,
StyleSheet,
View,
Animated,
Easing,
Text,
Button,
Alert,
} from 'react-native';
import {RNCamera} from 'react-native-camera';
import ImagePicker from 'react-native-image-picker';
import {QRreader} from 'react-native-qr-scanner';
const options = {
title: '请选择',
quality: 0.8,
cancelButtonTitle: '取消',
takePhotoButtonTitle: null,
chooseFromLibraryButtonTitle: '选择相册',
customButtons: [{name: 'hangge', title: '输入编码'}],
allowsEditing: true,
noData: false,
storageOptions: {
skipBackup: true,
path: 'images',
},
};
export default class Qrcode extends PureComponent {
static navigationOptions = ({navigation}) => {
return {
title: '扫一扫',
headerRight: () => (
<View style={styles.rightBtn}>
<Button
title={
navigation.state.params.torchState === 'off' ? '开灯' : '关灯'
}
onPress={() => navigation.state.params.onchangeTorchState()}
/>
<Button
title="+"
onPress={() => navigation.state.params.choosePicker()}
/>
</View>
),
};
};
//构造函数
constructor(props) {
super(props);
this.state = {
torchState: 'off',
qrCode: null,
};
this.moveAnim = new Animated.Value(0);
}
componentDidMount() {
this.startAnimation();
// this.props.navigation.setParams({
// choosePicker: this.choosePicker,
// onchangeTorchState: this.onchangeTorchState,
// torchState: this.state.torchState,
// });
}
_onBarCodeRead = (value) => {
const {qrCode} = this.state;
if (qrCode) {
return;
}
this.setState(
{
qrCode: value,
},
() => {
this.props.hideCamera(this.state.qrCode.data)
this.props.back()
// alert(this.state.qrCode.data); // 打印出扫描后的数据
},
);
};
// 扫描动画
startAnimation = () => {
this.moveAnim.setValue(0);
Animated.timing(this.moveAnim, {
toValue: 1,
duration: 3500,
easing: Easing.linear,
useNativeDriver: true,
}).start(() => this.startAnimation());
};
// 修改torchState 状态
onchangeTorchState = () => {
const {torchState} = this.state;
this.setState(
{
torchState: torchState === 'off' ? 'ON' : 'off',
},
() => {
this.props.navigation.setParams({torchState: this.state.torchState});
},
);
};
// 从相册获取二维码并扫描返回结果
choosePicker = () => {
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
this.onClickModal();
} else {
if (response.uri) {
var path = response.path;
if (!path) {
path = response.uri;
}
QRreader(path)
.then((data) => {
this.props.hideCamera(data)
this.props.back()
// Alert.alert(data);
})
.catch((err) => {
Alert.alert('识别失败');
});
}
}
});
};
onClickModal = () => {
};
//渲染
render() {
const winWidth = Dimensions.get('window').width;
const winHeight = Dimensions.get('window').height;
return (
<>
<View style={styles.container}>
<RNCamera
style={styles.preview}
ratio={'16:9'}
defaultVideoQuality={RNCamera.Constants.VideoQuality['720p']}
scanAreaLimit={true}
scanAreaX={(115 * winWidth) / 750}
scanAreaY={(328 * winHeight) / 1334}
scanAreaWidth={(522 * winWidth) / 750}
scanAreaHeight={(521 * winHeight) / 1334}
flashMode={
this.state.torchState == 'off'
? RNCamera.Constants.FlashMode.off
: RNCamera.Constants.FlashMode.torch
}
onBarCodeRead={this._onBarCodeRead.bind(this)}>
<View style={styles.rectangleContainer}>
<View style={styles.rectangle} />
<Animated.View
style={[
styles.border,
{
transform: [
{
translateY: this.moveAnim.interpolate({
inputRange: [0, 1],
outputRange: [-200, 0],
}),
},
],
},
]}
/>
<Text style={styles.rectangleText}>
将二维码放入框内,即可自动扫描
</Text>
</View>
</RNCamera>
</View>
</>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
},
btnText: {color: 'white', fontSize: 16},
rectangleContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
},
rectangle: {
height: 200,
width: 200,
borderWidth: 1,
borderColor: '#00FF00',
backgroundColor: 'transparent',
},
rectangleText: {
flex: 0,
color: '#fff',
marginTop: 10,
},
border: {
flex: 0,
width: 200,
height: 2,
backgroundColor: '#00FF00',
},
rightBtn: {
flex: 1,
flexDirection: 'row',
alignItems: 'flex-end',
},
});
\ No newline at end of file
import React, { PureComponent } from 'react'
import {
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native'
import UseCamera from './Camera'
import Qrcode from './Qrcode'
class Camera extends PureComponent {
constructor(props) {
super(props)
}
render() {
return (
<>
{
this.props.type === 1 ?
<UseCamera
showPhoto={this.props.showPhoto}
hideCamera={this.props.hideCamera}
back={this.props.back}
></UseCamera> : this.props.type === 2 ?
<Qrcode hideCamera={this.props.hideCamera} back={this.props.back} /> : null
}
</>
)
}
}
export default Camera
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment