I am fairly new to React and I am developing an app which will take actual screenshots of a web page and the app can draw and add doodles on top of the screenshot taken. I initially used html2canvas and domToImage to take client-side screenshots but it doesn't render the image exactly as it is shown in the web page.
Reddit user /pamblam0 suggested I look into Google's Puppeteer. How it works is that it launches a headless chromium browser which goes to my react app on localhost then gets a screenshot of that whole page easily. My problem however, is that puppeteer doesn't play nice inside a react app. It gives me a ws error which, as explained on a google search can be fixed by simply installing ws (which doesn't work by the way).
What happens now my puppeteer script works out my react app. From what I understand it doesn't work with client side app (I might be wrong). What I want to happen is that whenever I click the button from my react app, puppeteer should execute and return a base64 string which will then be passed to a component in my react app.
Here is what I've done so far.
puppeteerApp.js
const puppeteer = require('puppeteer');
const takeScreenshot = async () => {
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
const options = {
path: 'saved_images/webshot.png',
encoding: 'base64'
}
await page.goto('http://localhost:3000/', { waitUntil: 'networkidle2' });
const elem = await page.$('iframe').then(async (iframe) => {
return await iframe.screenshot(options)
});
await browser.close()
});
}
takeScreenshot();
Code from react app.
App.js
import React, { Component } from 'react';
import ScreenshotsContainer from './containers/ScreenshotsContainer/ScreenshotsContainer'
import ImageContainer from './containers/ImageContainer/ImageContainer';
import html2canvas from 'html2canvas';
import domtoimage from 'dom-to-image';
import Button from './components/UI/Button/Button'
import classes from './App.module.css';
import { CSSTransition } from 'react-transition-group'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
imgURIArray: [],
img: null,
showImageContainer: false,
showScreenshotContainer: false,
selectedImageURI: null,
showSaveAnimation: false,
showNotify: false
}
}
storeImageToArrayHandler = (imgURI) => {
if (imgURI !== "") {
this.setState({ imgURIArray: [...this.state.imgURIArray, imgURI] }, () => {
this.setState({ showImageContainer: !this.state.showImageContainer })
})
}
}
getScreenshotHandler = () => {
//use puppeteer here!!!
}
getSelectedImageFromContainerHandler(selectedImageURI) {
this.setState({
selectedImageURI: selectedImageURI,
showImageContainer: !this.state.showImageContainer
})
}
showImageContainerHandler(showImageContainer) {
this.setState({ showImageContainer: showImageContainer })
}
showScreenshotContainerHandler = () => {
this.setState({ showScreenshotContainer: !this.state.showScreenshotContainer })
}
notify = (submitSuccessful, msg) => {
let message = msg ? msg : ""
submitSuccessful ?
toast.success(message, {
autoClose: 3000,
position: toast.POSITION.TOP_CENTER
})
:
toast.error(message, {
position: toast.POSITION.TOP_CENTER
});
}
render() {
let buttonOps = (
<CSSTransition
in={!this.state.showScreenshotContainer}
appear={true}
timeout={300}
classNames="fade"
>
<div className={classes.optionButtons}>
<Button icon={"fas fa-camera"} type={"button-success"} gridClass={""} buttonName={""} style={{ width: "100%", height: "70px" }} onClick={() => this.getScreenshotHandler} />
<Button icon={"fas fa-images"} type={"button-primary "} gridClass={""} buttonName={""} style={{ width: "100%", height: "70px" }} onClick={() => this.showScreenshotContainerHandler} />
</div>
</CSSTransition>
)
return (
<div>
{
this.state.showImageContainer ?
<div>
<ImageContainer
img={this.state.img}
showImageContainer={showImageContainer => this.showImageContainerHandler(showImageContainer)}
storeImageToArrayHandler={imgURI => this.storeImageToArrayHandler(imgURI)}
notify={(submitSuccessful, msg) => this.notify(submitSuccessful, msg)}
/>
</div>
: null
}
<CSSTransition
in={this.state.showScreenshotContainer}
appear={true}
timeout={300}
classNames="slide"
unmountOnExit
onExited={() => {
this.setState({ showScreenshotContainer: false })
}}
>
<ScreenshotsContainer
imgURIArray={this.state.imgURIArray}
getSelectedImageFromContainerHandler={imgURI => this.getSelectedImageFromContainerHandler(imgURI)}
showScreenshotContainerHandler={() => this.showScreenshotContainerHandler}
notify={(submitSuccessful, msg) => this.notify(submitSuccessful, msg)}
/>
</CSSTransition>
{this.state.showImageContainer ? null : buttonOps}
{/* <button onClick={this.notify}>Notify !</button> */}
<ToastContainer />
</div >
);
}
}
export default App;
Any help would be appreciated. Thanks!
See Question&Answers more detail:
os