Reputation: 4807
I created a react app for a tutorial and it writes text over an uploaded image. The user after image upload types in Top Box
and Bottom Box
but when I try to save the image after the user has typed text, no text shows up. Can anyone please help as I want the image to be downloaded with text.
The files are Header.js
with code:
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import CardMedia from '@material-ui/core/CardMedia';
import { withStyles } from '@material-ui/core/styles';
import CardActionArea from '@material-ui/core/CardActionArea';
import { Card, CardContent } from "@material-ui/core";
import IconButton from '@material-ui/core/IconButton';
import PhotoCamera from '@material-ui/icons/PhotoCamera';
const styles = theme => ({
media: {
display: 'flex',
height: 400,
objectFit: 'contain',
alignItems: 'center',
},
})
class Header extends Component {
render() {
const {classes} = this.props;
return(
<Card >
<CardContent >
<div style={{position:'relative'}}>
<CardMedia
component="img"
image = {this.props.imageIn}
/>
<div style={{position: 'absolute', color: 'white', top: '5%', left: '50%', transform:'translateX(-50%)'}}>
{this.props.topText}
</div>
<div style={{position: 'absolute', color: 'white', bottom: '5%', left: '50%', transform:'translateX(-50%)'}}>
{this.props.bottomText}
</div>
</div>
</CardContent>
</Card>
);
}
}
export default withStyles(styles)(Header)
and App.js
as:
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import CardMedia from '@material-ui/core/CardMedia';
import { withStyles } from '@material-ui/core/styles';
import CardActionArea from '@material-ui/core/CardActionArea';
import { Card, CardContent } from "@material-ui/core";
import Header from './Header';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import IconButton from '@material-ui/core/IconButton';
import PhotoCamera from '@material-ui/icons/PhotoCamera';
const styles = (theme) => ({
input: {
display: 'none'
}
});
class App extends Component{
state = {
topText: '',
bottomText: '',
randomImg: "http://i.imgflip.com/1bij.jpg",
allImages: [],
images: []
}
handleChange = (event) => {
const {name, value} = event.target
this.setState({ [name]: value })
}
handleCapture = ({ target }) => {
const fileReader = new FileReader();
const name = target.accept.includes('image') ? 'images' : 'videos';
fileReader.readAsDataURL(target.files[0]);
fileReader.onload = (e) => {
this.setState((prevState) => ({
[name]: [...prevState[name], e.target.result]
}));
};
};
render() {
return(
<div>
<input
accept="image/*"
style={{display: 'none'}}
id="icon-button-photo"
onChange={this.handleCapture}
type="file"
/>
<label htmlFor="icon-button-photo">
<IconButton color="primary" component="span">
<PhotoCamera />
</IconButton>
</label>
<Grid container justify="left" spacing={8}>
<Grid item xs={12} sm={6}>
<TextField
id="standard-name"
label="Top Text"
name = "topText"
value={this.state.topText}
onChange={this.handleChange}
margin="normal"
variant="filled"
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="standard-name"
label="Bottom Text"
name = "bottomText"
value={this.state.bottomText}
onChange={this.handleChange}
margin="normal"
variant="filled"
/>
</Grid>
<Header topText = {this.state.topText} bottomText = {this.state.bottomText} imageIn = {this.state.images}/>
</Grid>
</div>
)
}
}
export default App
Upvotes: 7
Views: 4200
Reputation: 2352
In your code the texts are distinct elements (childs of the div
's in below code):
<CardMedia
component="img"
image = {this.props.imageIn}
/>
<div style={{position: 'absolute', color: 'white', top: '5%', left: '50%', transform:'translateX(-50%)'}}>
{this.props.topText}
</div>
<div style={{position: 'absolute', color: 'white', bottom: '5%', left: '50%', transform:'translateX(-50%)'}}>
{this.props.bottomText}
</div>
</div>
Rather you may try to incorporate the text into image like:
const canvas = this.refs.canvas;
const ctx = canvas.getContext("2d");
const img = this.refs.image;
img.onload = () => {
ctx.drawImage(img, 0, 0);
ctx.font = "40px Courier";
ctx.fillText(this.props.text, 210, 75); // THIS IS THE PLACE TEXT IS EMBEDDED INTO THE PICTURE
};
In this code sandbox link, https://codesandbox.io/s/blazing-sun-e2bgf , you can find a simple class named Canvas
which uses above method so that when the file is saved you can see the text.
One catch here, the example I provided is free from material UI. You are using CardMedia
class which takes the image and shows it. But in the example I provided, the method is generic. So you can place the image seperately in the card.
Credit: https://blog.cloudboost.io/using-html5-canvas-with-react-ff7d93f5dc76
Upvotes: 6
Reputation: 154
What you're asking is not possible with a DOM manipulation library such as React. You're trying to do some image edition and rendering in a browser with a framework that is made to handle DOM.
You can use React for the "preview", but when it's time to actually render the image, you'll need a tool such as https://imagemagick.org/index.php. You can find the node binding here : https://www.npmjs.com/package/gm
Upvotes: 0
Reputation: 99
I’m fairly new to it all but if you you want to lay text over the image you may need to use the z index. Z-index: 1; would be the back div (photo or image) Z-index:2; would be the would be the div with the text over the top. Aswell as alpha opacity to set the div with the text to transparent.
And using absolute position will ensure they are both positioned on top of each other.
Upvotes: 0