How to tell the Highcharts renderer to make the viewport bigger? - javascript

In the past I have drawn tables below the chart based on the series. This works because I can tell Highcharts to vertically align the legend, move it to the left, and I can use the legend height and positioning as well as x-axis tick information to figure out where to draw the table lines:
This takes advantage of the fact that once I set the legend vertical, Highcharts is going to internally set the viewport (viewbox?) large enough that the legend is visible, and since my table aligns with the legend, it's also visible.
Now I have a situation where I need to draw a table below the chart that is not based on the series. I actually moved the legend to be floating in the upper right of the chart because there's only one series and it fits nicely there.
I figured out how to position my table lines based on the x-axis label box dimensions (along with tick information), but my table extends out of the viewport (viewbox?) and is not visible:
If I go after the fact and do
myChart.renderer.box.setAttribute("viewBox", "0 0 1200 1200")
to "zoom out", I can see that my whole table did draw, it's in there in the SVG:
So - how do I tell the Highcharts renderer to extend the viewport/viewbox/canvas (whatever the correct term is) down so that when I add my table, it's visible?
I trigger the table drawing from a redraw event handler, and if it's a question of setting some option/parameter I can easily do the calculations I need to figure out how much to extend beforehand and include that in a chart.update({...stuff...}, true) so that it's the right size already when I go to draw my table.
Or I can mess with the chart.renderer directly in code if that's what it will take.

Ok, figured it out.
chart.renderer has a function to setSize, which will set the overall size of the SVG root element itself, however the SVG root is contained within the "highcharts container" div, which has inline CSS styles to set height and width, and importantly, has overflow: hiddden, so you have to also set the height of the container div in order for the increased SVG to be visible. Luckily that is easily accessible at chart.container.
In my case I need to conditionally draw a table after a zoom in or out, so in my function to actually draw the table I also calculate the height it will need and return that so that I can adjust the SVG root and container div after the table is drawn. It looks a little like this:
const drawTable = async (chart, dataForTable) => {
// figure out how much room the table is going to take up
const extraHeight = (rowHeight * dataForTable.length) + littleExtraOffset
// create the SVG container for the table
const group = chart.renderer.g('bottom-table').add();
// draw all the lines for the table etc and add them to the group, then
return {
group,
extraHeight
}
}
const redrawEventHandler = (event) => {
// do stuff to figure out if we need to draw a table or not
let newWidth = baseWidth;
let newHeight = baseHeight;
if (shouldDrawTable) {
const { dataForTable } = this.props;
drawTable(event.target, dataForTable).then(tableResult => {
// save the table SVG group so we can easily destroy it later
this.tableGroup = tableResult.group;
// calculate the new total height
newHeight += tableResult.extraHeight;
this.chart.renderer.setSize(newWidth, newHeight);
this.chart.container.style.height = `${newHeight}px`;
});
} else {
// no table, newWidth and newHeight are still
// the original height and width
this.chart.renderer.setSize(newWidth, newHeight);
this.chart.container.style.height = `${newHeight}px`;
}
}

Related

GoJS resize every element of diagram

My diagram has elements of fixed size. For example I have couple elements with different sizes: First is 250x200px, second 307x501px etc.
There are lot of panels, shapes etc.
Is there any way to resize/rescale every element on current diagram?
For example I want to double every element size so I would like just multiply it by 2.
diagram.scale = diagram.scale * 2; //resize elements like 250px X 200px to 500px X 400px
I've read about scale and resizing from documentation but scale does not seem work.
I am not attaching any code because it is generic question.
I would think that would work (to double the Diagram's scale) but that isn't exactly resizing elements.
You can programatically go over all nodes:
myDiagram.startTransaction();
myDiagram.nodes.each(function(node) {
node.scale = node.scale * 2;
});
myDiagram.commitTransaction();
Or else resize them by setting their width and height, or setting the width and height of some element inside the node.
Maybe related depending on what you are trying to do: This tool extension demonstrates how to modify the ResizingTool to work with multiple selection: https://gojs.net/latest/extensions/ResizeMultiple.html

How to Change Position of One Element in a Bubble Chart

I'm trying to implement a bubble chart in D3.js with an interaction: selecting one bubble causes it to move to the center of the chart, increase size, and display additional information.
d3-hierarchy has the d3.pack layout, which works well for making bubble charts. I can update the data to increase the size of a circle in the selection. However, I don't see any way to set the position of a circle in the layout and get the other circles to arrange themselves around it. I'd need something like "given the circle at (x, y), create a bubble chart around it", and that doesn't exist in d3.pack.
d3-force can also be used to make bubble charts. If I add in some interaction similar to this example using d3.drag, I can make a circle move towards the center like so:
function dragstarted(event) {
const interX = d3.interpolate(event.x, dimensions.width / 2);
const interY = d3.interpolate(event.y, dimensions.height / 2);
if (!event.active) simulation.alphaTarget(0.3).restart();
(function toCenter(i) {
setTimeout(function() {
event.subject.fx = interX(i / 100);
event.subject.fy = interY(i / 100);
i++;
if (i < 100) {
toCenter(i);
} else {
event.subject.fx = null;
event.subject.fy = null;
}
}, 1)
})(1);
}
This only kind of works - it moves the circle to the center, but it runs the force through a few ticks so the circle might not actually end up at the center of the chart. Also, unlike updating data, I don't know how to reset the circles to their original positions when a user wants to dismiss the expanded circle.
My question is, how do I get a circle in a bubble chart to move to the center and have the other circles rearrange themselves? Is there some change I can make to d3.pack or d3.force that would work, or should I be trying a different approach? Another library, even?

How to expand chart to match width of container using ngx-charts

I'm trying to use ngx-charts to display an area chart that spans the full width of my page (it's supposed to match the width of the horizontal line above it). However, it appears that when generating a chart there is some sort of padding inside the svg, which I imagine is useful if you have a legend etc but I am not using that, here's what I see:
See how the actual area chart doesn't expand the full width?
My code:
<ngx-charts-area-chart
[scheme]="colorScheme"
[results]="heatmaps"
[curve]="curve"
[showGridLines]="showGridLines"
[tooltipDisabled]="tooltipDisabled"
(select)="onSelect($event)">
</ngx-charts-area-chart>
And my config variables in my component.ts
curve = d3.curveNatural;
showGridLines = false;
tooltipDisabled = true;
colorScheme = {
domain: ['#3f3f3f']
};
As you can see I am not using the view attribute so the chart should expand to the width of the page. I was thinking I could utilize a viewBox on the svg but not quite sure, any thoughts?
Thanks!

In Highcharts, how to redraw chart after legend item width resized?

The requirements are:
1. "ellipsis" visual effect "..." for overflowed legend items' text.
2. After resized, the legend item width should adjust with the new container size. Means if the legend item had text "blahblahblahblah", and after resizing, the chart container get smaller and have no enough space to display the full text, it should display "blahblah...".
I've figured out the 1st requirement by giving the legend itemWidth a fixed width in pixel, and set the textOverFlow to "ellipsis":
chartOptions.legend.itemStyle.width = $container.width() + "px";
chartOptions.legend.itemStyle.textOverflow = "ellipsis";
But for the 2nd requirement, I changed the chart.legend.itemStyle.width to the current container width, and call the chart.redraw(), and it doesn't really redraw the chart.
`chart.legend.itemStyle.width = $container.width() + "px";
`chart.redraw()`
Is there any way I can update the chart with new legend.itemStyle.width?
Jsfiddle: https://jsfiddle.net/scottszb1987/z5qv1t80/
Cheers
In your case, I'd suggest adding a window .resize() event and then producing a new version of the chart with the updated options. That way, your legend size will pull in the new width of the container element.
// whenever the window is resized, destroy the chart and redraw it using updated options
$(window).resize(function() {
chart.destroy();
chart = new Highcharts.Chart(getChartOptions($("#container")));
});
Here's an updated version of your fiddle with this change: https://jsfiddle.net/brightmatrix/z5qv1t80/5/
I hope this is helpful for you.

Responsive Absolute Positioning

I am trying to add a hover effect to each speech bubble in the image below that link to a different page when clicked on.
Currently, I am using an image map and whenever an area of is hovered over jQuery changes the whole image to be the appropriate image that has the speech bubble filled in. It kind of works but IE flickers every time a hover happens and the website is responsive so the image map does not scale when the screen size is changed. I've tried using https://github.com/stowball/jQuery-rwdImageMaps too but it doesn't seem to work on a mobile device.
Ideally I'd like to be able to have the speech bubbles be separate images that are positioned correctly when scaled so I can manipulate the speech bubbles easier.
Screenshot
You could go with approach which uses scaled coordinates.
Here's the basic idea:
var coordManager = {
"imageBaseHeight":800,
"imageBaseWidth":800,
"imageID":"myImg",
"baseCoordinateActions" = [
{"x":10,"y":10,"h":100,"w":100, "text": "Mousing over first option"}, // the square you want the mouseover to cover for a given action
{"x":200,"y":200,"h":100,"w":100, "text": "Mousing over first option"}
],
"scaledCoordinateActions" : [], // this should contain the baseCoordinateActions with the scaled values
"init" : function(id, baseHeight, baseWidth)
{
var self=this;
this.imageID=id;
this.imageBaseHeight=baseHeight;
this.imageBaseWith=baseWidth;
var img = document.getElementById(id);
img.onresize =function()
{
// regenerate the scaled coordinates based on the difference between the imageBaseHeight and the current height;
// usually you can get the scale by dividing the imageBaseHeight by the actual height
};
image.onmousemove = function(event)
{
// check the mouse location based on scaled coordinates if it's within the scaled coordinates of any of the scaledCoordinateAction items
// display that scaleCoordinateAction item's text using the current eventX and Y, or do it relative to the coordinateACtion X and Y.
// make sure to check if the bubble is already showing before changing the display: property to avoid flicker.
}
}
};
coordManager.init("imageID", 800,800);

Categories