0

I'm using d3.js to draw a rectangle with it's top-left corner at (10,10). Then I move that rectangle by doing a translate(200, 200).

Then I attach an on-mouseout event that simply prints out the coordinates of the mouse pointer. To my surprise the coordinates it reports indicate the boundaries of the rectangle before it was translated. Why???

How can I make it report the actual mouse-coordinates, not the mouse coordinates of where the rectangle used to be before it was translated?

Here is the code:

var myApp = angular.module('myApp', []);

myApp.controller('MyController', function() {
    var self = this;
    self.svgContainer = d3.select("#my_svg_widget");

    var my_rect = self.svgContainer.append("rect")
      .attr("x", 10)
      .attr("y", 10)
      .attr("width", 60)
      .attr("height", 60);

    my_rect.attr("transform", "translate(200, 200)");    

    my_rect.on("mouseover", function() {
        var coordinates = [0, 0];
        coordinates = d3.mouse(this);
        var mouseX6 = Number(coordinates[0]);
        var mouseY6 = Number(coordinates[1]);
        console.log('mouseX6 = ', mouseX6, typeof mouseX6);
        console.log('mouseY6 = ', mouseY6, typeof mouseY6);
      }
    );
  }
);

Here is the output:

mouseX6 =  13 number
mouseY6 =  9 number

Here is the Plunker: https://plnkr.co/edit/VSTzUhehdsVLFgmZqzHi?p=preview

Saqib Ali
  • 11,931
  • 41
  • 133
  • 272

1 Answers1

4

d3.mouse -> Find x,y coordinates of the current mouse event relative to a container element.

Try this code:

var coordinates = d3.mouse(this);
var pt = self.svgContainer.node().createSVGPoint();
pt.x = coordinates[0];
pt.y = coordinates[1];
pt = pt.matrixTransform(my_rect.node().getCTM());
var mouseX6 = pt.x, mouseY6 = pt.y;

Updated plunker: Try clicking on the rect to test the new position calculation.

var self = {};
self.svgContainer = d3.select("#my_svg_widget");

console.log("Hello!");

var my_rect = self.svgContainer.append("rect")
  .attr("x", 10)
  .attr("y", 10)
  .attr("width", 60)
  .attr("height", 60)
  .attr("fill", "Pink")
  .attr("stroke", "Blue")
  .attr("stroke-width", 2);

my_rect.attr("transform", "translate(200, 200)");

my_rect.on("mouseover", function() {
  console.log("\n\n\n================ MouseOver Rectangle ================");
  var coordinates = [0, 0];
  console.log("1");
  coordinates = d3.mouse(this);
  var pt = self.svgContainer.node().createSVGPoint();
  pt.x = coordinates[0];
  pt.y = coordinates[1];
  pt = pt.matrixTransform(my_rect.node().getCTM());
  //coordinates = d3.mouse(self.svgContainer);
  console.log("2");
  var mouseX6 = Number(coordinates[0]);
  var mouseY6 = Number(coordinates[1]);
  console.log('mouseX6 = ', mouseX6, typeof mouseX6);
  console.log('mouseY6 = ', mouseY6, typeof mouseY6);

  mouseX6 = pt.x;
  mouseY6 = pt.y;
  console.log('mouseX6 = ', mouseX6, typeof mouseX6);
  console.log('mouseY6 = ', mouseY6, typeof mouseY6);
});

my_rect.on("click", function() {
  console.log("\n\n\n================ Click on Rectangle ================");

  var coordinates = d3.mouse(this);
  var pt = self.svgContainer.node().createSVGPoint();
  pt.x = coordinates[0];
  pt.y = coordinates[1];
  pt = pt.matrixTransform(my_rect.node().getCTM());
  var mouseX6 = pt.x,
    mouseY6 = pt.y;
  self.svgContainer.append("circle").attr("cx", pt.x).attr("cy", pt.y).attr("r", 3);
  console.log('mouseX6 = ', mouseX6, typeof mouseX6);
  console.log('mouseY6 = ', mouseY6, typeof mouseY6);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="my_svg_widget" width="300" height="300" style="border-style:solid;border-color:purple;border-width: 2px;">
</svg>
Gilsha
  • 14,431
  • 3
  • 32
  • 47
  • I didn't try the first option. But the second option you showed is not correct. Here is the new plunker containing it: https://plnkr.co/edit/VSTzUhehdsVLFgmZqzHi?p=preview. When you enter the square at the top-left corner, it should report (approximately) X=210, Y=210. Instead it reports X=220, Y=220. – Saqib Ali Apr 13 '16 at 06:15