Simple React Native Image Game is Lagging.How do i optimize? - reactjs

I am trying to implement a simple img tapping game with react native for learning purposes.I am using this librarys https://github.com/bberak/react-native-game-engine game loop component as a game loop.
What I am trying to do is render some imgs with random locations on screen with small radius then increase it up to a value, after decrease it and finally remove the img from screen (or when tapped) - just like in this game: http://mouseaccuracy.com/
import React, { Component } from "react";
import { AppRegistry, StyleSheet, Dimensions, View,ToastAndroid,TouchableOpacity,Image,Text } from "react-native";
const { width: WIDTH, height: HEIGHT } = Dimensions.get("window");
import { GameLoop } from "react-native-game-engine";
import { Fonts } from '../utils/Fonts';
import FastImage from 'react-native-fast-image'
import * as Progress from 'react-native-progress';
import SwordImage from "../assets/tapfight/sword.png";
import ShieldImage from "../assets/tapfight/shield.png";
this.renderSwordOrCircle(circle)
)
})
)
}
}
renderHealth = () =>{
return(
<View>
<View style={{ alignItems:'center' ,position: "absolute", top: 0, left: 0}}>
<Text style={{fontFamily:Fonts.MainFont,color:'white',fontSize:25}}>{this.playerHealth}</Text>
<Progress.Bar color='red' progress={this.playerHealth/100} width={100} height={18} />
</View>
<View style={{ alignItems:'center',position: "absolute", top: 0, right: 0}}>
<Text style={{fontFamily:Fonts.MainFont,color:'white',fontSize:25}}>{this.enemyHealth}</Text>
<Progress.Bar color='red' progress={this.enemyHealth/100} width={100} height={18} />
<FastImage resizeMode="contain" style={{width:100,height:100}} source={require('../assets/tapfight/guard_2_head.png')}></FastImage>
</View>
</View>
)
}
render() {
if(this.state.fight==true){
return (
<View style={{flex:1,backgroundColor:'transparent'}} >
<GameLoop style={{flex:1}} onUpdate={this.updateCircle}>
{this.renderHealth()}
{this.renderCircles()}
</GameLoop>
</View>
);
}else{
return(
null
)
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
What i am doing is i am generating images every 600 ms on screen and player tries to tap them.The problem is when there is more than 2 images on screen fps drops significantly.Tested in android actual device.I am updating state once at the end of updateCircle function.
Game loop is updateCircle component

Your problem is that you're trying to animate a component that is not made for being animated.
I dont know well the FastImage component, but I understand that it purpose is to pre-load images, not animate.
You shoud use Animated.Image for the image, and Animated.timing for managing the animations of Animated.Image

Related

Change Image on Click in React Native

I am trying to create an app that changes to one of many images when the image is clicked. I have used touchable opacity and can make the image show an alert when clicked. I just can't get it to change to one of the many others in the file.
Here is all my code so far:
import React from 'react';
import { Component, Stylesheet, useState, TouchableOpacity, Button, View, Text, Image, ScrollView, TextInput, Alert } from 'react-native';
// main part of the app
const App = () => {
var array = [require("./cards/card.png"), require("./cards/card2.png")]
var x = 0
//onclick function
const handlePress = () => {
//some logic
alert("help")
x+=1
}
// what shows up on the app
return (
<ScrollView>
<View>
<Text>{array[x]}</Text>
<Text>{x}</Text>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<TouchableOpacity
onPress={(handlePress)}
>
<Image
style={{
width: 300,
height: 300,
}}
resizeMode="contain"
source={
array[x]
}
/>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
}
export default App;
The other images that I want the card to change to are in the cards folder. What do I do to make it dynamic and change it to any of the other cards in the folder?
in order to change image or any information in a screen that have been mounted, React have to re-render the screen and put the right information that you want.
To achieve this, you should use React states https://reactnative.dev/docs/state
Your code could be like this:
import React from 'react';
import { Component, Stylesheet, useState, TouchableOpacity, Button, View, Text, Image, ScrollView, TextInput, Alert } from 'react-native';
// main part of the app
const App = () => {
const [card, setCard] = useState(0); //initial state
var array = [
"./cards/card.png",
"./cards/card2.png"
]
//onclick function
const handlePress = () => {
//some logic
//set card state to the next index
setCard( current => current + 1);
//so everytime you click function the state will change and this re-render your component with the new data.
}
// what shows up on the app
return (
<ScrollView>
<View>
<Text>{array[card]}</Text>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<TouchableOpacity
onPress={(handlePress)}
>
<Image
style={{
width: 300,
height: 300,
}}
resizeMode="contain"
source={ require(array[card]) }
/>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
}
export default App;

BottomTabNavigator coming on top instead of bottom in React Native expo mobile app

I am developing a mobile react native expo app. I am using BottomTabNavigator (NavigationContainer). As the name suggests it should appear at the bottom but it is incorrectly appearing on top.
I already have another image (logo.png) on the top of the screen and the navigationbar (or NavigationContainer) is also coming on top and overlapping above the image. Please help me resolve this issue. See my code below:
In the below code MyTabs is the Navigator created from createBottomTabNavigator(). This is incorrectly appearing on top of the screen.
import React from 'react';
import { Image, StyleSheet, Text, View, SafeAreaView, StatusBar, Platform } from 'react-native';
import logo from './assets/logo.png';
import { NavigationContainer } from '#react-navigation/native';
import MyTabs from './navigator/AppNavigator';
export default function App() {
return (
<SafeAreaView style={{ paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight: 0 }} >
<View>
<View style={styles.container}>
<Image source={logo} style={{ width: 100, height: 100 }} />
<Text style={{color: '#888', fontSize: 18, alignItems: 'center'}}>
To share a photo from your phone with a friend or anyone, just press the button below!
</Text>
</View>
<View >
<NavigationContainer >
<MyTabs /> // This is incorrectly coming on top of screen.
</NavigationContainer>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
// justifyContent: 'center',
},
});
The NavigationContainer should be the outermost component in App. This then wraps the Tab.Navigator component (in your case MyTabs), where you create tabs linked to each of your components. Inside your components, you are able to utilize SafeAreaView to then display the image at the top of the screen. Any type of Navigation scheme has to be made the top most component in the hierarchy in react native, wrapping the rest of your components. I've altered your code below:
import React from 'react';
import { Image,  StyleSheet, Text, View, SafeAreaView, StatusBar, Platform } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
export default function App() {
  const Tab = createBottomTabNavigator()
  return (
    <NavigationContainer >
      <Tab.Navigator>  
        <Tab.Screen name="Home" component={myComponent} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}
const myComponent = () => {
  return (
    <SafeAreaView style={{ paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight: 0 }} >
      <View>
        <View style={styles.container}>
          <Image source={require('#expo/snack-static/react-native-logo.png')} style={{ width: 100, height: 100 }} />
          <Text style={{color: '#888', fontSize: 18, alignItems: 'center'}}>To share a photo from your phone with a friend or anyone, just press the button below!</Text>
        </View>
      </View>
    </SafeAreaView>
  )
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
     alignItems: 'center',
    // justifyContent: 'center',
  },
});

Show loading indicator between navigation of pages in a Webview

I have a url in the Webview like https://www.nytimes.com/ The current code works inital page load but If I type tap anything in the link, the page takes a while to load and there are no loading indicators in the website. Is there any way we can put a page loading indicator in React Native while we click on any link or page loading specially if it is server side rendered like next js?
Here is my ReactNative code.
import * as React from 'react';
import {
View,
Text,
Image,
SafeAreaView,
ScrollView,
TextInput,
TouchableOpacity,
} from 'react-native';
import styles from './styles';
import { WebView } from 'react-native-webview';
// import WelcomeSwiper from '../../components/WelcomeScreen/WelcomeSwiper';
import LoadingIcon from '../../components/Loading/index.js';
const WebView = ({ navigation }) => {
return (
<SafeAreaView style={styles.container}>
<WebView
javaScriptEnabled={true}
domStorageEnabled={true}
renderLoading={LoadingIcon}
source={{ uri: 'https://www.nytimes.com/ ' }}
startInLoadingState={true}
/>
</SafeAreaView>
);
};
export default WebView;
Here is my loading component
import React from 'react';
import { StyleSheet, Platform, ActivityIndicator } from 'react-native';
const LoadingIcon = () => {
return (
<ActivityIndicator
color='#009688'
size='large'
style={styles.ActivityIndicatorStyle}
/>
);
}
export default LoadingIcon;
const styles = StyleSheet.create(
{
WebViewStyle:
{
justifyContent: 'center',
alignItems: 'center',
flex: 1,
marginTop: (Platform.OS) === 'ios' ? 20 : 0
},
ActivityIndicatorStyle: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
}
});
we can use these two approaches to get the result:
You can check if the WebView is loading something or not with the onLoadProgress method. This method gives you a number between 0 and 1. If the page is fully loaded it will return number 1, update your state and show the ActivityIndicator according to it:
you can use onLoadStart and onLoadEnd to update your state and show the ActivityIndicator according to it!
for more info check the: https://github.com/react-native-community/react-native-webview/blob/master/docs/Reference.md#onloadprogress
you can also use your ActivityIndicator wrapped by WebView, *do not Forget this method works in ios for android put it outside of WebView
and this is a working code sample for you:
import React, {useState} from 'react';
import {View, Text, SafeAreaView, ActivityIndicator} from 'react-native';
import {WebView} from 'react-native-webview';
function WebViewTest() {
const [isLoading, setLoading] = useState(false);
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{uri: 'https://www.nytimes.com/'}}
onLoadStart={(syntheticEvent) => {
setLoading(true);
}}
onLoadEnd={(syntheticEvent) => {
setLoading(false);
}} />
{isLoading && (
<View style={{flex: 10, backgroundColor: 'white'}}>
<ActivityIndicator
color="#009688"
size="large"
// style={{position: 'absolute', left: 200, top: 300}}
/>
</View>
)}
</SafeAreaView>
);
}
export default WebViewTest;
I hope it helps
I use onLoadProgress to solve this issue.
renderLoading function is just called at the initial loading state of webview component. Using renderLoading is not helpful to show activity indicators at any tap on page links or navigating between webview pages.
Checking onLoadStart and onLoadEnd is useful in android but in iOS onLoadEnd is not called in the back navigation gesture which results in endless spinning of activity indicator.
onLoadProgress returns a number between 0-1 while webview is in the loading state. You check its progress state and update your activity indicator state.
For more information about onLoadProgress: onLoadProgress
Here is a working code example for you:
import React, {useState} from 'react';
import {View, Text, SafeAreaView, ActivityIndicator} from 'react-native';
import {WebView} from 'react-native-webview';
function WebViewTest() {
const [isLoading, setLoading] = useState(false);
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{uri: 'https://www.nytimes.com/'}}
onLoadProgress={({nativeEvent}) => {
if (nativeEvent.progress != 1 && isLoading == false ) {
setLoading(true)
} else if (nativeEvent.progress == 1 ) {
setLoading(false)
}
}}
/>
{isLoading && (
<View style={{flex: 10, backgroundColor: 'white'}}>
<ActivityIndicator
color="#009688"
size="large"
// style={{position: 'absolute', left: 200, top: 300}}
/>
</View>
)}
</SafeAreaView>
);
}
export default WebViewTest;

How to play a segment of a Lottie Animation (.json file ) in React Native

I want to play only a segment of the animation in react native using lottie view
the length is about 5 seconds while in speed={1} I wanna play only the first 3 seconds and then go to the next screen
the code is down below
LogoScreen.js :
import React from 'react';
import { StyleSheet, View, Image, TextInput, StatusBar, Text } from "react-native";
import Icons from 'react-native-vector-icons/Ionicons';
import LottieView from "lottie-react-native";
export default class ChatScreen extends React.Component {
onAnimationFinish = () => {
this.props.navigation.navigate("Login")
}
render () {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" backgroundColor="#161f3d" />
<View>
<LottieView
source={require('../assets/animations/lottie/MotionCorpse-Jrcanest.json')}
style={{ justifyContent: "center", alignSelf: "center", height: "100%", width: "100%" }}
autoPlay
loop={false}
speed={1}
onAnimationFinish={this.onAnimationFinish}
/>
</View>
</View>
)
}
Well you can do it by several ways, one of the way would be like below.
You can use ref to play it manually and then after 3 seconds just redirect to next screen.
import React from 'react';
import { StyleSheet, View, Image, TextInput, StatusBar, Text } from "react-native";
import Icons from 'react-native-vector-icons/Ionicons';
import LottieView from "lottie-react-native";
export default class ChatScreen extends React.Component {
componentDidMount() {
this.anim.play();
setTimeout(() => {
this.props.navigation.navigate("Login")
}, 3000);
}
render() {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" backgroundColor="#161f3d" />
<View>
<LottieView
source={require('../assets/animations/lottie/MotionCorpse-Jrcanest.json')}
style={{ justifyContent: "center", alignSelf: "center", height: "100%", width: "100%" }}
autoPlay={false}
loop={false}
speed={1}
ref={animation => { this.anim = animation; }}
/>
</View>
</View>
)
}
}
Another way is if you know exact frame numbers then you can play animation till that frame which completes at 3 seconds. It is already mentioned in Lottie documentation.
this.animation.play(30, 120);

How to create clickable points of interest in React Native?

I want to know how i can create something like this (See the picture) in react native, exactly i mean the clickable points of interest.
Is there any component that solves this problem?
Examples:
You can make a Marker component as shown below. This is just a simple demo, you can modify it as you want
Marker.js
import React from 'react'
import {TouchableOpacity} from 'react-native'
const Marker = ({onPress, top, left}) => (
<TouchableOpacity onPress={onPress} style={{height: 10, width: 10, borderRadius: 5, backgroundColor: 'red', position: 'absolute', top, left}} />
)
export default Marker
Usage
import Marker from './Marker'
_onPress = () => //...Do the stuff here
render() {
return (
<ImageBackground style={{flex: 1}} source={{uri: 'https://s3.envato.com/files/214277896/us-map-html5.png'}} resizeMode={'stretch'}>
<Marker onPress={this._onPress} top={100} left={200} />
</ImageBackground>
)
}

Resources