Search code examples
jqueryclickpositionoverlay

jQuery- click at a co-ordinate on an image and draw a circle there


I have a div namely image_preview with an image inside it. There is an input box to get the radius of the circle from the user. When I click at a point on the image, I need a circle with that point as the center and the radius input as the radius. The circle should be marked with a marker . The div with id hotspot_display works as the marker and the div circle acts as the circle to be displayed . Both are absolutely positioned. The marker has as its background an image with the dimension 24X24.

Whenever I click, clones of the marker and circle are made and positioned around the point I clicked. The marker and circle positions are changed with mouse click (You'll find the fiddle link at the end of this question).

I can capture the co-ordinate of the point at which I click with offset and clientX etc. the code for which follows:

HTML: Image:

<input type="text" value="0"  class="radius" name="radius"/>

<div id="image_preview">

    <img src="https://i.imgur.com/B7VsSq3.png" alt="background" />



    <div id="hotspot_display"></div>

    <div class="circle" style="background-color: #858585; position: absolute;"></div>

</div><!-- end of id image_preview-->

<div id="hotspot_answer_points">


</div><!-- end of id hotspot_answer_points-->

JS:

   $('#image_preview').bind('click', function (ev) {

                var $div = $(ev.target);
                var $div = $(this);
                var $display =  $('#image_preview').find('#hotspot_display');
                var offset_t = $(this).offset().top - $(window).scrollTop();
                var offset_l = $(this).offset().left - $(window).scrollLeft();

                var left = Math.round( (ev.clientX - offset_l) );
                var top = Math.round( (ev.clientY - offset_t) );

                if(window.console){

                    console.log("left = "+left+" top = "+top);

                }


                var display_clone=$display.clone().show();
                display_clone.css({'top':top+33,'left':left+10,'display':'block'});

});

The code of display_clone.css has in its parameter some values (33 and 10 ) added to left and top without which I cannot get the center of image marker sit just on the co-ordinate.

Question 1) I just randomly used the 33 and 10 values to be added. What calculation should really be done here as these numbers to be added might vary If my HTML code sits inside a complex HTML page?

Question 2: I cannot get the circle's center to be the co-ordinate I clicked . What should be the way here ?

The whole of the scenario is here in a fiddle.

EDIT: My scenario has actually 2 parts. The situation stated above takes place in an admin area. In the user area , I again show the image inside a div namely hotspot_user_area. I saved the clicked co-ordinates in database from the admin area. In user I area, I have the HTML:

<div id="hotspot_user_area">

<img src="https://i.imgur.com/B7VsSq3.png" alt="background" />

</div>

I take the co-ordinates from database and want to show them with markers in the hotspot_user_area on the image. Suppose I get the co-ordinate from DB to be in variable x1 and y1; Now if I use the following code from Roko C. Buljan's answer , it won't work :

 $("<div />", {
      "class": "circle",
      css: {
        top: y1,
        left: x1,
        width: r * 2,
        height: r * 2,
      },
      appendTo: $this // append to #image_preview!
    }).

This doesn;t work because in admin panel left and top properties have the values x and y respectively which have the following calculation:

y = ev.pageY - o.top,
x = ev.pageX - o.left;

So how can I place the markers at the same place of the image in whatever div the image is placed again ?


Solution

  • Here's a simpler approach:

    • To get the center use something like ev.pageX - el.offset().left
    • Don't clone elements. Create them on the fly.
    • Use CSS transform translate to center them

    var App = App || {};
    App.points = []; // Use array to store objects like [{x,y,r}, {x,y,r} ...]
    
    
    jQuery(function($) {
    
      const $radius = $(".radius");
    
      $('#image_preview').on('click', function(ev) {
    
        const $this = $(this),
           r = parseInt($radius.val().trim(), 10), // Parse as Integer radix 10
          o = $this.offset(),
          y = ev.pageY - o.top,
          x = ev.pageX - o.left;
    
        $("<div />", {
          "class": "circle",
          css: {
            top: y,
            left: x,
            width: r * 2,
            height: r * 2,
          },
          appendTo: $this // append to #image_preview!
        });
    
        // Append data to App.points
        App.points.push({x,y,r});
        // Test
        console.log( App.points )
    
      });
    
    });
    #image_preview {
      position: relative; /* Add this since child are absolute! */
      background: #eee;
      margin: 15px;
      cursor: crosshair;
    }
    
    #image_preview img {
      display: inline-block;
      vertical-align: top;
    }
    
    .circle {
      position: absolute;
      background: rgba(255, 0, 0, 0.2) url("https://i.imgur.com/qtpC8Rf.png") no-repeat 50% 50%;
      border-radius: 50%;
      transform: translate(-50%, -50%); /* use translate instead of JS calculations */
    }
    
    .radius {
      position: absolute;
    }
    Radius: <input type="text" value="20" class="radius" name="radius">
    
    <div id="image_preview">
      <img src="https://i.imgur.com/B7VsSq3.png" alt="graph">
    </div>
    
    <script src="//code.jquery.com/jquery-3.1.0.js"></script>