JSFiddle - React, Tailwind, and code Playground

by Kanatantsin

HTML

<div class="layout" id="layout">
  <h2 class="layout__title">iLayout</h2>
  <div class="layout__hint">Перетащите изображения в соответстующие части коллажа, нажмите на кнопку «Получить коллаж» и наслаждайтесь магией!</div>
  <div class="layout__positions">
    <div class="layout__item layout__item_left"></div>
    <div class="layout__item layout__item_top"></div>
    <div class="layout__item layout__item_bottom"></div>
  </div>
  <div class="layout__controls">
    <button class="layout__button">Получить коллаж</button>
  </div>
  <textarea class="layout__result" readonly placeholder="Здесь будет размещён HTML-код вашего коллажа"></textarea>
</div>

CSS

body {
  margin: 0;
  font-family: Helvetica;
}
.layout {
  padding: 10px;
  box-sizing: border-box;
}
.layout__hint {
  margin: 10px 0;;
}
.layout__positions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  grid-template-areas:
    'left top'
    'left bottom';
  height: 50vh;
}

.layout__item {
  box-sizing: border-box;
  overflow: hidden;
  position: relative;
}
.layout__controls {
  margin: 10px 0;
}
.layout__button {
  border-radius: 6px;
  background: blue;
  color: #fff;
  border: none;
  height: 40px;
  width: 200px;
  cursor: pointer;
  font-size: 14px;
  text-transform: uppercase;
}
.layout__result {
  width: 100%;
  height: 40px;
}
.layout__image {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
}

.layout__item_active {
  border: 4px dashed black;
}
.layout__item_left {
  grid-area: left;
  background: rgb(228, 228, 228);
}

.layout__item_top {
  grid-area: top;
  background: rgb(186, 196, 255);
}

.layout__item_bottom {
  grid-area: bottom;
  background: rgb(149, 188, 160);
}

JavaScript

const addClass = ( className, context ) => context.classList.add( className ),
      removeClass = ( className, context ) => context.classList.remove( className ),
      hasClass = ( className, context ) => context.classList.contains( className );

class iLayout {
  constructor( container ) {
    this.container = container;
    this.positionsContainer = container.querySelector( '.layout__positions' );
    this.actionButton = container.querySelector( '.layout__button' );
    this.result = container.querySelector( '.layout__result' );
    this.layout = {
      left: document.querySelector('.layout__item_left'),
      top: document.querySelector('.layout__item_top'),
      bottom: document.querySelector('.layout__item_bottom')
    };
    this.canvas = document.createElement('canvas');
    this.canvas.width = this.positionsContainer.getBoundingClientRect().width;
    this.canvas.height = this.positionsContainer.getBoundingClientRect().height;
    this.ctx = this.canvas.getContext('2d');
    this.files = [];
    this.registerEvents();
  }

  load(event) {
    event.preventDefault();
    event.target.innerHTML = '';
    const file = event.dataTransfer.files[0],
          img = document.createElement('img');
    img.src = URL.createObjectURL(file);
    img.classList.add('layout__image'); 
    event.target.appendChild(img);
    event.target.classList.remove('layout__item_active'); 
    const iwidth = event.target.clientWidth,
          iheight = event.target.clientHeight;
    let ix = event.target.offsetLeft, iy = 0;
    if (event.target === this.layout.bottom) {
      iy = event.target.clientHeight - 1;
    }  
    img.addEventListener('load', () => {
      this.ctx.drawImage(img, 0, 0, iwidth, iheight, ix, iy, iwidth, iheight);
      URL.revokeObjectURL(img.src);
    }) 
  }
  
  generateImg(event) {
    event.preventDefault();
    this.result.value = `<img src="${this.canvas.toDataURL()}">`;
  }

  registerEvents() {
   ...