Display small portions of image in flat list - small images are not visible - react-native

I am trying to display small rectangular portions of an image within a flat list - each row in the list contains a small portion of the image with a description. I am given a list of objects that contain a description as well as 4 (x,y) coordinates indicating the top left and bottom right portion of an image (i.e. bounding boxes).
The bounding boxes are all different sizes and in order to fill all available space within each flatlist row I am scaling the image according to the width/height of the given bounding box.
const objectWidth = item.x2 - item.x1;
const objectHeight = item.y2 - item.y1;
const heightScalingFactor = windowHeight / objectHeight;
const widthScalingFactor = windowWidth / objectWidth;
const imageStyle = {
backgroundColor: 'blue',
marginLeft: -item.x1 * widthScalingFactor,
marginTop: -item.y1 * heightScalingFactor,
position: 'absolute',
flex: 1,
//size of image
width: IMAGE_WIDTH * widthScalingFactor,
height: IMAGE_HEIGHT * heightScalingFactor,
};
This solution works fine for most of the bounding boxes but when the bounding box is especially small the image does not show up and is totally blank. I cannot figure out why this is. In the partially working expo example here: https://snack.expo.dev/#melampus123/flatlistwithimages all the images render correctly except for the "right ear" which does not show up at all.
In order to display the small portions of each image I am working off this related stack overflow answer. Please help me find a way to display even small bounding boxes within my flatlist. I found this website useful for marking specific (x,y) points on an image.

Related

React native responsive units

I have been watching Rect Native tutorials on youtube and everyone uses units like padding:20 or fontSize:18 etc
Is that how you do in a professional app too? What is the ideal practice? Is this automatically responsive?
No, this is not automatically responsive. If we set padding:20, then the padding of that component stays on 20 no matter what phone we use. The same holds for font sizes, even though they are scaled depending on what pixel density we are dealing with.
A responsive design must be implemented by hand which could be done by using Dimensions as follows.
import { Dimensions } from 'react-native'
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
We can now use a different padding depending on the width of our device.
padding: width > 320 ? 20 : 15
When it comes to general layouting, then flexbox takes care of responsiveness, e.g. imagine that we have three elements on the screen with flexDirection: 'row' and we set flex:1 for all of them, then the available space will be divided equally between them no matter what our screen width is. The same applies for height.
It might be advised to create a separate Fonts file which holds font sizes and similar constants in a common place, so we only need to change them once.
import { Dimensions } from 'react-native'
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
export Fonts = {
h1: width > 320 ? 18 : 15,
h2: width > 320 ? 16 : 14,
...
}
The above takes care of responsive font sizes and we can use it in all other screens, e.g.
<Text style={{fontSize: Fonts.h1}}>I am a header</Text>

Plotting points on an image and rendering on a different screen size in React Native

So I have an image of a field in which the user is able to plot points by touching their screen. These points are then stored within a database by obtaining the e.nativeEvent.locationX, e.nativeEvent.locationY on the user's press.
So when I plot the point on my Tablet and review where I placed the plot on the field image using my PC, It is displayed incorrectly. I assume this is due to different screen sizes and the image is a different size depending on the device screen size.
How do I resolve this issue so that the plots are consistent no matter what device you are using?
Any other possible solutions are much appreciated.
Below is how I display the image / obtain the user's x-axis and y-axis values and on press these values are retrieved by the function ObtainPosition
<TouchableOpacity onPress={this.ObtainPosition}>
<Image style = {{
width:wp('60%'),
height:hp('100%'),
resizeMode: 'contain'}} source={require('./pitch.png')}/>
</TouchableOpacity>
note - you may see wp and hp this is an import
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
Below is how I display user's plots (Note this is usually on a different screen/component). These plots are retrieved from the database and displayed in an array of objects. As you can see below the .map function loops through the array called PlotsArray and retrieves the x and y values and returns a View which is our plots.
<View>
<Image style = {{
width:wp('60%'),
height:hp('100%'),
resizeMode: 'contain'}} source={require('./pitch.png')}/>
{this.state.PlotsArray.map((data) => {
return (
<View
style={{
position: 'absolute',
left: data.x,
top: data.y,
backgroundColor:'#242424',
width: 10,
height: 10,
borderRadius: 50
}}>
</View>
)
})}
</View>
I think your obtainPosition function will need to take into account the Image element's left and right values (from the window / page) at the same time as you getting the actual x y coordinates of the click (if its padded, or the user is scrolled etc.)
You will also need to get the size of the image when you log the coordinates. So for example have a function similar to:
(event) => {
var {height, url, width} = event.nativeEvent.source;
}
called within the onLoad prop of the image. That way when you store the x, y co-ordinates you can also record the size of the image at the time (say 200px x 200px). Later on, when you render the clicks overlaid on the image, you can asses the size of the image on the 'viewing' device and adjust x y coordinates accordingly or store them as percentages rather than fixed pixels.
Rough example here of how using % on top and left values can adjust to the image varying in size https://jsfiddle.net/skfroxyt/ (have a play with editing the image height and width)

React Native view scaling

So I'm developing a cross platform React Native app, the app is using allot of images as buttons as per design requirements that need to be given an initial height and width so that their aspect ratios are correct. From there I've built components that use these image buttons and then placed those components on the main screen. I can get things to look perfect on one screen by using tops and lefts/ rights to get the components positioned according to the design requirements that I've been given.
The problem I'm running into is now scaling this main screen for different screen sizes. I'm basically scaling the x and y via the transform property on the parent most view as such. transform: [{ scaleX: .8 }, { scaleY: .8 }] After writing a scaling function that accounts for a base height and current height this approach works for the actual size of things but my positioning is all screwy.
I know I'm going about this wrong and am starting to think that i need to rethink my approach but am stumped on how to get these components positioned correctly on each screen without having to hard code it.
Is there any way to position a view using tops and lefts/rights, lock that in place, then scale it more like an image?
First of all, try using flex as far as you can. Then when you need extra scaling for inner parts for example, you can use scale functions. I have been using a scale function based on the screen size and the pixel density, and works almost flawless so far.
import { Dimensions } from "react-native";
const { width, height } = Dimensions.get("window");
//Guideline sizes are based on standard ~5" screen mobile device
const guidelineBaseWidth = 350;
const guidelineBaseHeight = 680;
const screenSize = Math.sqrt(width * height) / 100;
const scale = size => (width / guidelineBaseWidth) * size;
const verticalScale = size => (height / guidelineBaseHeight) * size;
const moderateScale = (size, factor = 0.5) =>
size + (scale(size) - size) * factor;
export { scale, verticalScale, moderateScale, screenSize };
Then you can import these and use in your components. There are different types of scales, you can try and see the best one for your components.Hope that helps.
I ended up going through each view and converting everything that was using a hard coded height and width pixel to setting the width and then using the property aspectRatio and giving that the hard coded height and widths. That along with implementing a scaling function that gave me a fraction of the largest view, so say .9, and then scaling the main view using transform. People arent kidding when they say this responsive ui stuff is tough.
2022 update -
I resolved this problem on my next app by using flex everywhere & a function called rem that I use everywhere that needs a fixed pixel count. With this I can set the width on an image and define an aspect ratio based on the images original dimensions and get an image that scales to the screen size, it's been super reliable.
static width = Dimensions.get("window").width;
static height = Dimensions.get("window").height;
static orientation = 'PORTRAIT';
static maxWidth = 428;
static rem = size => {
let divisor = window.lockedToPortrait || Styles.orientation === 'PORTRAIT' ? Styles.width : Styles.height;
return Math.floor(size * (divisor / Styles.maxWidth))
};
The maxWidth is a predefined value from the largest device I could find to simulate which was probably an iPhone max.

Slower performance on iOS release than development

I've built a React Native iOS app which is fairly basic; it's a few screens which the user can click through to from a 'Home' component, and each one consists of basic components comprising solely Text/View/Image components.
In simulator the app is responsive and there aren't any JS console warnings, however when I do a release to my iPad (Air 2), there's a noticable lag between the home screen and certain other screens. These are notably the screens which have more images on.
I'm wondering if it's because I'm using larger images (the app was designed for the iPad Pro 2) and scaling the images down for use where I want them. The worst offender is a page which has a masonry-style grid of images. There's still only about 30 in a ScrollView there though. Once the component has been shown once the app is much more responsive.
I've already taken steps to optimise my components in terms of using extending PureComponent or using stateless functions wherever possible, and console logging shows me that the touchables are responding immediately, so the delay is definitely at the render time of the component.
N.B. All images are local (loaded via require('./path/to/file')), nothing is being loaded over the network.
Here's an example of the code that populates an array of items for display inside the ScrollView:
...
const items = mediaItems.map((item, index) => {
// scale desired width (1044 for video, 520 for images) based on designed width and device aspect ratio.
const imageWidth = item.video ? (1044/2732) * deviceWidth : (520/2732) * deviceWidth
return (
<TouchableHighlight key={index} onPress={() => onOpen(item)}>
<View style={[styles.gridImageView, { width: imageWidth }]}>
<Image style={styles.gridImage} source={item.image} />
</View>
</TouchableHighlight>
)
});
...
and the styles for the gridImageView and gridImage are as follows:
...
gridImageView: {
height: (460/2732) * width,
margin: (2/2732) * width,
position: 'relative'
},
gridImage: {
resizeMode: 'cover',
width: null,
height: null,
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
...
So my question is kind of multi-layered:
What is the best practice with regards to ensuring the component appears quickly?
Should I not be setting a width/height on the image itself at all?
Should I be doing some kind of pre-loading of the images in the sizes I want before I let the user begin to navigate around the app?
Should I be using JPG instead of PNG images?
Is there another trick I'm missing?
Should I not be setting a width/height on the image itself at all
You have to set a width and height. If you don‘t do so, your image won‘t display
Should I be doing some kind of pre-loading of the images in the sizes I want before I let the user begin to navigate around the app?
It is a good idea to downlod images beforehand. Huge images need a lot of performance. Probably your issues are gone, if you resize your images before displaying them. Therefore you could use react native image resizer
Should I be using JPG instead of PNG images?
You should use JPGs, because they provide a higher compression rate.

Determine the dimensions of a Dojo Widget

My objective is to build a Dojo widget that embeds some graphic information, for example an Analogue Gauge, along with some other stuff.
I have managed to build a widget and initialise the Analogue widget with code such as:
gauge = new dojox.widget.AnalogGauge({
id: "defaultGauge",
width: 300,
height: 200,
cx: 150,
cy: 175,
radius: 125,
Now I've generalised this so that the width, height, cx, cy and radius can be calculated if I know the dimensions in which the widget will be rendered. Say for example, it's going to be in the "top" region of a Border Layout of height 150px, then I can compute suitable values.
The question: how do I determine the available space for my widget to work in? Is there some API by which I can obtain this information from the Layout or Content Pane?
It seems that the widget can provide a
resize(dimensions)
method, which is called both when the widget is first displayed and also when it is resized. The argument being an object holding the width and height of the display area.
Hence if the
gauge = new dojox.widget.AnalogGauge({
code is moved into the resize method we can provide suitable size information to the gauge constructor. I have yet to understand how to deal with subsequent resize() requests.