JSFiddle - React, Tailwind, and code Playground

by neonDog

HTML

<h1>CSS3 Marching Ants <small>(with image masking)</small></h1>
<p>Image cropping tool layout (rudimentary dragging added)</p>

<div class="image-crop-wrapper" data-margin='[0,0,0,0]' style="width:100%;">
  <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/133490/hedgehog.jpg" width="500" height="250" alt="">
</div>

<div class="image-crop-wrapper" data-padding='[0,0,0,0]' style="width:100%;">
  <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/133490/hedgehog.jpg" width="500" height="250" alt="">
</div>

<div class="image-crop-wrapper" data-dimensions='[0,0]' style="width:100%;">
  <img class="neon-resizable-item-target" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/133490/hedgehog.jpg" width="500" height="250" alt="">
</div>

SCSS

* {
  box-sizing: border-box;
}

.image-crop-wrapper {
  background: red;
  border: 1px solid #000;
  position: relative;
  display: inline-block;
  img {
    cursor: crosshair;
    display: block;
    margin: 0 auto;
    width: 100%;
    height:100%;
  }
}

.crop-area {
  width: 100%;
  height: 100%;
  position: absolute;
  top:0;
  right:0;
  bottom:0;
  left:0;
  border:1px #eee solid;
  
  &.active {
    border-color: red;
  }
}

.handle {
    border: 1px solid rgba(00, 00, 00, .6);
    background: aquamarine;//rgba(255, 255, 255, .6);
    cursor: move;
    display: block;
    width: 10px;
    height: 10px;
    border-radius:50%;
    margin: auto;
    position: absolute;
    
    &:hover {
      background:blue;
    }
}

.handle-top-left {top: -5px; left: -5px; cursor: nw-resize;}
.handle-top-mid {top: -5px; left: -5px; right: -5px; cursor: n-resize;}
.handle-top-right {top: -5px; right: -5px; cursor: ne-resize;}

.handle-mid-left {top: -5px; left: -5px; bottom: -5px; cursor: w-resize;}
.handle-mid-right {top: -5px; right: -5px; bottom: -5px; cursor: e-resize;}

.handle-bot-left {bottom: -5px; left: -5px; cursor: sw-resize;}
.handle-bot-mid {bottom: -5px; left: -5px; right: -5px; cursor: s-resize;}
.handle-bot-right {bottom: -5px; right: -5px; cursor: se-resize;}


// Extra ----------------
body {font-family: Georgia; font-size: 18px; padding: 15px; margin: 0; background: #333; color: #ccc; text-align:center;}
h1 {margin: 0; line-height: 1.1; small {display: block;} } p {margin: 0.5em 0;}

JavaScript

const directions = ['top','right','bottom','left'];
const dimensions = ['height','width'];

const properties = {
	padding:['paddingTop','paddingRight','paddingBottom','paddingLeft'],
  margin:['marginTop','marginRight','marginBottom','marginLeft'],
  dimensions:['height','width','height','width']
};

const handles = {
	'top-left':[3,0],
  'top-mid':[0],
  'top-right':[1,0],
  'mid-right':[1],
  'mid-left':[3],
  'bot-left':[3,2],
  'bot-mid':[2],
  'bot-right':[1,2]
};

const viewportProperties = ['vw','vh'];

const nearest = function(x, to=2) {
	return Math.round(x / to) * to;
};

class NeonResizableItem {
    
    constructor(element, settings = {}) {

        const defaults = {
            classPrefix: 'p-',
            property: 'padding',
            //mode: 'pixel', //'viewport', 'percent', 'pixel'
            mode: 'viewport',
            cornerBehavior: 'proportional',
            xAxisLink: true,
            yAxisLink: false
        };
        
        this.options = Object.assign({}, defaults, settings);
        this.element = element;
        this.resetUI();

        this._animate = this._animate.bind(this);
        this._mousedown = this._mousedown.bind(this);
        this._mouseup = this._mouseup.bind(this);
        this._mousemove = this._mousemove.bind(this);
        this.moveListenersActive = false;
        this.eventListenersActive = false;
        this._eventListeners();
        
        this.renderHelpers();
    }

		renderHelpers() {
    
    	this.el = document.createElement('div');
      this.el.className = 'crop-area';
      let html = '';
      
      for (const key in handles) {
      	html += `<span class="handle handle-${key}" data-handle="${key}"></span>`;
      }
      this.el.innerHTML = html;
      
      this.element.append(this.el);
      
    }

    resetUI() {
        this.ui = { mouse: [], bounds: [], active: false, data: null, panel: null, parent: null, parentBounds: null };
    }

    _mousedown(e) {
    		if...