I'm making a tooltip lib. It uses a styled <div>.
The red colored one. It was transformed 45 degree and clipped off.
I need a dynamic calculation of height for the clipped-red one above.
In this case the height is exactly getBoundingClientRect().height / 2,
the built in function is transformed aware but not clipped aware.
The clip-path property is might be customized, so we cannot just simply dividing the height by 2.
I need a custom js function something like: getClippedBoundingClientRect()
Related
I recently came across this video from a tutorial series on D3 + React. In this video, to resize the graph, a custom hook is created that uses ResizeObserver:
const useResizeObserver = ref => {
const [dimensions, setDimensions] = useState(null);
useEffect(() => {
const observeTarget = ref.current;
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
setDimensions(entry.contentRect);
});
});
resizeObserver.observe(observeTarget);
return () => {
resizeObserver.unobserve(observeTarget);
};
}, [ref]);
return dimensions;
};
I've always seen previously / used before the viewBox attribute to make SVG's responsive, as this article (and many others) discusses. My question is: is there a reason (performance, simplicity) to use this useResizeObserver custom hook over simply using viewBox? With viewBox, I don't need to create a custom hook, or have a wrapper div around my SVGs (as he explains in the video), or have to use refs... viewBox seems simpler. However, apparently ResizeObserver is a new API to get the dimensions of elements, and perhaps there are advantages to using it over viewBox that are not obvious.
Issues with viewBox
Although viewBox is the quickest solution, it has several downsides:
viewBox tries to satisfy the aspect ratio. If your chart doesn't have an aspect ratio of 1:1, this happens:
The chart didn't fit itself to the container. Instead viewBox kept the aspect ratio and added padding. We can stretch with preserveAspectRatio=none:
Chart (and text) scaling. Not preserving aspect ratio introduces another problem. If you use height and width, you can define your text to be a certain size in CSS - but not with viewBox. Take the case of a 0 0 600 300 viewBoxed chart fitting a 1200*600 page. In this case, everything gets scaled, including text, as you can see above: you can no longer set the font size. This won't happen just to text: other elements that don't stretch perfectly, like circles, will have the same issue.
At first glance, viewBox seems much simpler. But because of the footguns highlighted above, viewBox becomes very complex to use in practice.
Manual scaling
The solution is to size the chart manually using height and width, avoiding all the above issues. Say we have a chart component that renders itself based on height and width. The process becomes:
set the width and height on the first render, fit to the container. The container needs its own width and height set.
Use a ResizeObserver on the container, which will notify us when its size changes.
When the chart is resized, get the new height and width, and use them to render the chart.
When the height and width are set manually, we size only what needs to be scaled. For example, with the above bar chart:
We could set the bars' size relative to the height and width of the chart.
If we have text, we could position it relative to the height and width, but make the text size fixed, which would be impossible with viewBox, since it scales everything:
Conclusion
Use viewBox as a quick solution if you have a chart with elements that don't get affected by stretching: for example, with rectangles, or if you don't care about the above issues.
Use manual scaling if you have elements that cannot be stretched, like text or circles, and need text to stay the size you tell it to.
If you don't care about the aspect ratio issue, and can work around your text being scaled up or down, then viewBox can be the right choice, as long as you're aware of its pitfalls.
However, in practice, manual scaling ends up being the better choice in the vast majority of cases, because there are very little cases where you have only stretchable elements in a chart.
Sources
Images taken from Responsive SVG charts — viewBox may not be the answer.
I'm trying to create a nested pie / donut chart, where the inner ring displays a 'group' (e.g. a car manufacturer) and the outer ring displays a breakdown for that 'group' (e.g. the models made by each manufacturer).
I need the individual segments to be exploded / sliced so that it looks like this:
http://imgur.com/TBtySVa
I have managed to get this working using the sliced and slicedOffset properties (the image above is actually a screenshot of my chart), however this creates strange effects (see the fiddle) when there are fewer 'groups'.
I have put together a fiddle to demonstrate how the chart looks odd when there are fewer groups in the inner ring. It looks really bad when there are only one or two items in the inner ring:
http://jsfiddle.net/danielcrisp/784jzLe2/
I would like to know if there is a better way of achieving the result I require? Probably sliced is not the right way to go as it isn't its intended use. How else can I get a gap between items?
Note: the chart will be displayed over a photo so I can't use borders to create the effect.
Update: It's ok if the spacing between segments is regular, e.g. 10px, unlike the irregular spacing shown in the first screenshot.
Transparent borders should be the perfect solution but they don't mask the segment fill colour unfortunately.
Thank you!
You can add some dummy data points that will be transparent. This solution will need some calculations for good visual results.
jsFiddle: http://jsfiddle.net/25acys4j/4/
Example of transparent slice:
{x: 0,
y: 3,
color: 'rgba(0,0,0,0)'
},
Try to adapt donut chart, and border like this: http://jsfiddle.net/25acys4j/. The border can get a transparent color, when you define it as rgba();
In D3, I want to fill up a vertical bar with 3 different colors. How can this be accomplished?
The bar has a "domain" of 0 to 3.
Red from 0 to 1.5.
Blue from 1.5 to 2.
Yellow from 2 to 3.
I can't figure out how to accomplish this. It seemed really simple, I would use 3 <rect>, each one would have a Y of the lower bound, and a height of the range distance.
This seemed to fail, since for the final bar, my scale returns a height of 0. Am I using scales wrong?
http://jsfiddle.net/7V8vZ/
Ok here is my solution: http://jsfiddle.net/7V8vZ/1/
Basically I couldn't do it with (min,max) data. I had to use a weird (min, height). This let me figure out where to position the rect, and then how tall to make it. Maybe d3 gives a scale meant for this?
I'm assuming there is a lot d3.js I'm not leveraging. So if you have a better way of doing it, please comment or answer!
I need to do something like this:
This may look quite easy, but there are some requirements:
- the width of the containing div should depend on the text length (is it possible at all in CSS?)
- all circles should be positioned randomly - this is the most diffucult part for me.
As I'm using border-radius for creating circles (setting height, width and border-radius of 50%) I try to create some kind of grid in JavaScript where I iterate through each element and get its dimensions. Then I get the position of previous element (if any) and add them to the current element dimensions. Additionally, adding some margins will help avoid collisions. Is it correct approach?
I'm just looking for a suggestion how to solve my two issues.
Circles that scale based on size of content.
This is something you will need to solve first, because you wont be able to place them anywhere without first knowing their dimensions.
Naturally the size of a DIV expands first by width, then by height. That is, the maximum width of a container must first be utilized before moving on to the height constraint. Because of this, making a circle scale with equal radius may prove to be quite difficult without using a relative averaging.
Relative averaging is finding the average dimensions of your height / width based of the exhisting area of the contianer bounding your content. For example:
The width and height of the DIV bounding your content can be detected with javascript. Let's say youve discovered those properties too be 200px x 20px respectively.
Your total area is width * height so 4000px; But we are trying to acheive a square so we can apply rounded corners and form a rounded circle. We want to find dimensions of a rectangle that will be equal to the same area and then apply those new dimensions.
To acheive the same area with an equal width * height you can do something like:
√ 4000 = 63.2455532
Thus: 63.2455532 x 63.2455532 = 4000
Random placement of DIVs, and avoid collisons between DIVs.
After finding dimensions, you will be able to use a rand on your (X,Y) coordinates for the placement. Push these coordinates and radius onto an array. Use recursion too place the remaining circles on collsion failures. A collision failure would come from an element that has overlapping (X,Y)+radius relative too elements in the array that were pushed successfully.
I'm trying to get border width of a particular element.
Getting border width style setting is pretty easy by simply reading if from current calculated style of an element:
var styles = (
document.defaultView && document.defaultView.getComputedStyle ?
document.defaultView.getComputedStyle(de, null) :
de.currentStyle
);
Reading a particular border value is then rather simple by:
var top = styles.borderTopWidth;
var value = parseFloat(top);
This is all fine and dandy (as long as you don't use IE) and I can get top border width in the value variable. But this number relates to pixels only when border width was set in pixels. If it wasn't (was em for instance) than value has the number of that particular dimension.
I have to get an answer to any of these two questions:
How do I always get border width in pixels?
How do I calculate different units into pixels?
Example
I've prepared a jsFiddle example where you can see various dimensions reported by DOM and jQuery. Run it in different browsers and you'll see the difference in IE. All dimansions in Crome are in integer values while Firefox calculates margin and padding in floats while border in integers.
BTW: Margin, border and padding are all set to 2mm.
Most libraries solve this problem for you, as does YUI3 for example.
If you don't want to use those libraries, then at least you can peak at how they do it ;)
http://developer.yahoo.com/yui/3/api/dom-style-ie.js.html
Awnser contained therein.
You can generally get computed pixel sizes using element.offsetWidth and element.offsetHeight. This is somewhat sensitive if you want to support a range of browsers. In that case, use a library. For example, using jQuery you can get guaranteed pixel dimensions with something like this: jQuery("#theID").width().