Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: interactions misaligned when plot is inside transform: scale()-ed element #888

Closed
hellochar opened this issue Aug 26, 2016 · 22 comments · Fixed by #5193
Closed

bug: interactions misaligned when plot is inside transform: scale()-ed element #888

hellochar opened this issue Aug 26, 2016 · 22 comments · Fixed by #5193
Labels
bug something broken community community contribution

Comments

@hellochar
Copy link

See http://codepen.io/hellochar/pen/JKgXPE and mouse around.

Looks like plotly.js miscalculates where the mouse is positioned if the plot itself is rendered inside an element with a transform: scale() CSS property set. The tooltips don't match up with where the mouse is. This is most apparent when you try to click+drag an area of the plot and it clearly shows the locations don't match up.

The buttons in the toolbar line up correctly, as does the hitbox for dragging the axes or enabling/disabling specific traces.

Also it looks like if you're at the top-left corner of the plot, the mouse position is accurate (that is, the (0, 0) origin point is handled correctly; only the scaling factor is off).

Also see palantir/plottable#1644 which looks to be the same issue in Plottable.

Let me know if that makes sense and if there's something else I can do!

@etpinard
Copy link
Contributor

etpinard commented Sep 1, 2016

Ah. I guess you must be the first person to use plotly.js with CSS transforms.

Thanks for reporting.

@etpinard etpinard added bug something broken community community contribution labels Sep 1, 2016
@MaximilianBuegler
Copy link

I'm having the same issue. Any news on this?

@everly
Copy link

everly commented Mar 9, 2017

This is also preventing click events from firing properly

@alexcjohnson
Copy link
Collaborator

Implementation notes from when I rediscovered this issue just now:

If the page transforms the plotDiv element (or any parent of it) with a scale, rotation, or matrix, any call to getBoundingClientRect on an element within that div gives us misleading results and breaks hover effects. Certain getBoundingClientRect calls are necessary to find text sizing, but this should always be called on elements in gd._tester, which we attach directly to <body>, far off-screen. We should get rid of all other getBoundingClientRect calls. This will also be better for performance.

Seems from the comments here like the problems extend beyond just getBoundingClientRect calls though - I guess anything that depends on clientX and clientY from the mouseEvent is susceptible. In principle I guess we'll need to find a way to invert the transform in order to interpret these correctly... make 3 little test elements in different corners of the plot and call getBoundingClientRect on each? window.getComputedStyle().transform on the plot and all its parent nodes and combine them? Those all sound kind of painful, any other options I'm not thinking of?

@alexcjohnson
Copy link
Collaborator

Related I suspect: doesn't seem to be an issue in Chrome, but in FF if I move a plot from my external monitor to my laptop monitor (retina display, 2x resolution but this seems partially masked) hover labels get messed up:
screen shot 2017-05-18 at 5 27 52 pm
And retina -> normal:
screen shot 2017-05-18 at 5 33 18 pm

@monfera
Copy link
Contributor

monfera commented Sep 21, 2017

@alexcjohnson yes, moving between screens with a different devicePixelRatio would need that everything that depends on it be rerendered. On an old project I used polling (a few times a second with a setInterval), now one can create an event listener on a media query:

var dprSource = window.matchMedia(
  '(min-resolution:' + 
  window.devicePixelRatio +
  'dppx) and (max-resolution:' +
  window.devicePixelRatio +
  'dppx)'
);
dprSource.addListener(function() {
  console.log('new devicePixelRatio is', window.devicePixelRatio);
});

...hm this works on Chrome on FF but not (yet?) on Safari. So, polling might be safer atm.

@nicknack23
Copy link

nicknack23 commented Oct 24, 2017

I'm having this problem too. My app requires zooming entire charts, and because of this bug I have to make duplicate enlarged versions of every chart, and swap them out every time the user zooms.

Here is a codepen that demonstrates my use case.

On a side note: notice that I'm implementing click-to-zoom using pure HTML+CSS. This is because I can't get a click event in the main plot area to work because of the transparent div overlays. Here are my semi-failed attempts at doing it in Javascript:
https://codepen.io/anon/pen/RLzWMr
https://codepen.io/anon/pen/wrLxJW

@foxik
Copy link

foxik commented Feb 25, 2018

The transform: scale() still does not work, neither on FF 52 nor Chrome 64.

I am using plotly.js to draw graphs in my presentations (using https://github.com/gnab/remark), and the presentation framework uses transform: scale() to scale the slide, which breaks plotly interactions.

Any idea how this could be fixed?

@alexeid
Copy link

alexeid commented Mar 27, 2018

I think this is the same problem I have with plotly plots embedded in reveal.js slides which get scaled to fit the window size. If reveal.js is forced to scale=1 then hover and mouse coordinates behave correctly, but this is not a solution because then slides do not fill the screen :)

@fruchti
Copy link

fruchti commented Mar 27, 2018

I faced the same issue with reveal.js. My workaround is to add a CSS transform which precisely undoes Reveal's scaling and then scale the plot with its width and height attributes. Something along the lines of

    window.addEventListener('resize', function(event) {
        var plots = [...document.getElementsByClassName('js-plotly-plot')];
        plots.forEach((plt) => {
            var width = Reveal.getScale() * parseInt(plt.getAttribute('data-target-width'));
            var height = Reveal.getScale() * parseInt(plt.getAttribute('data-target-height'));
            plt.style.width = width + 'px';
            plt.style.height = height + 'px';
            Plotly.Plots.resize(plt);
            plt.style.transform = 'scale(' + 1 / Reveal.getScale() + ')';
        });
    });

I added the data-target- attributes so I could do this. There are probably more elegant ways but it does the job. I hope it helps someone!

EDIT: I just noticed this workaround doesn't seem to work in Chrome, only in Firefox.

@nicknack23
Copy link

we'll need to find a way to invert the transform in order to interpret these correctly... make 3 little test elements in different corners of the plot and call getBoundingClientRect on each? window.getComputedStyle().transform on the plot and all its parent nodes and combine them? Those all sound kind of painful, any other options I'm not thinking of?

@alexcjohnson, how about putting the responsibility for detecting the transform on the user? Maybe the layout object could have an optional property called parenttransform or something that points to a callback function that returns the transformation matrix or a string of individual CSS transforms. Then hover or other interactions could just invert the transformation to calculate the correct position. I know this isn't ideal, but it's a simple fix that would provide a useful workaround for many users still suffering from this bug.

@hellochar
Copy link
Author

FYI Plottable's fixed this here: https://github.com/palantir/plottable/blob/b6e36fbd4d8d7cba579d853b9f35cc260d1243bf/src/utils/mathUtils.ts#L173 . The gist is to walk up the DOM tree and account for the transform properties of each element

@etpinard
Copy link
Contributor

Another report from #4343


Due to a specific requirement of fitting multiple charts which are added dynamically and arranged horizontally in available viewport without scroll I am using transform scale for example transform: translateZ(0) scale(0.50, 0.50) on charts based on available space, although the whole chart is scaled down uniformly but when mouse hover over is done on chart I observe there is offset between mouse location and hover popup location shown in attached screen shot

tooltipissue

Using resize() or relayout does not give crisp chats when they are scale down due to uneven transform of chart and labels.

issue

I have created code pen to explain issue
https://codepen.io/contactsandygo/pen/wvvXdov

@jackparmer
Copy link
Contributor

This issue has been tagged with NEEDS SPON$OR

A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.

Sponsorship range: $10k-$15k

What Sponsorship includes:

  • Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
  • Tests for this feature
  • Long-term support (continued support of this feature in the latest version of Plotly.js)
  • Documentation at plotly.com/javascript
  • Possibility of integrating this feature with Plotly Graphing Libraries (Python, R, F#, Julia, MATLAB, etc)
  • Possibility of integrating this feature with Dash
  • Feature announcement on community.plotly.com with shout out to Sponsor (or can remain anonymous)
  • Gratification of advancing the world's most downloaded, interactive scientific graphing libraries (>50M downloads across supported languages)

Please include the link to this issue when contacting us to discuss.

@alexhartstone
Copy link
Contributor

I have a fix working for the hover aspect of this bug. It is similar to Palantir's plottable fix mentioned above, in that it traverses the DOM ancestors of the event target element to produce a transform matrix, then applies the inverse of that to a point. However, there are multiple mouse interactions affected by this issue (such as clicking and dragging, hover, and probably others I can't think of or don't know about), and the fix applied to the hover case is a bit specific.

To be precise - it transforms xpx and ypx in the components/fx/hover.js _hover() call thus:

xpx = evt.clientX - dbb.left;
ypx = evt.clientY - dbb.top;

var transformInverse = Lib.inverseTransformMatrix(Lib.getFullTransformMatrix(evt.target));
var transformedCoords = Lib.apply2DTransform(transformInverse)(xpx, ypx);

xpx = transformedCoords[0];
ypx = transformedCoords[1];

I would like guidance on what a fix PR for this should look like - is it going to have to be a fix for every interaction in the same vein as the hover code above, or is there a more general way of doing things, e.g. transforming further up the chain? I've tried doing that, with little success - I think my lack of knowledge of the codebase is limiting me.

@azawalich
Copy link

azawalich commented Oct 19, 2020

Another workaround solution for this problem is to change main charting library from Plotly to mpld3. Of course, is not ideal and depends highly on your use case, but solves the initial problem ;). You can find more info on the issue, as well as some example code in my sample repository.

@archmoj
Copy link
Contributor

archmoj commented Oct 22, 2020

#5193 with some adjustments is getting close to address this issue.

@nicolaskruchten
Copy link
Contributor

This issue was tagged as "needs sponsorship" and was instead resolved by the contribution of an excellent pull request for this long-standing issue. Thank you so much for taking the initiative on this @alexhartstone!

@davidlove
Copy link

Misalignment is happening for me in Chrome 92.0.4515.159 (Plotly.py 5.2.2, Jupyter Lab 3.0.16) but not in Firefox 91.0.2. Found the same behavior in both OSX and Linux, i.e., works in FF but not Chrome-based browsers.

Slides were created with jupyter nbconvert --to slides --no-input --no-prompt

I also saw the misalignment in tooltips in plots created by altair and mpld3.

@jzadra
Copy link

jzadra commented Oct 14, 2022

This is still happening for me with Chrome. Working correctly in Firefox.

@Moving-Electrons
Copy link

This is still happening on Chrome and Microsoft Edge. Unfortunately, I can't install FireFox on my computer. Your help fixing this bug or at least providing a workaround would be greatly appreciated.

@jknottUOW
Copy link

This is also unfortunately still happening for me too on Chrome. It does work as expected on Firefox, across a wide range of wondow sizes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken community community contribution
Projects
None yet
Development

Successfully merging a pull request may close this issue.