How to add text to an image in React Native? - javascript

I'm researching if it is easily possible with React Native or whether I should build a native app.
I want to edit an image from the photo library and add a text overlay to it. Think of it like a postcard with a greeting message on it.
How would I add text to and image and make a new copy of it in react native? I'm not looking for detailed code, just for an explanation on how to get started.
Update:
Would it be a good alternative to just save the coordinates of the message on the picture instead of generating a new image?

You can go with 2 ways. You can either render the text on an image component and save the position of that text or you can process the image and get a new image with the text.
The first option brings up the problem of that position is relative to image's size. Means that if the image renders on a different sized screen the position and size of text should also be moved accordingly. This option needs a good calculation algorithm. Also, another problem could be the rendering times. Text component will render instantly but Image component needs to load the image first. This option also needs a good way of render algorithm.
The second option is not possible without a 3rd party library or some level of native code since react-native doesn't support image processing beyond the limits of CSS. A good and maintained image processing library is gl-react-native-v2. This library helps you to process and manipulate the image as you wish and then save the result with captureFrame(config). This option is more capable of processing file but needs you to save a new image.
Either way is good if the way you go is appropriate for your use case. The decision really depends on your case and preference.

You could use react-native's ImageBackground tag since using the Image tag as a container would give you a yellow box warning.
The sample code for it with overlay is as shown below
<ImageBackground source={SomeImage} style= {SomeStyle} resizeMode='SomeMode'>
{loader}
</ImageBackground>
It would be efficient if you work on the same image by changing the flex property in the styles of the image or you may set the position: absolute to the main image container and assign top , bottom, left, right to the nested container.
Helpful link may be found here

<View style={{flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: 20}}>
<Image
style={{
flex: 1,
width:100,
height:100,
}}
source={require('../imgs/star.png')}
/>
<Text style={{position: 'absolute', fontSize: 20}}>890</Text>
</View>

I'm researching if it is easily possible with React Native or whether I should build a native app.
This sounds very doable in react-native
I want to edit an image from the photo library and add a text overlay to it. Think of it like a postcard with a greeting message on it.
I think I would capture ALL the relevant metadata as an object...
{
image: {
uri: './assets/image.jpg',
height: 576,
width: 1024
},
message: {
fontFace: 'Calibri',
fontSize: 16,
text: 'Happy Holidays!'
boundingBox: {
height: '30%',
width: '30%',
top: 0,
left: 0
}
}
}
With the above detail (and maybe more), you'd then be able to reconstruct the design intent on any device, regardless of size (tablet vs mobile) or pixel depth. The boundingBox would be expressed in terms relative to the image's rendered dimensions. In the above example, the message would be contained in a text box no more than 30% of the image width, 30% of its height, and positioned in the top-left corner.
How would I add text to an image and make a new copy of it in react native?
This eliminates the need to do any screenshotting or actual image manipulation, etc. No need to "make a new copy". Just treat them as two separate and distinct assets, then merge them at render using the metadata you captured.
Final thought: if you "need" to "upload the finished photo to a server" as you stated in another comment to another solution, you can do this serverside using any number of technologies using the metadata as your guide.

You can use position: absolute since adding children to Image is deprecated. Following code will align text in middle (vertically and horizontally) over the image:
<View>
<Image
source={yourImageUrl}
resizeMode={'cover'}
style={{
width: 300,
height: 300
}}
/>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 300, alignItems: 'center', justifyContent: 'center' }}>
<Text>Your overlay text</Text>
</View>
</View>

If you need to only show a text over the image you can wrap the image in a view and after the image insert a text element with position: 'absolute'. If you want a copy of the image containing the text then you can use the same approach but take a snapshot using react-native-view-shot

As far as I know, the image component can be used as a container so you can do something like this:
<Image>
<Text>{"some text"}</Text>
</Image>

Below code is what I used to add text on image at particular coordinates on image.
Xcode 8.3.2 Swift 3.1
func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
let textColor = UIColor.white
let textFont = UIFont(name: "Helvetica Bold", size: 10)!
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
] as [String : Any]
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
let rect = CGRect(origin: point, size: image.size)
text.draw(in: rect, withAttributes: textFontAttributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
And call above method as below
let imageWithText = textToImage(drawText: "textOnImage", inImage:yourImage, atPoint: CGPoint(x:9,y:11))

There are several libraries available for that. If you want to add a text to an image you can use: react-native-media-editor. This is quite a good option for adding text to an image but I recommend you react-native-image-tools. It provides so much flexibility. You can add a text and position it accordingly. Apart from that, there are filters, cropping option, light adjustments and so much more.

It depends on the use case but if you simply want to put a text on an image, using ImageBackground is a one of the good approach.
Do it like below.
<ImageBackground
source={{ uri: hoge }}
style={{
height: 105,
width: 95,
position: 'relative', // because it's parent
top: 7,
left: 5
}}
>
<Text
style={{
fontWeight: 'bold',
color: 'white',
position: 'absolute', // child
bottom: 0, // position where you want
left: 0
}}
>
hoge
</Text>
</ImageBackground>

Related

React Native Expo Image Resizing

I'm using react native expo to display images of various subjects. The images I'm using have a variety of widths, heights and aspect ratios. The images themselves come from google firebase storage and are retrieved from the currently selected subjects JSON data which contains the firebase storage Image URL.
Some subjects have no images at all, which is fine however I want the content below to therefore move up, so a fixed size/aspect ratio image or view in this case is not appropriate. And as I said Images are of various heights and widths (some landscape / some portrait.
Currently the styling for the image I'm attempting is like so:
Image: {
height: null,
width: "90%",
alignSelf: 'center',
resizeMode: 'contain'
},
Currently with the height set to null the image is not shown at all, and the content below sits above.
The width I want fixed at 90% of the screen's width which it currently is doing, however I'm having issues with the height. I've tried setting the height at 'undefined' and also percentages '40%' etc however this is not appropriate for the subjects missing images.
Another way I have tried is placing the image inside a view like so:
<View style={styles.ImageContainer}>
<Image style={styles.Image} source={{ url: subject.ImageUrl }}/>
</View>
...
// Styling:
Image: {
height: null,
width: "100%", // 100% of the image container which is 90% of screen.
alignSelf: 'center',
resizeMode: 'contain'
},
ImageContainer: {
width: '90%',
alignSelf: 'center',
justifyContent: 'center'
}
What I really want to achieve is setting the image container to the size of the image height, which is resized so it fills 90% of the screens width (without skewing/stretching the image).
Is there are way of doing this within the styling or do I have to attempt using a function? I'm sure I have had success achieving this through styling in the past.
You could use a minHeight for the image to make sure it shows, it should use the actual image height but setting a minimum will allow it to display.
OR
You could use Image.getSize() and use the exact height of the image. However, Image.getSize() is an async function so you would have to await it to get the size until you can display the image.

How can you ensure flex shrink in react-native (and how can you debug react-native styles in general)?

I'm running a RN app using native-base for styling. I have four elements on my homepage: a header, a tab view from vreact-native-tab-view that contains a ScrollView that takes up about 70% of the viewport, and two smaller 100% width elements at the bottom.
However, the scrollview fills up more than 100% of the viewport and the two smaller elements get pushed to the bottom.
I looked in my element inspector and can apply a flexShrink to some of the many divs, but I'm not sure which one that is in my code because it's div hell when you use react-native. React devtools also has the same problem, except it's View hell.
So, two questions:
How can I ensure the scroll container doesn't fill up more than it should on the page?
How can I effectively debug react native when both chrome and react dev tools are a mess?
For reference, here's how I've styled so far:
Home.tsx:
<View flex={1}> // applied a flex for the entire homepage
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
renderTabBar={renderTabBar}
onIndexChange={setIndex}
initialLayout={{ width: layout.width, height: "100%" }}
/>
<View width="100%">
<Center width="100%">
<StrengthSlider />
</Center>
<AdMobBanner
...props
/>
</View>
</View>
Teas.tsx:
<View flex={1} bg="white">
<ScrollCards teas={blackTeas} />
</View>
ScrollCards.tsx:
<ScrollView>
<Center>
{teas.length > 0 &&
teas.map((teaObj) => (
<TeaCard id={teaObj.id} teaData={teaObj.data} key={teaObj.id} />
))}
</Center>
</ScrollView>
EDIT:
Code Sandbox link: https://codesandbox.io/s/gracious-sammet-l4tqz?file=/src/App.js&resolutionWidth=402&resolutionHeight=675
Note that the admob footer remains underneath the cards content. It should be sticky and remain always at the bottom of the screen. I also noticed that when I'm not using the header from the MainStackNavigator the footer works as intended - i.e. it remains sticky at the bottom - but I don't see why using the header (AppBar) component should interfere with my footer.
The proper solution to get the result you want is to add a flexBasis to the TabView like:
<TabView
style={{ flexBasis: 0 }}
// rest of the code
/> ​
Why?
The TabView has a default style of flex: 1, overflow: 'hidden' (see source code) causing it to expanded to the size of its biggest child. The flexBasis prevents this and makes sure the tabview get the correct height.
Resource: This is a nice article about flexBasis vs width/height: https://mastery.games/post/the-difference-between-width-and-flex-basis/
Debugging styling in React-Native doesn't have the best developer experience. There are some things you can use to help you with debugging styling:
RN inspector: As Berci mentioned, React native has a dev menu where you can select "show inspector" that kinda acts like "inspect element" in a browser. It is a good tool to debug elements you can see, it also helps with debugging input/tab events.
Color: Most often I just use old fashioned colored borders & background to get a clear view of where elements are & their size/overlaps.
Comments & Simplify: Feel free to comment out components you're not interested in and replace complex components/views like ScrollCards with just a simple colored view. This can help prevent multiple behaviours from influencing the thing you're trying to debug
Browser inspect & React devtools: If you happen to run your RN app in the browser, then getting familiar with those tools will help you loads. Just keep in mind that React & React-Native isn't the same.
When you debug visuals, the best way is start at the top layer and work you way down. Color the elements & feel free to comment out elements to get you a clearer view of the problem. Keep digging down until you find the problem, don't be afraid to look into the source code of the packages you use, it often helps clarify (unexpected) behaviours.
You could try to replace
<View flex={1}> // applied a flex for the entire homepage
in Home.tsx with
<View flex="1 1 0">
Working example here (is your code with that line modified).
As for debugging I can only suggest what is already suggested on react native Debugging page. Have you tried React Developer Tools yet? In chrome you can also add it as an extension from this link.
I am not sure if there is any difference between chrome extension and the npm package but the extension was enough for me at least (for now).
The only problem is that I also struggled debugging on codesandbox since the extension is definitely not working on javascript online editors (but probably the npm package doesn't either for this specific case)
Try this - https://snack.expo.dev/fL0OgQ9uS
import React from 'react';
import { View, Image, StyleSheet,ScrollView,Text } from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 50,
flex:1,
backgroundColor:'aqua',
},
tinyLogo: {
width: 200,
height: 200,
},
header:{
backgroundColor:'green',
paddingBottom:20,
},
footer:{
backgroundColor:'green',
paddingTop:20,
}
});
const DisplayAnImage = () => {
return (
<View style={styles.container}>
<View style={styles.header}><Text>header</Text></View>
<ScrollView>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
<Image
style={styles.tinyLogo}
source={require('#expo/snack-static/react-native-logo.png')}
/>
</ScrollView>
<View style={styles.footer}><Text>footer</Text></View>
</View>
);
}
export default DisplayAnImage;

Custom styling for MenuOption in React native popup menu

So I'm new to React (and JavaScript too for that matter). I'm creating an App using react native and currently trying to style my popup menu. (which looks like this: Popup menu image)
I want to change the style of the options (make the font size bigger and space them out and change the font color too). My code looks something like this:
<MenuProvider>
<Menu >
<MenuTrigger>
<Image
style={styles.menucontainer}
resizeMode="contain"
source={require('../assets/icon_more.png')}>
</Image>
</MenuTrigger>
<MenuOptions optionsContainerStyle={styles.optionsstyle}>
<MenuOption text= 'About' />
<MenuOption text= 'Help & Feedback'/>
<MenuOption text= 'Sign Out'/>
</MenuOptions>
</Menu>
</MenuProvider>
After checking
https://github.com/instea/react-native-popup-menu/blob/master/src/MenuOption.js
I found a prop customStyles. Just like I passed a styling object for MenuOptions as prop optionContainerStyle, I tried passing customStyles for MenuOption but that produced an error:
In this environment the sources for assign MUST be an object. This error is a performance optimization and not spec compliant.
Here is my styles code:
const styles = StyleSheet.create({
optionsstyle:{
marginTop: height*32/dev_dimension.h,
marginLeft: width*184/dev_dimension.w,
backgroundColor: '#fafafa',
width: width*168/dev_dimension.w,
height: height*160/dev_dimension.h,
flexDirection: 'row',
flex: 1,
justifyContent: 'space-between',
},
});
Can anyone tell what I'm doing wrong?
According to documentation the optionsContainerStyle are deprecated and I am not sure if they work properly. Try to use customStyles props instead as seen in StylingExample where you can find full example.
The thing is that customStyles is map of styles for different parts. Something like
<MenuOptions customStyles={{optionWrapper: { padding: 5}, optionText: styles.text}}>

Centering TextInput with dynamic width in React Native

I am trying to center a text input with a width that is the length of the text input. However when I use alignSelf: 'center', or alignItems: 'center', the text input is not visible without a width.
For example:
render() {
return <View style={{flex: 1}}>
<TextInput style={{alignSelf: 'center', minWidth: 1}}>
<View/>
}
Here the minWidth ensures that the textInput can be seen but it does not expand when you type in it. And without a width / minWidth the textInput would not be seen unless the centering style was removed.
Example with almost workable workaround:
constructor(props) {
super(props)
this.state = {
txt: ""
txtWidth: 0
}
}
render() {
return <View style={{flex: 1}}>
<TextInput
style={{minWidth: 1, alignSelf: 'center', width: this.state.txtWidth}}
value={this.state.txt}
onChange={txt=>this.setState({txt: txt.nativeEvent.text})}
/>
<Text
style={{position: 'absolute', right: 100000}}
onLayout={e=>this.setState({txtWidth:e.nativeEvent.layout.width})}
>
{this.state.txt}
</Text>
<View/>
}
As the text input receives input, it grows in size and works great. However, one thing prevents it from being full proof. The e.nativeEvent.layout.width value for emoji is always 20. And the actual width of the given emoji is not 20. Thus the txtWidth is no longer the correct width for the textInput and pieces of the textInput are now cut off.
Has anyone come up with a good solution for a centered text input with a dynamic width. I have been stumped on this for way too long. Would be happy to provide clarity if needed as well.
Thanks!
Sorry kept you waiting, fininally got time to finish this. Use View,Image and Text to fake TextInput.
1st step: format text when changed
you still need a TextInput.
emoji in text might look like :fire: or 🔥, I choose the later.
when text changes use regexp replace all emoji expressions to emoji character.
then you move cursor it will not stop inside an emoji
2nd setp: faked TextInput
it should look like
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
<Image style={styles.img} source={{uri: some_emoji}} />
<Text>
{"s"}
</Text>
<Text>
{"s"}
</Text>
<MyCursor />
</View>
the emoji images should be within the same width, and to get width of Text might look like
<Text onLayout={(event) => {
var {x, y, width, height} = event.nativeEvent.layout;
}} />
then you can get the total length and resize your View and hide your real TextInput and manage cursor, It worked in the web though not that smooth.
this is why i use Text for every char like <Text>{"s"}</Text> cursor index should change when you click on each of them, and when focus, blur and keypress happens. you may refer https://github.com/postor/react-input-emoji/blob/master/pages/index.js for a web version fake input I've tried, yet cut, paste and lot's of things remain unhandled

I keep getting a 404 error when I try to add a background image to a div in jQuery

I am trying to create a div as I press enter in my search bar. That part works for me and if I keep pressing enter a new div is created. However, I cannot give the created div a background image. My console keeps giving me a 404 error. Here is my code:
function appendToDom(poster) {
var aPoster = $('<div>');
aPoster.css({
display: 'inline block',
float: 'left',
margin: '10px',
marginLeft: '37px',
marginTop: '20px',
width: '200px',
height: '300px',
fontSize: '36px',
color: 'black'
//backgroundImage: '../services/images/1425663956-outline.png'
})
$(aPoster).css("background-image", "url(~/desktop/MyMovies/public/js/images/1425663956-outline.png)");
$(main).append(aPoster);
I tried to put my images file in a bunch of places and I changed the path a bunch of times. My ultimate goal is to add the image provided by the api I'm using as the background image of each div and have the image I am currently trying to use as the default for when the api has no image for a given data point.
I am working in node if that helps.
I find it easier/better to use relative directories. Similar to how you have in your commented out section of your example. In my opinion, files or assets that the program will use should be somewhere inside your project folder anyway.
Try url(./../images/question.png)depending on your file system.
Can you try like this:
$(aPoster).attr("style","background-image: url(~/desktop/MyMovies/public/js/images/1425663956-outline.png)");
The browser doesn't understand the ~ in the file path ~/desktop/MyMovies/public/js/images/1425663956-outline.png. It only has meaning in a shell with access to your local filesystem. You need to use the URL where that image file is being served.

Categories

Resources