title: How can I "lock" an HTML canvas's aspect ratio to an image's aspect ratio and allow the canvas to resize in its parent div using ReactJS?
body: <p>I am building a desktop app with ReactJS, Electron, and Material UI.  I have a densely populated UI, as I am managing inventory from a database.  Since I want my app to be resizable, almost every UI element is in a CSS flex box.  I have a parent &quot;Paper&quot; element from Material UI (think of it as a div with a background color) that is resizing correctly.  I want to load an image from the web into that &quot;Paper&quot;, maintaining the image's original aspect ratio while allowing it to grow and shrink with its parent &quot;Paper&quot; div.  From what I've read on Stack Overflow and Google so far, I understand I can use an HTML canvas element to load the image from the web.  This is working properly.  I've also found some CSS from a blog that will scale the image at its original aspect ratio properly, but only in one dimension (width <em>or</em> height, but not both).  This mostly works, except the canvas horizontally overlaps its parent &quot;Paper&quot; div when the window is horizontally narrowed.  How can I make an autoscaling canvas that resizes in both dimensions while not exceeding the bounds of its parent div with ReactJS?  Is there a different component besides a canvas that would accomplish this in an easier way?  I can't set hard values for width and height of the parent div since its in a flex box.  Additionally, I would like the image to be horizontally and vertically centered in its parent div, which is not currently working.  This is what I have so far:</p>
  <p>In my CSS file for the class:</p>
  <pre><code>canvas {
    position: absolute;
    height: 100%;
    width: auto;
    top: 0;
    left: 0;
  }
  
  .styled-paper {
    textAlign: 'center',
    backgroundColor: 'rgba(49, 51, 55, 1)',
    position: 'relative',
    width: '100%',
    /* this is to vertically align this div with two smaller divs next to it */
    height: '87%',
    border: 1,
    borderColor: 'rgba(108, 109, 112, 1)',
    borderRadius: 8,
    borderStyle: 'solid',
    borderWidth: '1px',
  }
  </code></pre>
  <p>In the TSX file for the class:</p>
  <pre><code>import React, { Component } from 'react';
  import Paper from '@material-ui/core/Paper';
  import TextField from '@material-ui/core/TextField';
  import { isNull, isUndefined } from 'underscore/modules/index';
  
  function isBlank(str: string | undefined) {
    return (isUndefined(str) || isNull(str) || str.trim().length === 0);
  }
  
  async function LoadImageFromUrl(url: string): Promise&lt;string&gt; {
  
    //URL shortening for database
    let shortenedURL: string;
    if (url.startsWith(&quot;https://tinyurl.com/&quot;)) {
      shortenedURL = url;
    } else {
    //Other async function that shortens URL with TinyURL
      shortenedURL = await ShortenURL(url);
    }
  
    //Get reference to canvas and its context
    let canvas = document.getElementById(&quot;itemImageCanvas&quot;) as HTMLCanvasElement;
    let context = canvas.getContext(&quot;2d&quot;);
  
    //
    let itemImg = new Image();
    itemImg.onload = function() {
      var wrh = itemImg.width / itemImg.height;
      var newWidth = canvas.width;
      var newHeight = newWidth / wrh;
      if (newHeight &gt; canvas.height) {
        newHeight = canvas.height;
        newWidth = newHeight * wrh;
      }
      context!.drawImage(itemImg, 0, 0, newWidth, newHeight);
    }
    itemImg.src = shortenedURL;
    return shortenedURL;
  }
  
  export default class ItemInfoEditPane extends Component&lt;any, any&gt; {
  
    state = {
      imageURL: '',
    }
  
    onChange = (event: any) =&gt; this.setState({ [event.target.name] : event.target.value });
    
    onImageURLChange = (newUrl: string) =&gt; this.setState({imageURL: newUrl});
  
    updateImageURL = (callback: Function) =&gt; {
      LoadImageFromUrl(this.state.imageURL).then(function(result) { callback(result) });
    }
  
    render() {
  
      const {
        imageURL,
      } = this.state;
  
      return(
  
        //Lots of parent elements and divs/flex box divs I removed for clarity that this block is nested in
  
                  &lt;div className=&quot;hRow textField triple&quot;&gt;
                    &lt;Paper className=&quot;styled-paper&quot; elevation={0}&gt;
                      &lt;canvas id=&quot;itemImageCanvas&quot;/&gt;
                    &lt;/Paper&gt;
                  &lt;/div&gt;
  
             //Other elements between these two, but this text field is where I grab the image URL from
  
                  &lt;div className='hRow textField'&gt;
                    &lt;TextField name=&quot;imageURL&quot; label=&quot;Image URL&quot; value={imageURL} onChange={this.onChange}
                                     onBlur={() =&gt; { if (!isBlank(imageURL)) { this.updateImageURL(this.onImageURLChange) }}}/&gt;
                  &lt;/div&gt;
  
      )
    }
  }
  </code></pre>