Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mouse position in SVG and RevealJS

I'm creating a presentation with RevealJS and would like to incorporate some interactive SVG visualizations created with D3. I've done this without difficulty a number of times before but I'm having some difficulty this time. After a bit of debugging, I've traced the problem to the following: For some reason, the mouse position relative to an SVG is not reported correctly when the whole thing is wrapped inside RevealJS.

My original version of the code used a standard D3 technique to get the mouse position. In an effort to simplify and isolate the problem, though, I eliminated D3 and now use vanilla Javascript to get the mouse position as detailed in this StackOverflow answer. My code (implemented as a stack-snippet) looks like so:

var info =  document.getElementById("info");
var svg =  document.getElementById("svg");
pt = svg.createSVGPoint();
var cursorPoint = function(evt){
  pt.x = evt.clientX; pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}
svg.addEventListener('mousemove',function(evt){
  var loc = cursorPoint(evt);
  info.textContent = "(" + loc.x + ", " + loc.y +   ")";
},false);
svg {
  border: solid black 1px;
  border-radius: 8px;
}
<div id="info">pos</div>
<svg id="svg" width="300" height="200"></svg>

When I run it inside RevealJS in Firefox on my Mac, though, I get very different numbers - as if the coordinates have been shifted by (-423,-321) or some other large negative pair of numbers. I've added a screen shot below to illustrate this. The mouse doesn't appear in the screenshot but is near the upper left corner so that it should read something like (3,5) or some small values like that.

enter image description here

I assume that RevealJS is performing some extra transformation on the SVG, but I can't find it and I'm hoping there's some RevealJS recommended way to deal with this.

There is a full working (well, not actually correctly) version of the RevealJS version here. It's pretty much the exact same code as in the stack-snippet above, but wrapped inside a minimal RevealJS template.


Looking into this more closely still, the issue appears to happen with Firefox but not with Chrome. I'm running current versions of those programs on my 1 year old Macbook Pro. I'd really like some code that works in a wide range of browsers and would certainly want it to run in both Firefox and Chrome.

like image 835
Mark McClure Avatar asked Dec 23 '17 01:12

Mark McClure


1 Answers

Notice how elegant it is to create slides animations with CSS tranforms but it is not the most reliable approach for the display as it yields inconsistent results across browsers and I suppose that you're going to do something with those coordinates later and that you may need to have a precise location of the mouse pointer.

You were right something unexpected happened in reveal.js
Basically, with scale and translate the SVG element is shifted downward right and scaled but the coordinates of the pointer are as if your hovering its original position and size area then computed relative to the current layout, hence the negative values, please take a look at the picture.

enter image description here

So I'm removing this line from the layout() function in reveal.js :

transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } );

that is used to center the element. With one set of statements for both of Firefox and Chrome (no need to check for features.zoom in the layout() function) I want to horizontally center the slide element:

dom.slides.style.left = '0';
dom.slides.style.top = 'auto';
dom.slides.style.bottom = 'auto';
dom.slides.style.right = '0';

Reveal.initialize({});
var info = document.getElementById("info");
var svg = document.getElementById("svg");
pt = svg.createSVGPoint();
var cursorPoint = function(evt) {
  pt.x = evt.clientX;
  pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}
svg.addEventListener('mousemove', function(evt) {
  var loc = cursorPoint(evt);
  info.textContent = "(" + loc.x + ", " + loc.y + ")";
}, false);
svg {
  background-color: white;
  border: solid black 1px;
  border-radius: 8px;
}
<script src="https://cdn.rawgit.com/Nasdav/revealjs-for-SO-answer/b6ea3980/reveal.js"></script>

<script src="https://cdn.rawgit.com/hakimel/reveal.js/master/lib/js/head.min.js"></script>
<link rel="stylesheet" href="https://cdn.rawgit.com/hakimel/reveal.js/65bdccd5807b6dfecad6eb3ea38872436d291e81/css/reveal.css">
<link rel="stylesheet" href="https://cdn.rawgit.com/hakimel/reveal.js/65bdccd5807b6dfecad6eb3ea38872436d291e81/lib/css/zenburn.css">
<link rel="stylesheet" href="https://cdn.rawgit.com/hakimel/reveal.js/65bdccd5807b6dfecad6eb3ea38872436d291e81/css/theme/sky.css" id="theme">

<body>

  <div class="reveal">
    <div class="slides">
      <section>
        <h2 style="font-size:150%">Mouse position in RevealJS</h2>
        <p style="font-size:75%">
          Make sure to click "full page" at the right most side of "Run Code Snippet". Then hover over the SVG below to get the "position" of the mouse in the SVG. The position is obtained using a standard javascript technique and works fine in
          <a href="non_reveal_version.html">this non-reveal version</a>. For some reason though. The values are correct now in Firefox when RevealJS is present. It works well in Chrome, though.
        </p>
        <svg id="svg" width="300" height="200"></svg>
        <div id="info">pos</div>
      </section>
    </div>
  </div>

Problem solved you have a reliable way of getting the coordinates of the mouse pointer consistently accross both Chrome, Firefox and even Internet Explorer in the SVG area from (0,0) to (300,200)

Here, it will be slightly less accurate coordinates if you want the slide to have the same size as the previous one (slightly bigger ) and/or responsive to resizing : In the same place as the previous code, I would let the width and the font size having percentages (I could do so to the SVG itself which is having a fixed size here)

dom.slides.style.width = '90%';
dom.slides.style.fontSize= scale*100+'%';
like image 199
user10089632 Avatar answered Sep 19 '22 09:09

user10089632