I am currently working on a stacked barchart using only svg, html and css and no third party library is used for this.
Please refer to this codepen https://codepen.io/a166617/pen/qBXvzQd
As per the stacked barchart, the data gets displayed properly but the count of each bar is not displayed.
the data used for this stacked barchart is as follows
const data = [
{
name: 'Transit',
passed: 2,
skipped: 5,
failed: 22,
untested: 0
},
{
name: 'Access',
passed: 0,
skipped: 0,
failed: 0,
untested: 100
}
];
As per this data, i am trying to display count 2 (for passed) ,5 (for skipped) , 22 (for failed) and 100 (for untested)
Can someone please let me know how to display these counts on their respective barcharts. To be clear, i want to display count on the barchart similar to the one shown in the below screenshot
Just add a <text> element into your bar group.
return (
<g key={Math.random()}>
<rect
width={50}
height={`${height}%`}
fill={bar.color}
x={60} // multiply with the width (50) + 10 for space
y={`${y}%`}
/>
<text
x={70} // visible centre point of your bar
y={`${y + height/2}%`}
dy="1.3em" // adjusts the text y position to adjust for
// text descenders. Makes the vertical centring
// more accurate. Normally 0.3 to -0.35, but has
// an extra ~1em because of the 180deg rotate.
textAnchor="middle" // centre the text horizontall at x
class="bar-label" // styling for this text
>{`${bar.value}%`}</text>
</g>
);
.bar-label {
fill: white;
font-size: 10px;
transform-box: fill-box;
transform: rotateX(180deg);
}
This change is complicated a little by the fact that you've rotated your bar SVGs by 180deg. That causes the text to be upside down. So I have to flip each <text> element back upright.
Related
I have to represent the below array in a chart using Recharts Js library. I want the Y axis to represent the cost property and the X axis to represent the volume property and the chart must be a bar chart (requested by the client).
Basically, the bar must grow in height based on their cost but they also have to grow in height based on their volume and that's what I'm struggling with: I can't find a way to display the bars based on their volume too. In this picture it's possible to see the desired result, it was sent me by the client but be aware that the visual representation might not be exact and consistent with the data.
What I've tried:
I tried to draw the bars "manually" using the shape prop in the Bar component and setting a width based on the volume value but it's just a weak workaround because the scale of the X axis won't be consistent with the bars. I also tried to set the width with the barSize prop but this only affects the height in case of a horizontal chart
Example of data array:
[
{
volume 1500,
cost: -136.06243,
totalVolumeCost: -204093.645
},
{
volume 1000,
cost: -272.12486,
totalVolumeCost: -294166.97366
}
]
Question: How can I set the bars width in order to display the volume value?
What I got so far: https://codesandbox.io/s/overall-cost-implication-forked-1sumje
I tried to accomplish it with a BarChart, but it doesn't seem possible: You can edit the BarGap, but there is no override for the container of each bar.
An alternative that still uses Recharts is this codesandbox:
https://codesandbox.io/s/area-chart-with-variable-rectangle-widths-h8o780?file=/src/App.tsx
The data format needs some modification, but i've written a loop for that:
//Init edited data data
let dataAdjusted = [{ volume: 0, cost: 0 }];
for (let i = 0; i < data.length; i++) {
let currentData = data[i];
dataAdjusted.push({
volume: dataAdjusted.at(-1).volume,
cost: currentData.cost
});
dataAdjusted.push({
volume: dataAdjusted.at(-1).volume + currentData.volume,
cost: currentData.cost
});
}
It works by supplying the fill parameter of the Area element with a gradient:
<Area dataKey="cost" stroke="#FFFFFF" fill="url(#gradient)" />
The gradient takes the modified data and adds gradient stops for each color.
<linearGradient id="gradient" x1="0" y1="0" x2="100%" y2="0">
{dataAdjusted.map(function (d, index) {
let cost = d.cost;
let volume = d.volume;
let percentage = 100 * (volume / totalVolume);
let fill = "#111111";
if (cost > 0) fill = "#d9f7be";
if (cost > 5) fill = "#b7eb8f";
if (cost < 0) fill = "#ffd8bf";
if (cost <= -5) fill = "#ff9c6e";
return <stop offset={`${percentage}%`} stopColor={fill} />;
})}
</linearGradient>
It still needs some labeling work, and probably some corrections in the gradient function, but it should be a start for your problem.
I am trying to draw nested shapes with GoJS library. For now I have rectangle with a single line inside.
Whole Object is a panel with two shapes, a rectangle and a minus line. At this point it is fine, MinusLine is rendered in the center of a rectangle.
What I want to achieve is to position/change location of a MinusLine from top to bottom and etc. based on some conditions that I got however I cannot move it in any way.
For example move perfectly centered YELLOW line to position of RED or BROWN
Code looks like:
GO(go.Node, "Table",
{
layerName: "AfterForeground",
movable: false,
locationObjectName: "BODY",
locationSpot: go.Spot.parse("0.5 0 0 0 "),
selectionObjectName: "MAIN_SHAPE",
selectionObjectName: "MAIN_SHAPE",
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
GO(go.Panel,go.Panel.Position, "Auto", {
row: 1,
column: 1,
name: "BODY",
stretch: go.GraphObject.Fill
},
GO(go.Shape, "Rectangle", {
fill: wellColor,
name: "MAIN_SHAPE",
stroke: myColor,
strokeWidth: 0.4,
}, new go.Binding("fill", "wellColor"),
) , new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)
,GO(go.Shape, "MinusLine", {}), // <-- Move this YELLOW line vertically somehere inside Rectangle
)
Is a Table panel (for the Node's Panel type) really what you want?
So far you have a Table panel that has a single cell, and by default all objects in the cell will sit in the center. You can easily move them by adding the alignment property to the elements.
Here's an example of your table, with two sub-panels added to the Node, one aligned to the top, one aligned to the center with a y offset of 30:
https://codepen.io/simonsarris/pen/zYBrLaX?editors=1011
There are other positioning examples for Table panel, here:
https://gojs.net/latest/intro/tablePanels.html
https://gojs.net/latest/intro/tablePanels.html#StretchAndAlignmente
Note that as you have defined the template, the Shape takes up the entire space of the table, with a minus line in the center of the shape. This is probably not what you want. I put a width and height on the Shape and a (bigger) height on the table to make the demonstration legibile.
What I have
I've created 2 graphs like shown in the first image below. I need these 2 graphs to be perfectly aligned on the 0 axis (which is shown in the section below).
Wanted result
In the image below, the graphs are perfectly aligned and should therefore contain 2 different datasets. One should be placed on the left grid and the other one on the right grid.
The options I thought of
I have not seen a way to implement this in ChartJS. Therefore, I thought of some possible solutions that could solve this issue in a different way.
Because both datasets don't have negative values, I could convert the left dataset to negative values (on the x-axis). The downside is that the range would be incorrect. Also, this would give issues with the popup message if you hover your mouse on a value.
Maybe, two canvas objects can be combined that could result in the wanted result
Question
Is there a way to achieve the wanted result within ChartJS? Or can I perform some wizardry to combine two charts nicely into one? Or should I move to the D3.js library for this particular graph?
I did not find a way to "nicely" implement this. I've changed the source code of the ChartJS library to implement this.
This method uses 2 different graphs that render in 2 different canvas objects. These canvas objects are placed side-by-side to let it look like one graph.
This is my result (The code in here is not exactly like the graph in the picture because I changed it to my specific needs).
I've added a custom variable in the options.layout category named sidePadding.
The code of creating the graph:
new Chart(document.getElementById(this.element), {
type: 'scatter',
data: {
datasets: [{
borderColor: this.lineColor,
borderWidth: 1,
pointBackgroundColor: '#000',
pointBorderColor: '#000',
pointRadius: 0,
fill: false,
tension: 0,
showLine: true,
data: [],
}],
},
options: {
aspectRatio: 0.3,
maintainAspectRatio: false,
responsive: true,
layout: {
sidePadding: {
left: 3,
right: -3,
}
}
}
});
The padding on the sides is by default 3 so it does not touch the border of the canvas. I wanted it to touch the border of the canvas, and therefore set it to -3.
Two different graphs need to be created, one for the left side and one for the right side.
The left graph should have the sidePadding.right set to -3. The right graph should have the sidePadding.left set to -3.
Changes in the ChartJS library
I've worked with ChartJS version v2.9.3. This method will possibly not work anymore in a new version.
I've changed the following in the Chart.js file:
Replace (line number around 11800):
// Adjust padding taking into account changes in offsets
// and add 3 px to move away from canvas edges
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3;
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3;
to:
// Adjust padding taking into account changes in offsets
// and add 3 px to move away from canvas edges
var layoutOptions = chart.options.layout || {};
var sidePadding = helpers$1.options.toPadding(layoutOptions.sidePadding);
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + sidePadding.left;
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + sidePadding.right;
Also you must add the sidePadding.left and sidePadding.right parameter to ChartJS.
This can be done by changing (line number around 7180):
core_defaults._set('global', {
layout: {
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
// Custom added parameter
sidePadding: {
left: 3,
right: 3,
}
}
});
to:
core_defaults._set('global', {
layout: {
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
// Custom added parameter
sidePadding: {
left: 3,
right: 3,
}
}
});
Note
This is probably not the best way to achieve this. Changing the ChartJS library is bad-practice and updating the library could revert these changes.
If someone has a better way to achieve this, please post your methods or comment below.
I have two issue to request for help,
Have an issue with the highcharts, it looks like some calculation needs to be done for the chart, but could not ale to figure out the actual thing.
please look at this Fiddle , In this the label at the right hand side $500 is at the correct position that is ok, but when the value is big/large, lets say $555555555555, then the
chart label goes out of chart. Have a look at this fiddle now
Fiddle having Issue
Error:
what should happen is that the $555555555555 should be inside the chart only, having the same position as show in the first fiddle.
What I tried.
Adding/subtracting the length of the string (point.y) with + 30
var chart = new Highcharts.Chart({
...
....
/*********RIGHT SIDE CPM*******************/
var point = chart.series[0].data[7];
var text = chart.renderer.text(
'$'+point.y,
point.plotX + chart.plotLeft - 30,
// point.plotX + chart.plotLeft - ( ((point.y).toString().length) + 30 ), //My change
...
});
But this point.plotX + chart.plotLeft - ( ((point.y).toString().length) + 30 ), did not support when the value again changes from $555555555555 to $5
so Basically the position should not vary when the length of the label is big/small.
My next question, The chart is starting with a small displacement at both sides.
have a look at the image below
As you can see the gap between the two red lines, at both the corners in the image, I need to remove that, It may be a padding issue, but I could not able to figure it out.
Please help me in this two issues.
Thanks for helping in advance.
Issue: Aligning rendered text
So from reading your code I think you are trying to adjust the position of the text and box from the length of the content. Problem here is you have a font that has variable width characters while subtracting a constant value per character. Also, the renderer.text method always anchors in the bottom left, which isn't in your favor.
What I would do here is leverage that the text.getBBox knows the exact bounding box of the text Element. You might be able to get this to look a bit better, but here's a crude example:
/*********RIGHT SIDE CPM*******************/
var point = chart.series[0].data[7];
// Draw dummy text, just to get bounding box
var text = chart.renderer.text('$'+point.y, 0, 0)
.attr({
zIndex: 5
}).add();
// Get bounding box
var box = text.getBBox();
// Destroy dummy text
text.destroy();
// Draw actual text based on bounding box
text = chart.renderer.text(
'$'+point.y,
point.plotX + chart.plotLeft - box.width + 2 ,
point.plotY + chart.plotTop - 10
).attr({
zIndex: 5
}).add();
// Get actual box
box = text.getBBox();
// Draw actual box
chart.renderer.rect(box.x - 5, box.y - 5, box.width + 10, box.height + 10, 5)
.attr({
fill: '#FFFFEF',
stroke: 'gray',
'stroke-width': 1,
zIndex: 4
})
.add();
And a JFiddle example (I only bothered to fix your right side CPM, but you get the point).
Issue: Spacing on sides of graph
Set the min and max of your x-axis to specific values, as shown in the JFiddle example, with the code:
xAxis: {
...,
min: 1,
max: 8
}
I have a simplest highchart donut chart question. The question I have is what is the default radius of this donut chart. I have not specified the radius anywhere, but seems to have a default radius set somewhere. I could not figure out how this radius was set and was wondering if somebody could help me understand how this radius is set. I did go thru the api reference but could not find this info. Not sure if I missed something.
Here's a demo
$(function () {
$('#container').highcharts({
chart: {
type: 'pie'
},
plotOptions: {
pie: {
startAngle: 90,
animation: false,
innerSize: '60%'
}
},
series: [{
data: [
['Firefox', 44.2],
['IE7', 26.6],
['IE6', 20],
['Chrome', 3.1],
['Other', 5.4]
]
}]
});
You cannot find out size value using options or API because it seems there is no any for that. It seems that you cannot find it out using for example:
var chart = $('#container').highcharts();
console.log(chart);
and inspecting properties. You will find out that size is set to null.
You can find out size inspecting DOM svg elements. To make my job easier I changed option
innerSize: 180
and find out following svg elements (Note: it is not circle element):
...
<path fill="#8bbc21" d="M 532.7575883045118 30.763342348475817 A 140 140 0 0 1 685.1378163408548 95.11881271952285 L 642.2314533619781 120.79066531969326 A 90 90 0 0 0 544.2727353386148 79.41929150973445 Z" stroke="#FFFFFF" stroke-width="1" stroke-linejoin="round" transform="translate(0,0)" visibility="visible"></path>
<path fill="#910000" d="M 685.2096374472518 95.23898645643143 A 140 140 0 0 1 696.8833100589593 120.0234896156345 L 649.7821278950453 136.8008147529079 A 90 90 0 0 0 642.2776240732333 120.86791986484879 Z" stroke="#FFFFFF" stroke-width="1" stroke-linejoin="round" transform="translate(0,0)" visibility="visible"></path>
...
Those are svg path elements which build slices of pie chart donuts. A 140 140... and A 90 90... are elliptical arc command and 90,90 is rx, ry for inner size, and 140,140 for outer size. So radius is 140 in this case.
I don't know how it is calculated exactly but radius is calculated according to your container size and all different margins, plot borders, spacing... if you do not set size using option size.
It doesn't have a radius option it seems. But there is an option:
width: Number
An explicit width for the chart. By default the width is calculated from the offset width of the containing element.
Try it: 800px wide
Refer to this link
No radius option, but there is a diameter. You can express it in pixels (given by number) or as a percent of the chart area (give as string 'N%'):
size: String|Number The diameter of the pie relative to the plot area.
Can be a percentage or pixel value. Pixel values are given as
integers. The default behaviour (as of 3.0) is to scale to the plot
area and give room for data labels within the plot area. As a
consequence, the size of the pie may vary when points are updated and
data labels more around. In that case it is best to set a fixed value,
for example "75%". Defaults to .
Here's your fiddle with the pie chart 150% of the chart area.