InTaVia / web

InTaVia web client
MIT License
2 stars 0 forks source link

[VQ]: brushing on visual querying page does not work in firefox #41

Closed stefanprobst closed 2 years ago

stefanprobst commented 2 years ago

brushing in the date-of-birth/death popover workd fine for me in chrome, but not in firefox:

https://user-images.githubusercontent.com/20753323/168595567-443d202c-127d-4a31-b26a-af4d213d343f.mp4

samuelbeck commented 2 years ago

Comment during dev meeting: potential cause different handling of foreignobject in different browsers? Would it make sense to implement widget as indivisual components separate from svg instead?

mfranke93 commented 2 years ago

I have been able to isolate one issue in a minimal working example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>*,*::before,*::after{box-sizing:border-box}</style>
</head>
<body>
  <svg width="1000" height="700" style="border:1px solid rebeccapurple">
    <foreignObject x="300" y="200" width="200" height="100">
      <xhtml:body xmlns="http://www.w3.org/1999/xhtml" style="border:1px solid hotpink;display:grid">
        <svg xmlns="http://www.w3.org/2000/svg" width="198" height="98" viewBox="0 0 198 98">
          <g id="foo"></g>
        </svg>
      </xhtml:body>
    </foreignObject>
  </svg>
  <script>
    const brush = d3.brushX()
      .extent([[0,0],[198,98]])
      .on('start', e => console.log('start', e.selection, e.sourceEvent.clientX, e.sourceEvent.clientY))
      .on('brush', e => console.log('brush', e.selection, e.sourceEvent.clientX, e.sourceEvent.clientY))
      .on('end', e => console.log('end', e.selection, e.sourceEvent.clientX, e.sourceEvent.clientY));
    d3.select('#foo').call(brush);
  </script>
</body>
</html>

This works as expected both in Firefox and Chromium. However, if I wrap the foreignObject in a g element with a translate() on it, it no longer works and exhibits the same behavior in Firefox as the issue itself. I am currently trying to get this to work in the histogram code as well.

mfranke93 commented 2 years ago

Update for my example above! Adding a viewBox to the outer SVG as well (as is the case in the VQ page), the brush is suddently offset in the X direction by the x offset of the viewBox:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>*,*::before,*::after{box-sizing:border-box}</style>
</head>
<body>
  <svg width="1000" height="700" style="border:1px solid rebeccapurple" viewBox="-50 -50 1000 700">
    <foreignObject x="300" y="200" width="200" height="100">
      <xhtml:div xmlns="http://www.w3.org/1999/xhtml" style="border:1px solid hotpink;display:grid;isolation:isolate">
        <svg xmlns="http://www.w3.org/2000/svg" width="198" height="98" viewBox="0 0 198 98">
          <g id="foo"></g>
        </svg>
      </xhtml:div>
    </foreignObject>
  </svg>
  <script>
    const brush = d3.brushX()
      .extent([[0,0],[198,98]])
      .on('start', e => console.log('start', e.selection, e.sourceEvent.clientX, e.sourceEvent.clientY))
      .on('brush', e => console.log('brush', e.selection, e.sourceEvent.clientX, e.sourceEvent.clientY))
      .on('end', e => console.log('end', e.selection, e.sourceEvent.clientX, e.sourceEvent.clientY));
    d3.select('#foo').call(brush);
  </script>
</body>
</html>

Similarly, a translate() anywhere in the ancestors of the foreignElement will offset the brush. So it seems the solution is to not have any translations on the root SVG element; i.e., working with global coordinates everywhere in the SVG. Not ideal, but workable.

Passing down a SVGTransform object as a property to all components to help keep track of the current displacement relative to the global coordinate system could be a solution. Every child that would have a transform or a viewBox would then instead add its displacement to the parent transform it receives:

const parentTransform: SVGTransform = ...;
const ownTransform = new SVGTransform();
ownTransform.setTranslate(ownTx, ownTy);
const list = new SVGTransformList();
list.initialize(parentTransform);
list.appendItem(ownTransform);
const finalTransform = list.consolidate();

The translate x and y could then be accessed via the (deprecated) matrix property of the SVGTransform. But maybe, since only translation is required, it is easier to make a self-rolled class or object just handling those two properties.

mfranke93 commented 2 years ago

So, to conclude my previous comment: The issue in Firefox is that the outer SVG created in VisualQuerying.tsx has a viewBox of ${-window.innerWidth / 2} ${-window.innerHeight / 2} ${window.innerWidth} ${window.innerHeight} as opposed to 0 0 ${window.innerWidth} ${window.innerHeight}.

Edit: As expected, just setting the viewBox as described, the brush works properly, but now all the other coordinate calculations need to be modified to reflect the change, as everything moves to the top left corner.