Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mark rectangle on an image inside a web page for region of interest selection

Tags:

javascript

I'd like to mark a rectangle on an image in a web page for region of interest selection. Meaning, given an image on a web page I'd like to be able to click-drag with a mouse that will draw a rectangle and return a set of x,y,w,h upon mouse release.

How can this be done?

Thanks.

like image 916
miluz Avatar asked Oct 17 '25 02:10

miluz


2 Answers

This can be accomplished with a little positioning trick. Set the image in a div positioned relatively. Then you can create an absolute positioned div inside of the div that contains the image as well. This allows you to move the nested div around the front of the image.

As per your update, I have added some javascript that controls the inner div's x,y,w,h on mousedown, mousemove and mouseup

NOTE: since this js snippet uses clientX/Y and offsetLeft/Top, the preview below may run a bit off because of its overflow container inside of the post page. I recommend viewing the "Run code snippet" in full screen mode.

var x,y,oldx,oldy;
var showDrag = false;
document.getElementById("cont").addEventListener("mousedown", function(e) {
  oldx = e.clientX; //mousedown x coord
  oldy = e.clientY; //mouedown y coord
  showDrag = true;
  e.preventDefault();
});
document.getElementById("cont").addEventListener("mousemove", function(e) {
  if (showDrag == true) {
    x = e.clientX; //mouseup x coord
    y = e.clientY; //mouseup y coord
    var bbox = document.getElementById("bbox");
    var contbox = document.getElementById("cont");
    //get the width and height of the dragged area
    var w = (x > oldx ? x-oldx : oldx-x);
    var h = (y > oldy ? y-oldy : oldy-y);
    var addx = 0, addy = 0;
    //these next two lines judge if the box was dragged backward
    //and adds the box's width to the bbox positioning offset
    if (x < oldx) { addx = w; }
    if (y < oldy) { addy = h; }
    bbox.style.left = (oldx-parseInt((contbox.offsetLeft+addx)))+"px";
    bbox.style.top = (oldy-parseInt((contbox.offsetTop+addy)))+"px";
    bbox.style.width = w+"px";
    bbox.style.height = h+"px";
    bbox.style.display = "block";
  }
  e.preventDefault();
});
document.getElementById("cont").addEventListener("mouseup", function(e) {
  showDrag = false;
  e.preventDefault();
});
div.focus-image {
  border:1px solid #dddddd;
  display:inline-block;
  position:relative;
  cursor:pointer;
}
div.focus-image div {
  display:none;
  border:2px solid red;
  position:absolute;
  left:90px; /*x*/
  top:60px; /*y*/
}
<div id="cont" class="focus-image">
  <img src="http://cdn.sstatic.net/Sites/stackoverflow/img/[email protected]?v=73d79a89bded&a" />
  <div id="bbox"></div>
</div>
like image 88
Spencer May Avatar answered Oct 19 '25 18:10

Spencer May


My next.js / react + typescript solution (based on Spencer May's answer):

"use client";

import Image from "next/image";
import { useState, MouseEvent } from "react";

type BoxState = {
  x: number;
  y: number;
  width: number;
  height: number;
  controlled: boolean;
  visible: boolean;
};

export default function SelectImage() {
  const [box, setBox] = useState<BoxState>({
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    controlled: false,
    visible: false,
  });
  const [start, setStart] = useState({ x: 0, y: 0 });

  function handleMouseDown(e: MouseEvent<HTMLDivElement>) {
    const rect = e.currentTarget.getBoundingClientRect();
    // Offset relative to parent div
    const x1 = e.clientX - rect.left;
    const y1 = e.clientY - rect.top;
    setStart({ x: x1, y: y1 });
    setBox((prev) => ({
      ...prev,
      x: x1,
      y: y1,
      width: 0,
      height: 0,
      controlled: true,
      visible: true,
    }));
  }

  function handleMouseMove(e: MouseEvent<HTMLDivElement>) {
    if (box.controlled) {
      const rect = e.currentTarget.getBoundingClientRect();
      const x2 = e.clientX - rect.left;
      const y2 = e.clientY - rect.top;
      setBox((prev) => ({
        ...prev,
        // Handle backward drag
        x: Math.min(x2, start.x),
        y: Math.min(y2, start.y),
        width: Math.abs(x2 - start.x),
        height: Math.abs(y2 - start.y),
      }));
    }
  }

  function handleMouseUp() {
    const cancelSize = 20;
    const cancelRect = box.width < cancelSize || box.height < cancelSize;
    setBox((prev) => ({ ...prev, controlled: false, visible: !cancelRect }));
  }

  return (
    <div className="flex flex-col justify-center items-center bg-white">
      <h2>Select image demo</h2>
      <div
        className="inline-block relative cursor-pointer border border-gray-300 select-none"
        onMouseDown={handleMouseDown}
        onMouseMove={handleMouseMove}
        onMouseUp={handleMouseUp}
        onMouseLeave={handleMouseUp}
        draggable="false"
      >
        <Image
          src="http://cdn.sstatic.net/Sites/stackoverflow/img/[email protected]?v=73d79a89bded&a"
          className="pointer-events-none select-none"
          alt="demo"
          width="400"
          height="400"
        />
        {box.visible && (
          <div
            className="absolute border-2 border-red-500 pointer-events-none select-none"
            style={{
              left: box.x,
              top: box.y,
              width: box.width,
              height: box.height,
              display: box.visible ? "block" : "none",
            }}
          ></div>
        )}
      </div>
    </div>
  );
}
like image 23
Ezra Avatar answered Oct 19 '25 19:10

Ezra



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!