Retrieving SVG position in Firefox vs Chrome with viewBox set - firefox

I'm trying to get the left position of an svg element that has a viewBox set. The viewBox is basically a square, while the actual svg element is more rectangular. In most cases, this isn't a problem (and in Chrome, everything works fine), however, when trying to get the left position of the element within Firefox, as the viewBox is square, Firefox reports the left position of the viewBox rather than the svg element.
See http://jsfiddle.net/c6SW6/11/ for an example which should make things obvious.
Basically, in Chrome, the number reported for the left position is 8 This is the number that I want. In Firefox, it's reported as 108. How do I get Firefox to report the number 8 as well?
Code
HTML
<div>
<svg viewBox="0 0 100 100"><rect x=0 y=0 width=100 height=100></rect></svg>
</div>
<p>
</p>
CSS
div {
height: 100px;
width: 300px;
margin: 0;
padding: 0;
position: absolute;
top: 200px;
}
svg {
background-color: green;
height: 100%;
width: 100%;
}
JS
$('p').text($('svg').offset().left);

Assuming we gave the rect an id="r" attribute...
If you just want the offset of the rect in the svg itself then it's
$('p').text(document.getElementById("r").getCTM().e);
If you want the offset from the page origin instead...
Call rect.getBoundingClientRect() and the left and top will contain the answer
$('p').text(document.getElementById("r").getBoundingClientRect().left);
or alternatively rect.getScreenCTM() the e and f members of the result will be the answer
$('p').text(document.getElementById("r").getScreenCTM().e);
If you rotate the svg using a transform then you'll get different answers, getBoundingClientRect() will give you an unrotated bounding rect but getScreenCTM will give you a transformed offset, since you're not doing that you can use either currently.
The 8 is the difference, i.e. the position of the element element on the page. That's not consistent with the description but if you want 8 then it's:
$('p').text(document.getElementById("r").getScreenCTM().e -
document.getElementById("r").getCTM().e);

Related

D3.js v7: interpolate translate/scale with new position?

I have a graph of several nodes and edges. I used a layout algorithm (elkjs) to calculate the position of the nodes and edges. The graph is fairly large, with 268 nodes and 276 edges, such that:
svg width: 800, height: 600; graph width: 1844, height: 3007
As such, I had to do the requisite math to calculate pan offsets and scaling so it would fully fit, centered in the viewport:
translate(225.228, 15) scale(0.1896)
I programmatically transition it into place over 2500ms - it works fine and fits nicely.
But then I wish to switch to a subgraph, picking a node to see only it and its descendants. This particular example subgraph is near the bottom of the graph, so the starting position of its y-values are relatively large. After I call the layout algorithm again to get the new positions, the repositioned graph is smaller:
svg width: 800, height: 600; graph width: 874, height: 459
I then do the join/enter/update/exit thing to update the positions over a duration (and remove the other elements not in the subgraph), and I also do the math again for pan offsets and scaling:
translate(20, 100.4348) scale(0.8696)
Here's the problem: while it does end up in the right place, it pans off-screen before panning back on-screen:
I think I see why: the re-positioning transition has the same duration (2500ms) as the panning/scaling, and something about that all combined has that undesirable effect. I'm able to keep the subgraph on-screen by making the positioning happen faster:
rects
.transition().duration(1500) // instead of 2500
.attr('x', (d) => d.x ?? 0)
.attr('y', (d) => d.y ?? 0)
but that's of course not a universal solution, and it kind of moves around haphazardly. I'd rather have a way to interpolate the re-positioning with the translate so it smoothly grows and pans to be centered without going off-screen. Is this possible? I'm aware of d3.interpolate and scaleExtent/translateExtent but am stumped on how to use them - so far I haven't figured out how to account for both repositioning and translate/scale at the same time.
Other relevant bits of code:
<svg
style={{ borderStyle: "Solid", borderWidth: "1px" }}
width="800"
height="600"
onClick={onClickHandler} <!-- this just switches to the subgraph -->
>
<g />
</svg>
const gr = d3.select('g');
const rects = gr.selectAll<SVGSVGElement, ElkNode>('rect').data(elkGraph.children, (d) => d.id);
// update, enter, exit logic omitted; see above for position update
// offset and scale logic omitted
gr.transition().duration(2500)
.attr('transform', `translate(${xCenterOffset}, ${yCenterOffset}) scale(${scaleFactor})`)

Is it possible to have a floating x-axis on a d3js grapgh?

Have a graph that is very long. And I have the x-axis on the top of the graph. Was wondering if it is possible to have a floating x-axis so when the user scrolls down the graph the x-axis stays on the screen for the user to see?
Thank you for your help
In most case in d3js,
developers create a single svg element. But you can separate them; eg. x/y axis and data drawing area. And then You can assign this svg field statically via css to be constant.
var axisSvg = d3js.select('axisSvg').append('svg').attr('class','axisSvg);
var mainSvg = d3js.select('mainSvg').append('svg').attr('class','maingSvg);
and in css
.axisSvg{
position: fixed;
bottom: 0;
right: 0;
width: 300px;
}

rotating svg elements around center

What am I doing wrong to rotate around center
I set transform-origin of each is right in the center of square
Look here pls
https://jsfiddle.net/yqx90or9/1/
group.style.transformOrigin = '100px 100px'
group.style.transform = 'rotate(90deg)'
The problem is this line:
transition: all 1s linear 1s;
When you change the style, you are changing the transform, but you are also changing the transform-origin. So both get animated. The result is your weird behaviour. Change it to:
transition: transform 1s linear 1s;

Algorithm for smooth alpha crossfade?

My application fades between various media and text layers by adjusting their alpha values. However, when using a linear crossfade the brightness appears to "dip" halfway through and then fade back up. After some searching I found this answer that explains the issue, however the suggested solution, fading only one layer at a time, won't work for me since most of the layers I use already contain transparency.
Here's an example of the issue I'm having, in HTML/CSS (code below because SO requires it.
<style>
body, html {
width: 100%;
height: 100%;
margin: 0;
background-color: black;
}
.example {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
#example1 {
background-color: red;
animation: 1s linear 0s fade infinite alternate;
}
#example2 {
background-color: red;
animation: 1s linear 1s fade infinite alternate;
}
#keyframes fade {
from {opacity: 0;}
to {opacity: 1;}
}
</style>
<div id="example1" class="example"></div>
<div id="example2" class="example"></div>
The two divs should fade their opacities back in forth, resulting in a solid red image the entire time. Instead, it appears to dip in brightness.
What is the algorithm or formula for creating a smooth crossfade using alpha? I'm using OpenGL, if that's relevant. (The HTML/CSS snippet was just the easiest way of demonstrating the issue).
Sorry, but it's not possible.
First off, the equation you want is defined here. I'll copy it here in other terms:
outputColor = overAlpha * overColor + (1 - overAlpha) * underColor
If I understand your question correctly, you're looking for a periodic function f(t) for your alpha transition such that:
1 = f(t - 1) + (1 - f(t)) * f(t - 1) = f(t - 1) + f(t) - f(t - 1) * f(t)
The only function that satisfies that equation, at least according to wolfram alpha is the constant 1. And that won't work if you want it to be zero at the beginning, and have it loop infinitely.
Unless you don't want a periodic function, and you just want your fades to look kinda nice. The equation linked above.
There is some good discussion of this topic at this other question.
It's true that there is no perfect solution, other than a step function, but you can mitigate the effects somewhat. The important thing is to have easing functions that cross at a relatively "high" point, rather than at 0.5. See graphs at this answer.

css: max-width or max-size

On my website I have images that have a width of 720 pixels. I want images to be either 95% of the width of the page (max-width: 95%), or 720px if the page width is too large. So, on larger screens, I don't want to blow them up. Is there a way to achieve this in CSS?
You can specify a width and a max-width. The element will respect the width, or shrink if max-width is smaller.
This behavior is described on the MDN.
It prevents the used value of the width property from becoming larger
than the value specified for max-width.
img {
background-color: grey;
border: 1px solid blue;
width: 400px; /* Ideal width */
max-width: 80%; /* Max width */
}
<img src="http://placehold.it/400x400">
Note as indicated in the other answer, you could set width to a percentage and max-width to pixels. The end result is the same: the calculated width of the image becomes the smallest of the two.
To me, specifying the width in pixels makes more sense semantically. After all, you want to have an image of so many pixels wide (related to the image itself), and want to have a constraint on the percentage for smaller screens. But if you write it the other way around, I won't hold it against you. ;-)
GolezTrol has it nearly right but just has the max-width and width values the wrong way round.
Below is the correct elements set as you require.
img {
width: 95%; /* Ideal width */
max-width: 720px; /* Max width */
}
<img src="http://placehold.it/800x800">
Max-width MDN Documentation
Width MDN Documentation

Resources