How to find the radius of the piechart/donut chart - javascript

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.

Related

Is it possible to display bars based on two values in Recharts Js?

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.

unable to display count on the barchart (svg graph)

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.

Add text to SVG path dynamically

I have an SVG exported from Adobe Illustrator with several paths like this, which produces a small polygon I intend to use as a text box
<svg viewbox="387 390 74 20">
<g>
<path class="st37" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0 C460,404.42,456.42,408,452,408z" />
</g>
</svg>
I'd like to dynamically add text to it. I've seen many similar questions here, but most of them involed specifying a x and y property for a text element based on the x and y property the path element. My path, however, does not have such properties.
I've tried to use a textPath element with xlink:href pointing to my path. I gets attached to the path, but the text wraps my path element instead of being inside it.
So, is there a way to achieve this? I'm open to different solutions here. My dream would be to use javascript to get the path element and add the text from there. But I could also edit the base SVG file to add a text or any other relevant element and attributes to make this work, as long as I can change the text dynamically from javascript later. And since this SVG is produced by Illustrator, I could also try different export options there in order to get a proper output for my goal.
Here's some sample code that takes a label path and adds a <text> element after it with whatever text you choose.
let label1 = document.querySelector("#label1");
addLabelText(label1, "Something");
function addLabelText(bgPath, labelText)
{
let bbox = bgPath.getBBox();
let x = bbox.x + bbox.width / 2;
let y = bbox.y + bbox.height / 2;
// Create a <text> element
let textElem = document.createElementNS(bgPath.namespaceURI, "text");
textElem.setAttribute("x", x);
textElem.setAttribute("y", y);
// Centre text horizontally at x,y
textElem.setAttribute("text-anchor", "middle");
// Give it a class that will determine the text size, colour, etc
textElem.classList.add("label-text");
// Set the text
textElem.textContent = labelText;
// Add this text element directly after the label background path
bgPath.after(textElem);
}
.st37 {
fill: linen;
}
.label-text {
font-size: 10px;
fill: rebeccapurple;
transform: translate(0, 3px); /* adjust vertical position to centre text */
}
<svg viewbox="387 390 74 20">
<g>
<path id="label1" class="st37" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0 C460,404.42,456.42,408,452,408z" />
</g>
</svg>
Since you can edit your base SVG align
create a proper SVG to work with
Your path is a background label starting (red circle) at a large offset x=452 y=408
I would recreate it,
starting at the green circle, (editor: https://yqnn.github.io/svg-path-editor/)
in a viewBox="0 0 80 20"
To get single coordinates for both backgroundlabel and (blue) textPath line
after that use JavaScript to add text dynamically.
No need for text x,y calculations, pathLength and startoffset do the work
Or if you go fancy you can create the blue line dynamically from getBBox()
That will also work with your current path; just more calculations required
It is all about coordinates,
and positioning the blue line (with stroke="transparent" then):
playground:
<svg viewbox="387 390 74 20">
<path fill="lightgreen" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0C460,404.42,456.42,408,452,408z" />
<circle cx="452" cy="408" r="2" fill="red"/>
<circle cx="388" cy="400" r="2" fill="green"/>
<path id="P" pathLength="100" d="M388 400h72" stroke="blue"/>
<text>
<textPath href="#P" startoffset="50" text-anchor="middle" dominant-baseline="middle"
fill="green" font-size="14px">My Text</textPath>
</text>
</svg>
Thanks for the answers! I end up using a tweaked version of Paul LeBeau's function to take into account the structure suggested by Danny '365CSI' Engelman so I don't have to use translate to center the text vertically.
let label = document.querySelector("#mylabel");
addLabelTextPath(label, "Something");
function addLabelTextPath(bgPath, labelText) {
let bbox = bgPath.getBBox();
let x = bbox.x + bbox.width / 2;
let y = bbox.y + bbox.height / 2;
// Create the path line
let pathElem = document.createElementNS(bgPath.namespaceURI, "path");
pathElem.setAttribute("pathLength", 100);
pathElem.setAttribute("d", `M${bbox.x} ${y}h${bbox.width}`);
pathElem.id = `baseline-${bgPath.id}`;
// Create a <text> element
let textElem = document.createElementNS(bgPath.namespaceURI, "text");
// Create a <textPath> element
let textPath = document.createElementNS(bgPath.namespaceURI, "textPath");
textPath.setAttribute("href", `#${pathElem.id}`);
//Center text
textPath.setAttribute("dominant-baseline", "Middle");
textPath.setAttribute("startOffset", 50);
textPath.setAttribute("text-anchor", "middle");
// Give it a class that will determine the text size, colour, etc
textPath.classList.add("label-text");
// Set the text
textPath.textContent = labelText;
// Add the elements directly after the label background path
bgPath.after(pathElem);
pathElem.after(textElem);
textElem.appendChild(textPath);
}
.st37 {
fill: lightblue;
}
.label-text {
font-size: 10px;
fill: darkblue;
}
<svg viewbox="387 390 74 20">
<g>
<path id="mylabel" class="st37" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0 C460,404.42,456.42,408,452,408z" />
</g>
</svg>

Is path animation possible with SVG.js

There are many examples of SVG path animation, both natively
http://jsfiddle.net/FVqDq/
and with Raphael.js
http://jsfiddle.net/d7d3Z/1/
p.animate({path:"M140 100 L190 60"}, 2000, function() {
r.animate({path:"M190 60 L 210 90"}, 2000);
});
How is this possible with the svg.js library?
No, this is not yet possible with svg.js. I have been looking into it and it will be a rather large implementation. As I try to keep the library small it will never be part of the library itself, but I might write a plugin. Although at the moment I do not have much time on my hands so all help will be appreciated.
UPDATE:
This is now possible with SVG.js out of the box if you use paths with equal commands but different values.
But we also have a path morphing plugin for SVG.js which is probably the thing you are looking for.
There is a quick and dirty way to animate a line with svg.js:
http://jsfiddle.net/c4FSF/1/
draw
.line(0, 0, 0, 0)
.stroke({color: '#000', width: 2})
.animate(1000, SVG.easing.bounce) // Using svg.easing.js plugin(not required)
.during(function(t, morph) {
this.attr({x2:morph(0, 100), y2: morph(0, 100)})
})
Animating complex SVG paths as wout said will require a plugin.
Unfortunately I don't (yet) know enough about SVG, but I'm thinking of writing a plugin which would use the SMIL animation tag. Which is what is used in the first link of the question.
We can make path animation by finding the bounding box of your path and the do like this.
if your path having some clipping -rectangle means like that below
<g id="container_svg_SeriesGroup_0" transform="translate(128.8,435)" clip-path="url(#container_svg_SeriesGroup_0_ClipRect)"><path id="container_svg_John_0" fill="none" stroke-dasharray="5,5" stroke-width="3" stroke="url(#container_svg_John0Gradient)" stroke-linecap="butt" stroke-linejoin="round" d="M 0 -17.25 L 21.7 -112.12499999999999 M 21.7 -112.12499999999999 L 43.4 -51.75 M 43.4 -51.75 L 86.8 -25.875 M 86.8 -25.875 L 108.5 -155.25 "/><defs><clipPath id="container_svg_SeriesGroup_0_ClipRect"><rect id="container_svg_SeriesGroup_0_ClipRect" x="0" y="-155.25" width="118.5" height="148" fill="white" stroke-width="1" stroke="transparent" style="display: inline-block; width: 118.5px;"/></clipPath></defs></g>
var box = $("#"+ path.id")[0].getBBox();
create the rectangle based on the box and the set this rectangle as your clip-path in path.
then increase the width of the rectangle step by step in jquery.animate.
doAnimation: function () {
//cliprect is your clipped rectangle path.
$(clipRect).animate(
{ width: 1000},
{
duration: 2000,
step: function (now, fx) {
$(clipRect).attr("width", now);
}
});
},
jquery.animate step function is used to increase the width of your clip-rect step by step.
You can animate paths using the svg.path.js plugin.
See the first examples (using the .drawAnimated method).
Another option, which we've resorted to, is to use textPath and then use a character.
In our case we're using the • entity, but I'm thinking if you create your own typography in .svg, .woff etc, you can have flat shapes of any kind.
So you would use your character as in here:
http://jsfiddle.net/wbx8J/3/
/* create canvas */
var draw = SVG('canvas').size(400,400).viewbox(0, 0, 1000, 1000)
/* create text */
var text = draw.text(function(add) {
add.tspan('•').dy(27)
})
text.font({ size: 80, family: 'Verdana' })
/* add path to text */
text.path('M 100 400 C 200 300 300 200 400 300 C 500 400 600 500 700 400 C 800 300 900 300 900 300')
/* visualise track */
draw.use(text.track).attr({ fill: 'none'/*, 'stroke-width': 1, stroke: '#f09'*/ })
/* move text to the end of the path */
function up() {
text.textPath.animate(3000).attr('startOffset', '100%').after(down)
}
/* move text to the beginning of the path */
function down() {
text.textPath.animate(3000).attr('startOffset', '0%').after(up)
}
/* start animation */
up()

Raphael JS Pie: Add ID to path slices

I've seen this question asked over at the Raphael Google Groups, but after hours of searching there, and also on here, and Google, I cannot seem to find a solution.
I would simply like to be able to target my pie chart (svg path) slices using jQuery, but I cannot figure out how to add custom id's to the path tags - there is no ID attribute in there by default:
<path fill="#764c29" stroke="none" d="M350,350L350.6911881148345,94.00093308961084A256,256,0,0,1,561.8463375189659,206.2741175716762Z" style="stroke-width: 1; stroke-linejoin: round;" stroke-width="1" stroke-linejoin="round"/>
What would be ideal would be this:
<path **id="my_id"** fill="#764c29" stroke="none" d="M350,350L350.6911881148345,94.00093308961084A256,256,0,0,1,561.8463375189659,206.2741175716762Z" style="stroke-width: 1; stroke-linejoin: round;" stroke-width="1" stroke-linejoin="round"/>
Has anyone got an idea how this could be achieved?
This is the code I'm using to create the pie chart:
window.onload = function () {
var r = Raphael("holder");
var pie = r.g.piechart(350, 350, 256, [56, 104, 158, 23, 15, 6]);
pie.hover(function () {
this.sector.stop();
this.sector.animate({scale: [1.1, 1.1, this.cx, this.cy]}, 500, "bounce");
}, function () {
this.sector.animate({scale: [1, 1, this.cx, this.cy]}, 500, "bounce");
});
};
Essentially, the reason I need to be able to do this, is so I can create some separate anchor triggers to perform the scale animations shown above.
Any help greatly appreciated.
the piechart object provides 3 ways to reach their sectors.
1) each function
pie.each(function(sector, cover, i) {
sector.attr({/*...*/}); //raphael
$(sector.node).foo(); //jquery
});
2) series object (for styling and transforming)
var i = 0; // 0 = 56, 1 = 104, 2 = 158 …
//raphael way to hide the first sector
pie.series.items[i].attr({ opacity : 0 });
//jquery way to hide the first sector
$(pie.series.items[i].node).hide();
whereby i is the index of your data-array
demo: http://jsbin.com/eriqa5/2/edit
3) covers object (for mouse and touch events)
//raphael way to hover the first sector
pie.covers.items[0].hover(...);
//jquery way to hover the first sector
$(pie.covers.items[0].node).hover(...);
demo: http://jsbin.com/eriqa5/4/edit

Categories