Closed stefanprobst closed 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?
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.
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.
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.
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