Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Downloading picture with text in ReactJS

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
like image 378
Zanam Avatar asked May 18 '19 22:05

Zanam


1 Answers

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

like image 145
Mehmet Kaplan Avatar answered Nov 12 '22 22:11

Mehmet Kaplan