JSFiddle - React, Tailwind, and code Playground
by joshmoto
HTML
<div class="area">
<img src="https://dragonballsuper-france.fr/wp-content/uploads/2017/10/DIHQF2OXoAAJi4n-660x330.jpg" alt="" />
<div class="rect">
<div class="exclusion-x"></div>
<div class="exclusion-y"></div>
</div>
</div>
CSS
/* area div */
.area {
overflow: hidden;
position: relative;
margin-bottom: 8px;
}
/* area div hover cursor */
.area:hover {
cursor: crosshair;
}
/* img tag in area div */
.area IMG {
display: block;
max-width: 100%;
pointer-events: none;
user-select: none;
}
/* rectangle div in area div */
.area .rect {
opacity: 0;
transition: all 0s ease;
position: absolute;
border: 1px solid red;
z-index: 1;
}
/* rectangle div css when in draw or drawn mode */
.area.draw .rect,
.area.drawn .rect {
opacity: 1;
}
/* below css is for fun rendering outer exclusion area of drawn rectangle div with a opaque overlay */
/* rectange exclusing pseudo elems base css */
.area.drawn .rect .exclusion-x::before,
.area.drawn .rect .exclusion-x::after,
.area.drawn .rect .exclusion-y::before,
.area.drawn .rect .exclusion-y::after {
position: absolute;
content: '';
display: block;
background: #000;
opacity: .75;
z-index: -1;
pointer-events: none;
user-select: none;
}
/* rectange outer opaque x above css */
.area.drawn .rect .exclusion-x::before {
bottom: calc(100% + 1px);
left: 50%;
transform: translateX(-50%);
height: 200vh;
width: 200vw;
}
/* rectange outer opaque x below css */
.area.drawn .rect .exclusion-x::after {
top: calc(100% + 1px);
left: 50%;
transform: translateX(-50%);
height: 200vh;
width: 200vw;
}
/* rectange outer opaque y left css */
.area.drawn .rect .exclusion-y::before {
right: calc(100% + 1px);
top: -1px;
bottom: -1px;
width: 200vw;
}
/* rectange outer opaque y right css */
.area.drawn .rect .exclusion-y::after {
left: calc(100% + 1px);
top: -1px;
bottom: -1px;
width: 200vw;
}
JavaScript
// our updatable variable objects to use globally
let img = {};
let position = {};
// image matrix function to update img object variable
function imgMatrix() {
// our image object inside area
let $img = $('IMG', '.area');
// offset data of image
let offset = $img.offset();
// add/update object key data
img.width = $img.outerWidth();
img.height = $img.outerHeight();
img.offsetX = offset.left - $(document).scrollLeft();
img.offsetY = offset.top - $(document).scrollTop();
}
// position matrix function to update position object variable
function positionMatrix(e, mousedown = false) {
// if mousedown param is true... for use in
if (mousedown) {
// set the top/left position object data with percentage position
position.top = (100 / img.height) * (e.pageY - img.offsetY);
position.left = (100 / img.width) * (e.pageX - img.offsetX);
}
// set the right/bottom position object data with percentage position
position.right = 100 - ((100 / img.width) * (e.pageX - img.offsetX));
position.bottom = 100 - ((100 / img.height) * (e.pageY - img.offsetY));
}
// mouse move event function in area div
$(document).on('mousemove', '.area', function(e) {
// / update img object variable data upon this mousemove event
imgMatrix();
// if this area has draw class
if ($(this).hasClass('draw')) {
// update position object variable data passing current event data
positionMatrix(e);
// if image x cursor drag position percent is negative to mousedown x position
if ((100 - position.bottom) < position.top) {
// update rectange x negative positions css
$('.rect', this).css({
top: (100 - position.bottom) + '%',
bottom: (100 - position.top) + '%'
});
// else if image x cursor drag position percent is positive to mousedown x position
} else {
// update rectange x positive positions css
$('.rect', this).css({
bottom: position.bottom + '%',
top:...