How can I make my react-native-camera capture fast? - reactjs

takePicture = async function() {
if (this.camera) {
const options = { quality: 0.5, base64: true, };
const data = await this.camera.takePictureAsync(options);
this.setState({path: data.uri})
}
}
As soon as I call my takePicture fuction to capture image, the camera still keeps moving and doesn't pause. I want the camera to pause and then show me the image.
Is there something here to with handling Promise? If yes, I don't know where to do it and how.
I've also tried using pauseAfterCapture:true but it still takes 1 or 2 second to capture the image.
I know this is an old issue but no solution could help me yet. Please help.

I've also found the available camera components very slow, that is why I created react-native-fast-camera and I just open sourced it for the public use. It is very customizable and completely controllable by react native components.
Note: Currently the Android version is still work in progress.
Here is an example:
import FastCamera, { Methods } from 'react-native-fast-camera';
<FastCamera style={{ height: cameraHeight, width: cameraWidth }}
onSaveSuccess={imageUrl => {
console.log("onSaveSuccess: ", imageUrl);
}}
onGalleryImage={imageUrl => {
console.log("onGalleryImage: ", imageUrl);
}}
onFlashToggle={isflashOn => {
console.log('flash info: ', isflashOn);
}}
>
{/* here render your buttons to control the camera component */}
<Button
title="capture picture"
onPress={()=> Methods.takePicture();}
/>
</FastCamera>
And here is a screenshot:

Related

React phaser Reinitialize problem because of grid-engine plugin

I use "phaser", 'ion-phaser' and a phaser plugin 'grid-engine' for integrate a phaser game to react.
I can initialize the game successfully, then destroy it without an error. But when I try to reinitialize it İt give 2 errors
Scene Plugin key in use: GridEngine
and
Uncaught TypeError: Cannot read properties of undefined (reading 'create')
at Scene.create (isometric.js:93:1)
at SceneManager.create (phaser.js:84684:1)
at SceneManager.loadComplete (phaser.js:84615:1)
at LoaderPlugin.emit (phaser.js:1671:1)
at LoaderPlugin.loadComplete (phaser.js:165337:1)
at LoaderPlugin.fileProcessComplete (phaser.js:165311:1)
at SpriteSheetFile.onProcessComplete (phaser.js:4290:1)
at data.onload (phaser.js:16548:1)
second error at this line
this.gridEngine.create(cloudCityTilemap, gridEngineConfig);
Additionally when I try a phaser game without plugins like grid engine, there is not an error.
I search and I think I must remove grid-engine plugin when destroy game but I could not find how.
My question is
How to destroy a phaser game with included plugins and restart it?
this is code sample
const game = {
width: 800,
height: 600,
type: Phaser.AUTO,
scene: {
preload,
create,
update,
},
plugins: {
scene: [
{
key: 'GridEngine',
plugin: GridEngine,
mapping: 'gridEngine'
},
],
},
}
const gameRef = useRef(null)
const [initialize, setInitialize] = useState(false)
const destroy = () => {
if (gameRef.current) {
gameRef.current.destroy()
}
setInitialize(false)
}
return (
<>
<IonPhaser game={game} ref={gameRef} initialize={initialize} />
<button onClick={()=>setInitialize(true)}>Initialize</button>
<button onClick={destroy}>Destroy</button>
</>
)
}
Finally I want to change different games or levels at the same page
Im not 100% sure, if this will solve all your problems, but
<button onClick={setInitialize(true)}>
is not correct, you should not call the function, you should only pass it.
Try replacing that line with
<button onClick={ () => setInitialize(true)}>
For Details, checkout this article / documentation for details.
Update: How to get the game object
to get the Phaser Game object you would have to use getInstance (link to ion-phaser documentation) and to remove the Plugin you would have to do something like this:
this.gameRef.current.getInstance()
.then(game => game.plugins.removeScenePlugin("GridEngine"))

react native Why does my async code hang the rendering?

General :
TL;DR: async code hangs rendering.
I have this component with a Modal and inside the Modal it renders a list of filters the user can choose from. When pressing a filter the color of the item changes and it adds a simple code(Number) to an array. The problem is that the rendering of the color change hangs until the logic that adds the code to the array finishes.
I don't understand why adding a number to an array takes between a sec and two.
I don't understand why the rendering hangs until the entire logic behind is done.
Notes: I come from a Vue background and this is the first project where I'm using react/react-native. So if I'm doing something wrong it would be much appreciated if someone points that out
Snack that replicates the issue :
Snack Link
My code for reference :
I use react-native with expo managed and I use some native-base components for the UI.
I can't share the whole code source but here are the pieces of logic that contribute to the problem :
Parent : FilterModal.js
The rendering part :
...
<Modal
// style={styles.container}
visible={modalVisible}
animationType="slide"
transparent={false}
onRequestClose={() => {
this.setModalVisible(!modalVisible);
}}
>
<Center>
<Pressable
onPress={() => this.setModalVisible(!modalVisible)}
>
<Icon size="8" as={MaterialCommunityIcons} name="window-close" color="danger.500" />
</Pressable>
</Center>
// I use sectionList because the list of filters is big and takes time to render on the screen
<SectionList
style={styles.container}
sections={[
{ title: "job types", data: job_types },
{ title: "job experience", data: job_experience },
{ title: "education", data: job_formation },
{ title: "sector", data: job_secteur }
]}
keyExtractor={(item) => item.id}
renderItem={({ item, section }) => <BaseBadge
key={item.id}
pressed={this.isPressed(section.title, item.id)}
item={item.name}
code={item.id}
type={section.title}
add={this.addToFilters.bind(this)}
></BaseBadge>}
renderSectionHeader={({ section: { title } }) => (
<Heading color="darkBlue.400">{title}</Heading>
)}
/>
</Modal>
...
The logic part :
...
async addToFilters(type, code) {
switch (type) {
case "job types":
this.addToTypesSelection(code);
break;
case "job experience":
this.addToExperienceSelection(code);
break;
case "formation":
this.addToFormationSelection(code);
break;
case "sector":
this.addToSectorSelection(code);
break;
default:
//TODO
break;
}
}
...
// the add to selection methods look something like this :
async addToTypesSelection(code) {
if (this.state.jobTypesSelection.includes(code)) {
this.setState({ jobTypesSelection: this.state.jobTypesSelection.filter((item) => item != code) })
}
else {
this.setState({ jobTypesSelection: [...this.state.jobTypesSelection, code] })
}
}
...
Child :
The rendering Part
render() {
const { pressed } = this.state;
return (
< Pressable
// This is the source of the problem and read further to know why I used the setTimeout
onPress={async () => {
this.setState({ pressed: !this.state.pressed });
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
<Badge
bg={pressed ? "primary.300" : "coolGray.200"}
rounded="md"
>
<Text fontSize="md">
{this.props.item}
</Text>
</Badge>
</Pressable >
);
};
Expected outcome :
The setState({pressed:!this.state.pressed}) finishes the rendering of the item happens instantly, the rest of the code happens after and doesn't hang the rendering.
The change in the parent state using the add code to array can happen in the background but I need the filter item ui to change instantly.
Things I tried :
Async methods
I tried making the methods async and not await them so they can happen asynchronously. that didn't change anything and seems like react native ignores that the methods are async. It hangs until everything is done all the way to the method changing the parent state.
Implementing "event emit-listen logic"
This is the first app where I chose to use react/react-native, coming from Vue I got the idea of emitting an event from the child and listening to it on the parent and execute the logic that adds the code to the array.
This didn't change anything, I used eventemitter3 and react-native-event-listeners
Using Timeout
This is the last desperate thing I tried which made the app useable for now until I figure out what am I doing wrong.
basically I add a Timeout after I change the state of the filter component like so :
...
< Pressable
onPress={async () => {
// change the state this changes the color of the item ↓
this.setState({ pressed: !this.state.pressed });
// this is the desperate code to make the logic not hang the rendering ↓
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
...
Thanks for reading, helpful answers and links to the docs and other articles that can help me understand better are much appreciated.
Again I'm new to react/react-native so please if there is some concept I'm not understanding right point me in the right direction.
For anyone reading this I finally figured out what was the problem and was able to solve the issue for me.
The reason the rendering was getting hang is because the code that pushes to my array took time regardless of me making it async or not it was being executed on the main thread AND that change was triggering screen re-render which needed to wait for the js logic to finish.
The things that contribute to the solution and improvement are :
Make the array (now a map{}) that holds the selected filters stateless, in other words don't use useState to declare the array, instead use good old js which will not trigger any screen re-render. When the user applies the filters then push that plain js object to a state or context like I'm doing and consume it, doing it this way makes sure that the user can spam selecting and deselecting the filters without hanging the interactions.
first thing which is just a better way of doing what I needed is to make the array a map, this doesn't solve the rerender issue.

How can I make the native controls on a React Native expo-av Video visible?

I need to know how to assign a function to an expo-av Video component's onPress event, or how to make a useRef reference trigger an onPress event on an expo-av Video component.
I have a lot of requirements for a React Native video component and I am trying to work out how to best achieve them all. The main issue is the user needs to be able to double-tap the video and have an animation fire. We have a Pressable component that renders over the video that handles a double tap. The animation works fine. But the problem is there's also a requirement that the video uses native controls, and the Pressable blocks those controls.
I have two strategies but I haven't been able to achieve either. Plan A, someone on StackOverflow tells me that expo-av Video components have some kind of onPress function not listed in their docs, and all I have to do is trigger the double tap animation using that function. So far I've tried onPress, onTouchEnd, onTouchStart, and onClick, and all are undefined and not listed in the docs. No dice.
Plan B, I can create a Pressable component that renders over the Video component that is short enough not to block the native controls, but tall enough to catch all other taps. Then, when a user double-taps the Pressable, the animation fires and everything's good. This is what I initially submitted as a solution, but our QA folks said they need the native controls to show on the first press. Normally the native controls are not visible and the expo-av Video shows them on any press, but the Pressable blocks those presses, and I cannot find anything in the docs that allows you to referentially trigger the expo-av press event. I have a useRef() working and have tried videoRef.current.press, videoRef.current.click, and videoRef.current.setStatusAsync({ progressUpdateIntervalMillis: 0 }). No dice. I just need the control buttons to show up like they normally do on a tap, nothing else.
So in summary, does anyone A: Know how to assign a function to the expo-av Video component's onPress event? or B: Know how to make a useRef reference trigger an onPress event on an expo-av Video component? or C: Some other solution I haven't thought of?
Here are some code snippets if it helps:
The Video instantiation:
<Video
ref={(video) => videoRef.current = video}
style={[styles.postMedia, { width: videoDimensions.width, height: videoDimensions.height }]}
source={{ uri: videoUrl }}
rate={1.0}
volume={1.0}
isMuted={isMuted}
resizeMode={Video.RESIZE_MODE_CONTAIN}
shouldPlay={shouldPlay || screenIsFocussed && (keyInView === _id)}
isLooping
useNativeControls
onError={(e) => setVideoUrl(`${getS3BaseUrl(video.s3Bucket)}/${video.url}`)}
onReadyForDisplay={(response) => {
const { width, height } = response.naturalSize;
if (width >= height) return setVideoDimensions({ width: screenWidth, height: screenWidth * height / width });
if (height > width) return setVideoDimensions({ height: screenWidth, width: screenWidth * width / height });
}}
/>
The handleDoubleTap function:
let lastTap = null;
const handleDoubleTap = () => {
const now = Date.now();
const DOUBLE_PRESS_DELAY = 300;
if (lastTap && (now - lastTap) < DOUBLE_PRESS_DELAY) {
toggleLike();
} else {
if (videoRef) videoRef.current.setStatusAsync({ progressUpdateIntervalMillis: 0 }); // <-- insert something that actually works here
lastTap = now;
}
};
The Pressable component instantiation:
{(video || image) && (
<Pressable
onPress={handleDoubleTap}
style={video ? {
...styles.videoLikePostFromFeedPressable,
height: IS_IOS ? DEVICE_WIDTH * 0.75 : DEVICE_WIDTH * 0.66,
} : {
...styles.likePostFromFeedPressable,
height: DEVICE_WIDTH,
}}
>
{renderOverlay()} {/* <-- This is just the animation render, works fine */}
</Pressable>
)}

Recording user interactions in React Native / Snap Carousel - firstItem

I have been having this issue for a couple of days and cannot seem to find a solution.
I would like to record the user's interaction on a database. I am displaying data using a React Native Snap Carousel, and need to record if the item was viewed by the user. To do this, and as described in other stack overflow questions, I am using "onSnapToItem". onSnapToItem is triggered everytime I change from one slide to another. Then, if the user views the slide for more than 2 seconds, I count that as an interaction.
The problem is that onSnapToItem is not triggered on the firstItem (which makes sense, because I am not changing slide). Can anybody think of a solution?
<Carousel
vertical
layout={"default"}
ref={_carousel}
data={promos}
renderItem={_renderItem}
autoplay={true}
autoplayInterval={4000}
onSnapToItem = { (index) => {
clearTimeout(writeDelay)
writeDelay=setTimeout (()=>{
console.log(promos[index].id)
},2000)
}}
onEndReached={_retrieveMore}
/>
Programmatically snap to item on screen focus.
componentDidMount() {
setTimeout(() => this._carousel.snapToItem(0), 1000);
// if above code doesn't work then you can try is for first item
writeDelay=setTimeout (()=>{
console.log(promos[0].id); // index here is 0 for first item
},1000)
}
...
render() {
<Carousel
...
ref={c => { this._carousel = c }}
/>
}`
you can use this library #svanboxel/visibility-sensor-react-native

React-google-maps: How do I create a clickable kml map that shows me meta data of the kml?

Edit: According to the guideline, there is a method called getMetadata(). How can I use this to return the props?
Description:
Using the React-google-maps package, I've been able to load Google Maps with my own kml file. That KML file contains multiple shapes each with metadata behind it. What I want is that when a user clicks on one of these shapes, he sees the data behind the shape in for example a pop up.
Example
Let's say I have a Google map with a kml file that shows two countries. The user hovers over one of these countries and sees a pop up that shows which country he's over over. He hovers over the second country and gets the same. When he clicks on the kml shape over the country, he receives more information.
This requires me to do know few things:
- How to create a hover effect over the KML shape that shows data based on the shape
- How to create a click event on the KML shape that shows data based on the shape
However, I fail to understand how I can make this KML file interactive.
This is what I have until now:
import React, { Component } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, KmlLayer } from "react-google-maps"
const MyMapComponent = withScriptjs(withGoogleMap((props) =>
<GoogleMap
defaultZoom={8}
defaultCenter={{ lat: 50.5010789, lng: 4.4764595 }}
>
<KmlLayer
url='https://example.be/kmlfile.kml'
options={{ preserveViewport : false}}
/>
{props.isMarkerShown && <Marker position={{ lat: 50.5010789, lng: 4.4764595 }} />}
</GoogleMap>
))
export default class GoogleMaps extends Component {
render(){
return(
<MyMapComponent
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=MYKEY&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
)
}
}
So I've fixed this my way following a few tutorials. I'm completely ignoring the react-google-maps package and I'm just using plain Javascript. Anyone who's looking for a way to add & switch between KMLayers and to add click and hover actions to it, this is how I did it and is my advice to fellow developers:
TIPS
1: Replace KML by GEOJSON
First of all, I am not using KMLayer but Datalayer now. This allows me more control and has much more documentation on Google. Therefore, you have to convert your KML to a GeoJson. I find that #Mapbox toGeoJSON does a fine job as it also keeps your custom data (which is very important if you want to have more freedom with your data!). Plus they also share their code to integrate in your application so you don't have to convert manually each time.
2: Analyze Google Api Data Layer documentation
Sounds straightforward, but still worth mentioning. As I said, Google shares a lot of information about implementing Data Layer. How to add click and mouseover events, how to style each individual shape and get that specific information, ...
Google doc on Data Layer
3: Replace loadGeoJson() by addGeoJson()
If your applications needs to switch between different Data Layers or simply has to add and remove one, you'll quickly find yourself stuck when using the loadGeoJson(). Hence the addGeoJson(), which will allow you to use map.data.remove() to remove the current Data layer.
Credit: #mensi for his answer on how to remove Data Layer
FINAL CODE
import React, { Component } from 'react';
import { SearchConsumer } from '../App.js';
import Icon from '../library/icons/Icon';
var map = ''
var dataLayer = ''
export default class mapSelection extends Component {
constructor(props){
super(props)
this.onScriptLoad = this.onScriptLoad.bind(this)
}
onScriptLoad() {
// CREATE YOUR GOOGLE MAPS
map = new window.google.maps.Map(
document.getElementById('map'),
{
// ADD OPTIONS LIKE STYLE, CENTER, GESTUREHANDLING, ...
center: { lat: 50.5, lng: 4 },
zoom: 8,
gestureHandling: 'greedy',
disableDefaultUI: true,
});
}
dataHandler = (getJson) => {
// FIRST I REMOVE THE CURRENT LAYER (IF THERE IS ONE)
for (var i = 0; i < dataLayer.length; i++) {
map.data.remove(dataLayer[i])
}
// THEN I FETCH MY JSON FILE, IN HERE I'M USING A PROP BECAUSE
// I WANT TO USE THIS DATAHANDLER MULTIPLE TIMES & DYNAMICALLY
// I CAN NOW DO SOMETHING LIKE THIS:
// onClick(this.dataHandler(www.anotherlinktojsonfile.com/yourjsonfile.json))
// ON EACH BUTTON AND CHOOSE WHICH JSON FILE NEEDS TO BE FETCHED IN MY DATAHANDLER.
fetch(getJson)
.then(response => response.json())
.then(featureCollection => {
dataLayer = map.data.addGeoJson(featureCollection)
// ADD SOME NEW STYLE IF YOU WANT TO
map.data.setStyle({strokeWeight: 0.5, fillOpacity: 0 });
}
);
map.data.addListener('mouseover', (event) => {
map.data.revertStyle();
// ADD A STYLE WHEN YOU HOVER OVER A SPECIFIC POLYGON
map.data.overrideStyle(event.feature, {strokeWeight: 1, fillOpacity: 0.1 });
// IN CONSOLE LOG, YOU CAN SEE ALL THE DATA YOU CAN RETURN
console.log(event.feature)
});
map.data.addListener('mouseout', (event) => {
// REVERT THE STYLE TO HOW IT WAS WHEN YOU HOVER OUT
map.data.revertStyle();
});
}
componentDidMount() {
// LOADING THE GOOGLE MAPS ITSELF
if (!window.google) {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = 'https://maps.google.com/maps/api/js?key=' + process.env.REACT_APP_MAPS_API_KEY;
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
// Below is important.
//We cannot access google.maps until it's finished loading
s.addEventListener('load', e => {
this.onScriptLoad()
this.dataHandler('https://linktoyourjson.com/yourjsonfile.json')
})
} else {
this.onScriptLoad()
}
}
render () {
return (
<div id='mapContainer'>
<div style={{ width: '100%', height: '100%' }} id='map' />
</div>
);
}
};
EXTRA CREDITS
I also want to thank cuneyt.aliustaoglu.biz for his wel explained tutorial in using Google Maps wihout any package.
And thanks to everyone who helped me with a minor problem
QUESTIONS OR SUGGESTIONS?
If there are any questions or if I missed something, you can always ask or tell me and I'll edit this post if necessary.

Resources