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...