how to build a 3d donut chart - javascript

I wonder if it's possible to build a 3d donut chart in html.
I have found a interesting link here but infortunatly i need to add links (or javascript event) when clicking to launch a ajax request.
Have you ever done such a thing ?
Thanks for your answers

See the following example I've just made:
http://jsfiddle.net/baQCD/3/embedded/result/
The key point (pun intended) is to add a url key for each row (object) in the data array, and use it in the 'click' event handler:
point: {
events: {
click: function(e) {
location.href = e.point.url;
e.preventDefault();
}
}
},
In your case instead of opening a new url, you could do your ajax request or do anything else. In my example I've shown how to manipulate the data and title.
click: function(e) {
if (this.name == "Randomize!") {
sliceK = getRandomInt(0,chart.series[0].data.length-1);
chart.options.series[0].data[sliceK].y = getRandomInt(1,30);
chart = new Highcharts.Chart(chart.options);
} else if (this.name == "Link") {
location.href = this.url;
e.preventDefault();
} else {
chart.setTitle(null,{text:this.name + " clicked"});
}
}
You can immediately see, 2 features I very like in Highcharts, the ability to print or download the chart, and the ability to disable part of the data (removing it from the chart) by clicking on the legend.
This is based on the code shown in:
http://birdchan.com/home/2012/09/07/highcharts-pie-charts-can-have-url-links/
http://www.highcharts.com/demo/3d-pie-donut/

this is a simple 3d Axonometric class i wrote for testing, its very simple it puts the canvas transformation into a plane of zy or zx or yx... it uses canvas setTransform
you first have to call the axionometric class with phi and theta the angles of view
get_bd is a function where you can enter x,y,z coordinates and the method returns an object with b and d value... b is the x of the screen and d is the y of the screen.
i have appended and example, you just have to put a canvas tag in the html with id canvasView
//3d Maths - Axonometric -- Artner Thorsten -- Austria -- Wiener Neustadt
var context=document.getElementById("canvasView").getContext("2d");
function Axonometric (phi,theta)
{
var cosPHI=Math.cos(phi);
var sinPHI=Math.sin(phi);
var cosTHETA=Math.cos(theta);
var sinTHETA=Math.sin(theta);
this.cosPHI=cosPHI;
this.sinPHI=sinPHI;
this.cosTHETA=cosTHETA;
this.sinTHETA=sinTHETA;
this.phi=phi;
this.theta=theta;
}
Axonometric.prototype.get_bd=function (x,y,z)
{
var b=y*this.cosPHI-x*this.sinPHI-500;
var d=x*this.cosPHI*this.cosTHETA+y*this.sinPHI*this.cosTHETA-z*this.sinTHETA+500;
return {b:b,d:d};
}
Axonometric.prototype.plane_zy=function (x)
{
context.setTransform (0,this.sinTHETA,-this.cosPHI,this.sinPHI*this.cosTHETA,500+x*this.sinPHI,500+x*this.cosPHI*this.cosTHETA);
}
Axonometric.prototype.plane_zx=function (y)
{
context.setTransform (this.sinPHI,this.cosPHI*this.cosTHETA,0,this.sinTHETA,500+y*-this.cosPHI,500+y*this.sinPHI*this.cosTHETA);
}
Axonometric.prototype.plane_yx=function (z)
{
context.setTransform (this.sinPHI,this.cosPHI*this.cosTHETA,-this.cosPHI,this.sinPHI*this.cosTHETA,500,500-z*this.sinTHETA);
}
Axonometric.prototype.draw_axis=function (length)
{
var O=this.get_bd (0,0,0);
var X=this.get_bd (length,0,0);
var Y=this.get_bd (0,length,0);
var Z=this.get_bd (0,0,length);
context.save;
context.beginPath ();
context.textAlign="top";
context.fillText ("X",-X.b,X.d);
context.moveTo (-O.b,O.d);
context.lineTo (-X.b,X.d);
context.strokeStyle="red";
context.stroke ();
context.beginPath ();
context.fillText ("Y",-Y.b,Y.d);
context.moveTo (-O.b,O.d);
context.lineTo (-Y.b,Y.d);
context.strokeStyle="green";
context.stroke ();
context.beginPath ();
context.fillText ("Z",-Z.b,Z.d);
context.moveTo (-O.b,O.d);
context.lineTo (-Z.b,Z.d);
context.strokeStyle="blue";
context.stroke ();
context.restore ();
}
// example
var Viewer=new Axonometric (Math.PI/4, Math.PI/8);
Viewer.draw_axis (400);
Viewer.plane_yx (0);
context.beginPath ();
context.fillStyle="red";
context.fillRect (0,0,200,200);
Viewer.plane_zx (0);
context.beginPath ();
context.fillStyle="lightgrey";
context.fillRect (0,0,200,-200);
Viewer.plane_zy (0);
context.beginPath ();
context.arc (-100,100,100,0,2*Math.PI);
context.fillStyle="black";
context.fill();

Using an existing library is an easy solution. If I'm understanding your question properly, you would like users to be able to click on a slice to open a new URL.
This can be achieved in ZingChart by setting up a "pie3d" type, and then including "url" and "target" in the series.
Here's how I did it:
{
"graphset":[
{
"type":"pie3d",
"plot":{
"slice":45
},
"plotarea":{
"margin-top":"35px"
},
"series":[
{
"text":"Apples",
"values":[5],
"url":"http://www.google.com",
"target":"_blank"
},
{
"text":"Oranges",
"values":[8]
},
{
"text":"Bananas",
"values":[22]
},
{
"text":"Grapes",
"values":[16]
},
{
"text":"Cherries",
"values":[12]
}
]
}
]
}

Expanding on Merrily's answer, you can also use ZingChart's API to track chart interaction and call any functions you like.
var ZCwindow;
function openWindow() {
ZCwindow = window.open("http://zingchart.com/docs/chart-types/pie/", "ZingChart Pie Charts");
}
zingchart.node_click = function(e){
if(e.value == 5) openWindow();
};
You can view a live demo here.
I am part of the ZingChart team. You can reach out to us for assistance via support#zingchart.com

For the past few months I have been working with Google Visualization charts, and I think it may be exactly what you're looking for. Here is the link to the documentation.
This will give you a donut chart (though I am not sure if you can make it 3-D or not, I believe you can) and you can add event handlers for when the user clicks on a slice. Here's what it looks like:
I highly recommend trying the charts, I have found them to be extraordinarily useful. Good luck!
EDIT: My apologies, after re-reading the section on donut charts it appears the new API does not yet support 3-D donut charts. Does it absolutely have to be three-dimensional? If not this is still an excellent choice.

It's not 3D, but you should have a look at chart.js

Related

Changing All Icons Rotating Down to One

I currently have a giant table of "auditpoints", some of those points are "automated". If they are automated they receive a gear icon in their row. The gear icon is not the only icon each row receives. Each row, no matter if it's automated or not receives two other icons, a pencil and a toggle button. When an automated point "runs" the gear icon rotates until it is finished "running". I've have implemented some code to ran all of these points at once but I have a small problem. When you click my button to run all these points all three of the icons I have mentioned rotate and this is not the result I am looking for. The line commented out in my code snippet (and it's matching bracket) will prevent the code from running all of the automated points. Commenting out the line is what causes all the icons to rotate. I know this line is required to get the automated points to run properly as it used in the single execution of automated points I just don't know what to change it to. It obviously shouldn't be click because you are no longer clicking the gear icon to get a point to run I just don't know what to change it to but the classes in that click function are related to the gear icon.
Hopefully this is a very easy question to solve and doesn't waste anyone's time. Thank you!
private updateAuto() {
var self = this;
$(".auditPointRow").each(function () {
//self.el.on("click", ".update, .edit", function () {
var row = $(this).closest(".auditPointRow");
var id = row.data("id");
var automated = (<string>row.data("automated")).toLowerCase() == "true";
var running = true;
if (automated && $(this).closest(".edit").length == 0) {
var gear = $(this).find(".fa");
var maxTurns = 120;
gear.css("transition", "transform linear " + maxTurns * 2 + "s");
gear.css("transform", "rotate(" + (maxTurns * 360) + "deg)");
var request = $.ajax(self.root + "api/sites/" + self.site.ID + "/auditpoints/" + id, {
"type": "PATCH", data: JSON.stringify([
{
Op: "Replace"
, Path: "/score"
, Value: "run"
}
])
});
request.done(function () {
gear.css("transition", "").css("transform", "rotate(0deg)");
row.prev().find("td").css("background-color", "");
if (row.prev().qtip("api")) {
row.prev().qtip("api").destroy(true);
}
});
}
//}
});
}
I think I found a solution to my problem. I used .each again to go through all of the "gears" and only rotate them.
private updateAuto() {
var self = this;
//$(".auditPointRow").each(function () {
$(".update, .edit").each(function () {
//Left out the rest of the code so this answer isn't too
//long, none of it changed if that matters.
});
//});
}
For some reason the result runs very slowly (but it runs!) and I'm not sure why so if anyone has any better suggestion/optimizations please feel free to leave those here.
Edit: I realized I didn't to go through .each twice, that's what was slowing to down so I removed that first each that went over auditPoints and just did the ones with gears instead.

chart.js Line chart with different background colors for each section

Lets say I have a Line chart with mon-fri for 4 weeks.
I want that these 4 weeks are diveded in sections. I want the first monday to friday have a white background color.
The second monday to friday a gray background.
The thirth a white bg again.
And the fourth weeks with monday to friday to have a gray background color.
What Im talking about is the background of the graph.
Is there a way to do this?
Chart.js clears the canvas before drawing (or redrawing) a chart.
We can jump in on this and draw our background once the chart is cleared. Just extend the Line chart and override the clear function in the initialize override.
Preview
Script
Chart.types.Line.extend({
name: "LineAlt",
initialize: function(data){
Chart.types.Line.prototype.initialize.apply(this, arguments);
// keep a reference to the original clear
this.originalClear = this.clear;
this.clear = function () {
this.originalClear();
// 1 x scale unit
var unitX = this.datasets[0].points[1].x - this.datasets[0].points[0].x;
var yTop = this.scale.startPoint;
var yHeight = this.scale.endPoint - this.scale.startPoint;
// change your color here
this.chart.ctx.fillStyle = 'rgba(100,100,100,0.8)';
// we shift it by half a x scale unit to the left because the space between gridline is actually a shared space
this.chart.ctx.fillRect(this.datasets[0].points[5].x - 0.5 * unitX, yTop, unitX * 5, yHeight);
this.chart.ctx.fillRect(this.datasets[0].points[15].x - 0.5 * unitX, yTop, unitX * 5, yHeight);
}
}
});
Then just use LineAlt instead of Line
var myNewChart = new Chart(ctx).LineAlt(data);
Fiddle - http://jsfiddle.net/oe2606ww/
Some people here have requested something that works for later versions, here's my hacked together solution that works on ChartJS 2.7.2 (EDIT: Apr 2020: Also 2.9.3) and could probably be adapted. Chart.types.Line.extend used in the answer above, doesn't seem to be valid in v2.
I managed to figure this out with help from this thread to get the plugin code, and also found this thread useful for gathering co-ordinates of the data points.
With some work this fiddle should allow you to pass the label array keys as start/stop positions via the following code (where 0 and 1 are the keys):
var start = meta.data[0]._model.x;
var stop = meta.data[1]._model.x;
You could loop this, along with the ctx.fillRect function to draw multiple rectangles.
Here's the working fiddle: http://jsfiddle.net/oe2606ww/436/
I combined #potatopeelings's and #v25's solutions for a chart.js v2 solution. It utilizes the format of #potatopeelings's solution, allowing to use an alternate chart type (LineAlt), and the updated implementation from #v25's solution.
Chart.controllers.LineAlt = Chart.controllers.line.extend({
draw: function (ease) {
if (this.chart.config.options.chartArea && this.chart.config.options.chartArea.backgroundColor) {
var ctx = this.chart.chart.ctx;
var chartArea = this.chart.chartArea;
var meta = this.chart.getDatasetMeta(0);
var start = meta.data[1]._model.x;
var stop = meta.data[2]._model.x;
ctx.save();
ctx.fillStyle = this.chart.config.options.chartArea.backgroundColor;
ctx.fillRect(start, chartArea.top, stop - start, chartArea.bottom - chartArea.top);
ctx.restore();
}
// Perform regular chart draw
Chart.controllers.line.prototype.draw.call(this, ease);
}
});
Then you can use the custom chart type just as in #potatopeelings's solution:
var myNewChart = new Chart(ctx, {type: 'LineAlt', data: data});
I'd try a little work around,I'd draw an image with four line each one with width 1px and a different color; then in a CSS sheet define:
canvas {
background-image: url(backgroundimage.jpg);
background-size: contain;
}

About image rotation once element with specific id is clicked

Logo and elements from ul once clicked rotates image. By default image is already rotated by certain degrees, then on each click image rotates to necessary value.
So far I was using the following:
$("#objRotates").css('opacity','.2');
var value = 0;
var prev_value = 0;
$( "li" ).click(function() {
var text=$(this).text();
if(text==="text1"){value=0;}
if(text==="text2"){value=33;}
if(text==="text3"){value=66;}
if(prev_value != value){
$("#objRotates").animate({opacity:'1'});
$("#objRotates").rotate({
animateTo:value,
easing: $.easing.easeInOutExpo,
center: ["25px", "150px"],
callback: function(){$("#objRotates").animate({opacity:'0.2'});}
});
}
prev_value = value;
});
Above code is the one that was used before, where images start position was 0 and its animation was triggered from link text.
Using jqueryRotate.js examples(here)
How do I change the code, so that images start position is certain degrees and animation starts if element with specific ID is clicked?
Give at least clue..Cause for now, looking at my old code, I am lost. Thanks in advance.
SIMPLIFIED FIDDLE
Ok, so I've created a couple of samples for you to check out. The first one is very basic and I've simplified the code a little to make it easier to understand. This one just uses completely static values and a static elementId for the event, which I'm pretty sure answers your question based on your response to my comment yesterday. http://jsfiddle.net/x9ja7/594/
$("#elementId").click(function () {
var startingAngle = 45;
var endingAngle = 90;
var elementToRotate = "img";
$(elementToRotate).rotate({
angle: startingAngle,
animateTo: endingAngle
});
});
But I wanted to give another example as well that would be dynamic and repeatable for multiple elements. With the code above, you would have to copy/paste the same code over and over again if you want to perform this animation by clicking different elements. Here's an alternative. In this example, you set all of your parameters in the data attributes in the clickable element, then the function is completely repeatable, you only have to write it once. Less code = everyone happy! Here's the example: http://jsfiddle.net/x9ja7/595/
//#region Default starting angles
$("#image1").rotate({ angle: 90 });
$("#image2").rotate({ angle: 20 });
//#endregion
$(".rotateAction").click(function () {
//#region Optional parameter - used in the optional callback function
var $self = $(this);
//#endregion
var startingAngle = Number($(this).attr("data-startingangle"));
var endingAngle = Number($(this).attr("data-endingangle"));
var elementToRotate = $(this).attr("data-elementtorotate");
//#region If the current angle is the ending angle, reverse the animation - this can be removed if you want, I thought it may be cool to show some of the things you can do with this.
var currentAngle = $(elementToRotate).getRotateAngle();
if ( currentAngle[0] === endingAngle) {
startingAngle = Number($(this).attr("data-endingangle"));
endingAngle = Number($(this).attr("data-startingangle"));
}
//#endregion
$(elementToRotate).rotate({
angle: startingAngle,
animateTo: endingAngle
//#region This is optional - uncommenting this code would make the animation single-use only
//, callback: function () { $self.off().removeClass("clickable"); }
//#endregion
});
});
Hope this helps. If you need any other assistance, please let me know.

Drawing shapes with JavaScript and Canvas

I am trying to generate and draw shapes using HTML 5 canvas and JavaScript. I am trying to make it as DRY as possible but having some issues. I have the code as follows:
var sections = {
"w_end" : {
"name":"W End",
"id":"w_end",
"dimensions": {"move_to":[0,0], "coords":[[0,50], [50,50], [0,50]]}
}
}
$(document).ready(function() {
$.each(sections, function(k,v){
make_shape(k, v);
})
});
function make_shape(id, attributes) {
var holder = document.getElementById('room');
var grid_canvas = document.createElement("canvas");
holder.appendChild(grid_canvas);
grid_canvas.setAttribute("id", id);
var item = grid_canvas.getContext("2d");
item.fillStyle = "rgb(154,250,50)";
item.beginPath();
item.moveTo(attributes.dimensions.move_to[0],attributes.dimensions.move_to[1]);
$.each(attributes.dimensions.coords, function(k ,v){
item.lineTo(v[0],v[1]);
});
item.fill();
item.closePath();
}
This does not seem to work when pulling the lineTo values from the json. I can switch the lineTo(v[0],v[1]) for lineTo(50,75) inside the lopp and it generates a filled shape. I am not great at JavaScript as you can tell. Does anyone know what the issue is here and maybe give some advice on generating multiple shapes from some sort of list?
Cheers
Tony
I played around with your code, it is flawless. There is no triangle drawn because somehow the path going back to itself fills nothing. Place only the first two vertices and it is OK.
"dimensions": {"move_to":[0,0], "coords":[[0,50], [50,50]]}
See this fiddle: http://jsfiddle.net/Nm7UQ/
Note that I commented out document.ready.

Drag and drop SVGs with Raphael JS

Looking for some advice (example would be great) on dragging and dropping SVGs using RaphaelJS.
I have found how to drag an object created withint Raphael
window.onload = function() {
var R = Raphael("canvas", 500, 500);
var c = R.circle(100, 100, 50).attr({
fill: "hsb(.8, 1, 1)",
stroke: "none",
opacity: .5
});
var start = function () {
...
},
move = function (dx, dy) {
...
},
up = function () {
...
};
c.drag(move, start, up);
};​
But I need to be able to adapt this to work with a seperate SVG file(s) so for example
myPage.html has myImage.svg, I need to be able to drag myImage.svg around the 'canvas'.
I'm thinking something like
var c = R.SOMEMETHOD('myImage.svg');
...
c.drag(move, start, up);
For example.
Is there a way to do this and if so, an example would be brilliant!
This magic method doesn't exist in RaphaelJS. But there is a way to accomplish this. You can have a look at the raphael-svg-import project on GitHub which works well for basic svgs
Then, you'll want to use a grouping facility as you cannot use the Set functionnality of RaphaelJS
1 - import your SVG
2 - As you import, mark the elements so they belong to the same group
Enjoy !!

Categories

Resources