bartbutenaers / node-red-contrib-ui-svg

A Node-RED widget node to show interactive SVG (vector graphics) in the dashboard
Apache License 2.0
94 stars 27 forks source link

Show mouse coordinates tooltipbox doesn't stick to mouse #28

Closed EvertDekker closed 5 years ago

EvertDekker commented 5 years ago

Thanks Bart for this amazing NR node.

The tooltipbox will not stick to the mouse cursor correct. How further you go to the lower right bottom how more distance there is between the mouse cursor and tooptipbox. In the upperleft corner of the floorpal it's fine.

Example

Also is the text in the tooltipbox hardly to read, it's a very light color grey.

bartbutenaers commented 5 years ago

Hi Evert, Thanks! Damn, I had a similar result as you in my initial version. So I had to fix it, but it didn't make sense to me why that fix worked... Would be nice if I could reproduce your problem somehow. Could you please give some information about your system (browser, operating system, ...) Bart

EvertDekker commented 5 years ago

Hi Bart,

I tested it with Firefox 68 on win10-64B desktop and then is not working correct. Tested it now on Chrome 76 on win10-64B desktop and then it works fine again.

It has something to do with the browser used. Maybe this can help you fix the problem: https://stackoverflow.com/questions/17106665/the-mouseevent-offsetx-i-am-getting-is-much-larger-than-actual-canvas-size

bartbutenaers commented 5 years ago

Hello Evert,

Now that is useful input! At least I can reproduce the problem on Firefox... Will try the solution in your link as soon as possible, and get back to you! Thanks!! Bart

bartbutenaers commented 5 years ago

Evert,

I have implement that Stackoverflow solution last night, but it didn't work on Firefox. Indeed the event also did contain offsetX/offsetY (but with other values as in Chrome). As a result the webkit-part of the code was executed (instead of the Firefox part).

Will try tonight the tippyjs(https://atomiks.github.io/tippyjs/creating-tooltips/) tooltip library...

Steve-Mcl commented 5 years ago

just a thought... perhaps we could inject a text element into the SVG and animate it's text?

bartbutenaers commented 5 years ago

@Steve-Mcl , In my initial version I did it that way. But then I had issues when my mouse cursor was near the border of my drawing (i.e. no collision detection). And if we change the viewbox of the SVG (for zoom in/out), we also would have to make sure the tooltip keeps having its original size ... That is why I had decided to stick a div-element to my mouse position ...

EvertDekker commented 5 years ago

Hi Bart,

Tested it today with

$scope.tooltip.style.left = (evt.layerX + 10) + 'px';
$scope.tooltip.style.top  = (evt.layerY + 10) + 'px';

and that works fine for me with Firefox, Chrome and Edge.

bartbutenaers commented 5 years ago

Evert,

Glad that you are helping us with this! I tried something similar last night, and indeed that also worked on my Firefox. But this introduced a new problem in my case. When I moved the mouse cursor at the lower or right edges of my SVG drawing, the tooltip disappeared (behind other dashboard elements) and scrollbars appeared around my SVG drawing. Wasn't that the case in your test?

Would prefer to have this simple solution, instead of needing a dependency to a third-party tooltip library ...

EvertDekker commented 5 years ago

Hi, I noticed the same behaviour with the scrollbars, however this was also with the "old" code with evt.offsetX The simplest solution will be to stick the tooltip to the upper left corner of the mouse pointer when X or Y is >80% of the canvas size.

And to make the font readable in the tooltipbox change line 246 to: $scope.tooltip.innerHTML = "<span style='color:#000000'>" +${pt.x},${pt.y}+ "</span>";

bartbutenaers commented 5 years ago

Evert, Almost solved, but not completely...

Have changed the tooltip code to this:

var tooltipX = evt.layerX;
var tooltipY = evt.layerY;
var svgBoundingBox = $scope.svg.getBoundingClientRect();

if (evt.pageX > (svgBoundingBox.right - $scope.tooltip.clientWidth - 20)) {
    // When arriving near the right border of the drawing, flip the tooltip to the left side of the cursor
    tooltipX = tooltipX - $scope.tooltip.clientWidth - 20;
}

if (evt.pageY > (svgBoundingBox.bottom - $scope.tooltip.clientHeight - 20)) {
    // When arriving near the bottom border of the drawing, flip the tooltip to the upper side of the cursor
    tooltipY = tooltipY - $scope.tooltip.clientHeight - 20;
}

$scope.tooltip.style.left = (tooltipX + 10) + 'px';
$scope.tooltip.style.top  = (tooltipY + 10) + 'px';

That works fine (Chrome, Firefox, Edge), except when I have scrollbars:

svg_tooltip_collision

Have been trying all kind of available coordinates, but no luck yet...

bartbutenaers commented 5 years ago

Normally the client dimensions of the SVG should represent the visual part of the drawing, as you can see on this nice picture from Stackoverflow:

image

However for some reason my SVG always returns the dimensions of scroll height, i.e. the dimensions of the ENTIRE drawing (also the part that is not visible).

But I found a workaround: the parent "md-card" element (generated by the dashboard) contains the visible size of our SVG. Since I saw a similar workaround afterwards (see Stackoverflow), I will use this workaround in our next beta version:

var tooltipX = evt.layerX;
var tooltipY = evt.layerY;

// We need the visible height and width of the SVG element, to have the tooltip flip when getting
// near the right and bottom border.  In case scrollbars are available around our SVG, the 
// clientHeight should represent the visible part of the SVG.  However that is not the case, because
// it seems to represent the entire SVG, instead of only the visible part.  
// Workaround: Get the "md-card" element (generated by the Node-RED dashboard) which is wrapping our
// SVG-node.  The height of that element is exactly the visible height of our SVG drawing.
// See also https://stackoverflow.com/questions/13122790/how-to-get-svg-element-dimensions-in-firefox
var mdCardElement = $scope.rootDiv.parentElement;            

if (tooltipX > (mdCardElement.clientWidth - $scope.tooltip.clientWidth - 20)) {
    // When arriving near the right border of the drawing, flip the tooltip to the left side of the cursor
    tooltipX = tooltipX - $scope.tooltip.clientWidth - 20;
}

if (tooltipY > (mdCardElement.clientHeight - $scope.tooltip.clientHeight - 20)) {
    // When arriving near the bottom border of the drawing, flip the tooltip to the upper side of the cursor
    tooltipY = tooltipY - $scope.tooltip.clientHeight - 20;
}

$scope.tooltip.style.left = (tooltipX + 10) + 'px';
$scope.tooltip.style.top  = (tooltipY + 10) + 'px';

The tooltip seems to be sticking and flipping correctly in both directions on Chrome/Firefox/Edge:

svg_stickly

I will also implement your "span" element solution!

Thanks for your input! Bart

bartbutenaers commented 5 years ago

Implemented in the dev branch.

EvertDekker commented 5 years ago

Nice, works great in all 3 the browsers. Thanks for the help.

bartbutenaers commented 5 years ago

Hi Evert, Thanks for giving feedback! Kind regards, Bart