[PWA] Access the Camera in a PWA built with React

It's possible to access some, but not all, of the native device features from a PWA. One that we canaccess, is the camera and camera roll. We'll use two different methods to allow users to upload images to our application.

First, we'll add a file input that accepts images. We can specify a capture param, which will tell PWAs on mobile devices to prompt the user to take a picture with their camera.

Next, we'll access the camera feed directly by rendering a video tag, and accessing navigator.mediaDevices.getUserMedia directly - which will allow us to show a live video feed from the camera. Then, we can use a canvas tag to grab a frame from that video, and render the image.

class Profile extends Component {

  state = {
    image: null,
    supportsCamera: 'mediaDevices' in navigator
  }

  changeImage = (e) => {
    this.setState({
      image: URL.createObjectURL(e.target.files[0])
    })
  }

  startChangeImage = () => {
    this.setState({ enableCamera: !this.state.enableCamera })
  }

  takeImage = () => {
    this._canvas.width = this._video.videoWidth
    this._canvas.height = this._video.videoHeight

    this._canvas.getContext('2d').drawImage(
      this._video,
      0, 0,
      this._video.videoWidth,
      this._video.videoHeight
    )

    this._video.srcObject.getVideoTracks().forEach(track => {
      track.stop()
    })

    this.setState({
      image: this._canvas.toDataURL(),
      enableCamera: false
    })
  }

  render() {
    return (
      <div>
        <nav className="navbar navbar-light bg-light">
            <span className="navbar-brand mb-0 h1">
              <Link to="/">
                <img src={Back} alt="logo" style={{ height: 30 }} />
              </Link>
              Profile
            </span>
        </nav>

        <div center' }}>
          <img 
            src={this.state.image || GreyProfile} alt="profile" 
            style={{ height: 200, marginTop: 50 }} 
          />
          <p #888', fontSize: 20 }}>username</p>

          {
            this.state.enableCamera &&
            <div>
              <video 
                ref={c => {
                  this._video = c
                  if(this._video) {
                    navigator.mediaDevices.getUserMedia({ video: true })
                      .then(stream => this._video.srcObject = stream)
                  }
                }}
                controls={false} autoPlay
                100%', maxWidth: 300 }}
              ></video>

              <br />

              <button
                onClick={this.takeImage}
              >Take Image</button>

              <canvas 
                ref={c => this._canvas = c}
                none' }}
              />

            </div>
          }

          <br />
          {
            this.state.supportsCamera &&
            <button
              onClick={this.startChangeImage}
            >
              Toggle Camera
            </button>
          }
        </div>

      </div>
    )
  }

}

Video, Code