Closed s4b2x closed 1 year ago
Thanks for the detailed report.
It seems that scale
applies to child elements too, so the scale
is first applied to the chart-container-scaled
element (reducing its size by half) and then it's applied a second time to the chartdiv
, so the chart ends up being a quarter of its original size.
So even though the size is correctly calculated by amCharts, the browser behavior of scale
causes it to break:
https://stackoverflow.com/questions/27051600/css-transform-scale-dont-scale-child-element
Why are you using transform: scale(0.5)
in the first place?
Why are you using transform: scale(0.5) in the first place?
The reason I use scale
(not exactly by 0.5 - that was just an example) is this: I am building a web application that lets the user configure a slideshow. Some slides may contain charts (preferaby amcharts - awesome features!). The config page has a preview area with the current slide. That area is not fullscreen because we need a navigation, inputs for adjusting the slide contents and so on. But the preview should of course show the slide as it would look like in fullscreen, just smaller. That is the perfect usecase for transform: scale()
.
So even though the size is correctly calculated by amCharts, the browser behavior of scale causes it to break
I know how transform: scale()
works. It doesn't get applied "a second time" to child elements. transform
just applies to the complete element and all its properties. Imagine a div that's transform: translate
ed. It wouldn't make sense if its content would stay unmoved.
See also the middle part of this introductory sentence from the Mozilla Web Docs:
By modifying the coordinate space, CSS transforms change the shape and position of the affected content without disrupting the normal document flow.
The image in the second DIV in my example below gets scaled along with the rest of the DIV. If now I would measure the displayed size and apply this as an inline style we would have the same symptom like I described in my first post. See the last DIV below.
I know how transform: scale() works. It doesn't get applied "a second time" to child elements. transform just applies to the complete element and all its properties.
Let me explain what I mean. amCharts displays in a <canvas>
, and amCharts needs to determine the size of the canvas. It uses getBoundingClientRect()
to figure out the width / height, and then uses that for the canvas.
So in your example, amCharts uses getBoundingClientRect()
on the chartdiv
and the width / height is 250px. So it sets the canvas's width / height to 250px.
However, because the parent has scale(0.5)
the browser resizes the canvas so that it's 125px even though it has specified 250px. That's why it is being displayed too small.
Imagine a div that's transform: translateed. It wouldn't make sense if its content would stay unmoved.
The content does stay unmoved. The transform
CSS style only changes the visual appearance, but the actual size / position stays unchanged. It's purely a visual illusion. Here is an example demonstrating that:
https://codepen.io/team/amcharts/pen/JjaKevB/ae1cffeb57a9bcbfe13a8e67c2f0f2cb
Even though the red box has been "resized" with scale(0.5)
it isn't actually resized, which is why there is a gap between the red and green box. This is different from width: 50%; height: 50%
(which actually resizes the box).
It's very difficult for amCharts to support transform
, because getBoundingClientRect()
always takes into account transform
, and there isn't any way to prevent that. Other libraries have the same problem. We can't use clientWidth
/ clientHeight
because that has other bugs which break things.
And it's basically impossible for us to support things like rotation / skew.
In addition, because scale
doesn't actually resize the element (it only changes the visual appearance), that means it doesn't trigger resize observers, which means that amCharts won't be able to detect any changes to the scale
, which means it can't properly redraw the canvas.
That also means that hit detection is completely messed up if you use transform: translate
, and there's nothing we can do to fix that.
I got this issue too. Will amcharts provide a option to choose clientWidth/clientHeight
or getBoundingClientRect
?
The content does stay unmoved. The
transform
CSS style only changes the visual appearance, but the actual size / position stays unchanged. It's purely a visual illusion.
Of course I meant "visually unmoved".
However, because the parent has
scale(0.5)
the browser resizes the canvas so that it's 125px even though it has specified 250px. That's why it is being displayed too small.
That sounds like it was a browser bug, which it is not. It is the intended behaviour that an element with all its child elements gets transform
ed as one.
It's very difficult for amCharts to support transform, because getBoundingClientRect() always takes into account transform, and there isn't any way to prevent that. https://github.com/okonet/react-container-dimensions/issues/5 have the same problem. We can't use clientWidth / clientHeight because that has other bugs which break things.
That's a pity. Especially when considering that it worked back then in v5.1.7:
v5.1.7 | v5.2.31 |
---|---|
That also means that hit detection is completely messed up if you use transform: translate, and there's nothing we can do to fix that.
Hit detection is not an issue for us because the slides won't be interactive.
For now I have pinned the node module version to 5.1.7. Maybe we will implement a workaround for the getBoundingClientRect
"feature" in our project.
That's a pity. Especially when considering that it worked back then in v5.1.7:
It worked because we used clientWidth
, but that causes other scaling issues. Things like clientWidth
are very old, which is why it doesn't support transform
, but that also means it has other issues which getBoundingClientRect
doesn't have.
After doing a lot of brainstorming, we decided upon this solution:
let scale = 2;
const root = am5.Root.new("chartdiv", {
calculateSize: (dimensions) => {
return {
width: dimensions.width * scale,
height: dimensions.height * scale,
};
},
});
This will cause the chart to display twice as big, to compensate for the scale(0.5)
.
You will have to adjust the scale
yourself, based on the transform
, and you will have to manually call root.resize()
when changing the transform
.
This new feature will be in the next version.
Thankyou, this will be fine!
Iplemented in 5.3.7.
calculateSize
setting on Root
. This is needed if you are using a transform: scale(...)
CSS style. More info.DateRangeSelector
: minDate
and maxDate
. Allows restricting date selection to specific date ranges. More info.StockChart
: drawingsupdated
and indicatorsupdated
. Events kick in when drawings or indicators are added, updated, or removed. More info.DateRangeSelector
will now limit dates to actual range of data. To disable set minDate
and maxDate
to null
. More info.XYSeries
were creating Bullet
objects even if distance between them was less than minBulletDistance
which was not efficient and could slow the browser down.ghostLabel
obstructing interactivity for other labels.Make sure you clear your browser cache after upgrading. And feel free to contact us again if you are still experiencing this issue.
@Pauan @martynasma Well... I'm not still experiencing this issue... but..
After upgrading (and clearing the cache of course) the chart contents seem to be "corrected" two times which leads to the result that it is scaled as if it wasn't scaled at all - but limited to the area of the scaled chart.
unscaled slide | scaled slide in preview area |
---|---|
(Please ignore the missing graph - the data I'm testing with actually only has timestamps atm.)
The devtools show different sizes on the canvas attributes and style:
Btw.: both ways from the docs (scale factor or clientWidth/-Height) have the same result.
@Pauan @martynasma I forked my codepen to show the problem and how it should look like in my opinion:
https://codepen.io/sebu10n/pen/ExeEpeZ
Interesting discovery: When you click on "Embed" in a codepen, you can scale the result. That works fine with amCharts. They use an iframe. Maybe we have to use an iframe as well.
The chart size behaves weird when it is inside a scaled (
transform: scale(0.5);
50% scaling only as an example) container:https://codepen.io/sebu10n/pen/ZEMQeWZ
This works fine in
"@amcharts/amcharts5": "5.1.7"
- not anymore in v5.2 or later - meaning the chart just gets scaled like any other element.amcharts now applies a wrongly calculated size to the canvas elements and some other (div) elements:
In this case
.container-500x500
is 500px x 500px,.chart-container-scaled
is the same size but scaled. The inner elements get the scaled size applied what scales them down one step too far.Environment
I noticed the bug using node, nuxt, vuetify... but it can be reproduced with vanilla JS.
Bug started at around amcharts@5.2