"Pizza pie charts by Zurb"
Bootstrap 3.0.0 Snippet by GavinWhite

<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <script type="text/javascript" src="//cdn.jsdelivr.net/snap.svg/0.1.0/snap.svg-min.js"></script> <div class="container"> <div class="row"> <div class="col-md-12"> <h2>Create your snippet's HTML, CSS and Javascript in the editor tabs</h2> </div> </div> <div class="row"> <div class="col-md-3"> <ul data-pie-id="svg"> <li data-value="60">Water Buffalo (60)</li> <li data-value="20">Bison (20)</li> <li data-value="12">Sheep (12)</li> <li data-value="32">Goat (32)</li> <li data-value="50">Shetland Pony (50)</li> </ul> </div> <div class="col-md-3"> <div id="svg"></div> </div> <div class="col-md-3"> <ul data-pie-id="my-cool-chart" data-options='{"donut": "true"}'> <li data-value="36">Pepperoni</li> <li data-value="14">Sausage</li> <li data-value="8">Cheese</li> <li data-value="11">Mushrooms</li> <li data-value="7">Chicken</li> <li data-value="24">Other</li> </ul> </div> <div class="col-md-3"> <div id="my-cool-chart"></div> </div> </div> </div>
/* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(0) { color: blue; } /* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(1) { color: #4d00ff; } /* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(2) { color: #9900ff; } /* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(3) { color: #e500ff; } /* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(4) { color: #ff00cc; } /* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(5) { color: #ff0080; } /* line 20, ../sass/pizza.scss */ [data-pie-id] li:nth-child(6) { color: #ff0033; } /* line 26, ../sass/pizza.scss */ ul[data-pie-id] { list-style: none; padding: 10px; } [data-pie-id='my-cool-chart'] li:nth-child(0) { color: #d84200; } /* line 20, ../sass/pizza.scss */ [data-pie-id='my-cool-chart'] li:nth-child(1) { color: #fc4d00; } /* line 20, ../sass/pizza.scss */ [data-pie-id='my-cool-chart'] li:nth-child(2) { color: #ff6420; } /* line 20, ../sass/pizza.scss */ [data-pie-id='my-cool-chart'] li:nth-child(3) { color: #ff7d44; } /* line 20, ../sass/pizza.scss */ [data-pie-id='my-cool-chart'] li:nth-child(4) { color: #ff9668; } /* line 20, ../sass/pizza.scss */ [data-pie-id='my-cool-chart'] li:nth-child(5) { color: #ffaf8b; } /* line 20, ../sass/pizza.scss */ [data-pie-id='my-cool-chart'] li:nth-child(6) { color: #ffc8af; }
;(function ($, window, document, undefined) { 'use strict'; var Pizza = { version : '0.0.1', settings : { donut: false, donut_inner_ratio: 0.4, // between 0 and 1 percent_offset: 35, // relative to radius stroke_color: '#333', stroke_width: 0, show_percent: true, // show or hide the percentage on the chart. animation_speed: 500, animation_type: 'elastic' // options: backin, backout, bounce, easein, // easeinout, easeout, linear }, init : function (scope, options) { var self = this; this.scope = scope || document.body; var pies = $('[data-pie-id]', this.scope); $.extend(true, this.settings, options) if (pies.length > 0) { pies.each(function () { return self.build($(this), options); }); } else { this.build($(this.scope), options); } this.events(); }, events : function () { var self = this; $(window).off('.pizza').on('resize.pizza', self.throttle(function () { self.init(); }, 100)); $(this.scope).off('.pizza').on('mouseenter.pizaa mouseleave.pizza touchstart.pizza', '[data-pie-id] li', function (e) { var parent = $(this).parent(), path = Snap($('#' + parent.data('pie-id') + ' path[data-id="s' + $(this).index() + '"]')[0]), text = Snap($(path.node).parent() .find('text[data-id="' + path.node.getAttribute('data-id') + '"]')[0]), settings = $(this).parent().data('settings'); if (/start/i.test(e.type)) { $(path.node).siblings('path').each(function () { if (this.nodeName) { path.animate({ transform: 's1 1 ' + path.node.getAttribute('data-cx') + ' ' + path.node.getAttribute('data-cy') }, settings.animation_speed, mina[settings.animation_type]); Snap($(this).next()[0]).animate({ opacity: 0 }, settings.animation_speed); } }); } if (/enter|start/i.test(e.type)) { path.animate({ transform: 's1.05 1.05 ' + path.node.getAttribute('data-cx') + ' ' + path.node.getAttribute('data-cy') }, settings.animation_speed, mina[settings.animation_type]); if (settings.show_percent) { text.animate({ opacity: 1 }, settings.animation_speed); } } else { path.animate({ transform: 's1 1 ' + path.node.getAttribute('data-cx') + ' ' + path.node.getAttribute('data-cy') }, settings.animation_speed, mina[settings.animation_type]); text.animate({ opacity: 0 }, settings.animation_speed); } }); }, build : function(legends, options) { var self = this; var legend = legends, graph; legend.data('settings', $.extend({}, self.settings, options, legend.data('options'))); self.data(legend, options || {}); return self.update_DOM(self.pie(legend)); }, data : function (legend, options) { var data = [], count = 0; $('li', legend).each(function () { var segment = $(this); if (options.data) { data.push({ value: options.data[segment.index()], color: segment.css('color'), segment: segment }); } else { data.push({ value: segment.data('value'), color: segment.css('color'), segment: segment }); } }); return legend.data('graph-data', data); }, update_DOM : function (parts) { var legend = parts[0], graph = parts[1]; return $(this.identifier(legend)).html(graph); }, pie : function (legend) { // pie chart concept from JavaScript the // Definitive Guide 6th edition by David Flanagan var settings = legend.data('settings'), svg = this.svg(legend, settings), data = legend.data('graph-data'), total = 0, angles = [], start_angle = 0, base = $(this.identifier(legend)).width() - 4; for (var i = 0; i < data.length; i++) { total += data[i].value; } for (var i = 0; i < data.length; i++) { angles[i] = data[i].value / total * Math.PI * 2; } for (var i = 0; i < data.length; i++) { var end_angle = start_angle + angles[i]; var cx = (base / 2), cy = (base / 2), r = ((base / 2) * 0.85); if (!settings.donut) { // Compute the two points where our wedge intersects the circle // These formulas are chosen so that an angle of 0 is at 12 o'clock // and positive angles increase clockwise var x1 = cx + r * Math.sin(start_angle); var y1 = cy - r * Math.cos(start_angle); var x2 = cx + r * Math.sin(end_angle); var y2 = cy - r * Math.cos(end_angle); // This is a flag for angles larger than than a half circle // It is required by the SVG arc drawing component var big = 0; if (end_angle - start_angle > Math.PI) big = 1; // This string holds the path details var d = "M" + cx + "," + cy + // Start at circle center " L" + x1 + "," + y1 + // Draw line to (x1,y1) " A" + r + "," + r + // Draw an arc of radius r " 0 " + big + " 1 " + // Arc details... x2 + "," + y2 + // Arc goes to to (x2,y2) " Z"; // Close path back to (cx,cy) } var existing_path = $('path[data-id="s' + i + '"]', svg.node); if (existing_path.length > 0) { var path = Snap(existing_path[0]); } else { var path = svg.path(); } var percent = (data[i].value / total) * 100.0; // thanks to Raphael.js var existing_text = $('text[data-id="s' + i + '"]', svg.node); if (existing_text.length > 0) { var text = Snap(existing_text[0]); text.attr({ x: cx + (r + settings.percent_offset) * Math.sin(start_angle + (angles[i] / 2)), y: cy - (r + settings.percent_offset) * Math.cos(start_angle + (angles[i] / 2)) }); } else { var text = path.paper.text(cx + (r + settings.percent_offset) * Math.sin(start_angle + (angles[i] / 2)), cy - (r + settings.percent_offset) * Math.cos(start_angle + (angles[i] / 2)), Math.ceil(percent) + '%'); } var left_offset = text.getBBox().width / 2; text.attr({ x: text.attr('x') - left_offset, opacity: 0 }); text.node.setAttribute('data-id', 's' + i); path.node.setAttribute('data-cx', cx); path.node.setAttribute('data-cy', cy); if (settings.donut) { this.annular_sector(path.node, { centerX:cx, centerY:cy, startDegrees:start_angle, endDegrees:end_angle, innerRadius: (r * settings.donut_inner_ratio), outerRadius:r }); } else { path.attr({d:d}); } path.attr({ fill: data[i].color, stroke: settings.stroke_color, strokeWidth: settings.stroke_width }); path.node.setAttribute('data-id', 's' + i); this.animate(path, cx, cy, settings); // The next wedge begins where this one ends start_angle = end_angle; } return [legend, svg.node]; }, animate : function (el, cx, cy, settings) { var self = this; el.hover(function (e) { var path = Snap(e.target), text = Snap($(path.node).parent() .find('text[data-id="' + path.node.getAttribute('data-id') + '"]')[0]); path.animate({ transform: 's1.05 1.05 ' + cx + ' ' + cy }, settings.animation_speed, mina[settings.animation_type]); text.touchend(function () { path.animate({ transform: 's1.05 1.05 ' + cx + ' ' + cy }, settings.animation_speed, mina[settings.animation_type]); }); if (settings.show_percent) { text.animate({ opacity: 1 }, settings.animation_speed); text.touchend(function () { text.animate({ opacity: 1 }, settings.animation_speed); }); } }, function (e) { var path = Snap(e.target), text = Snap($(path.node).parent() .find('text[data-id="' + path.node.getAttribute('data-id') + '"]')[0]); path.animate({ transform: 's1 1 ' + cx + ' ' + cy }, settings.animation_speed, mina[settings.animation_type]); text.animate({ opacity: 0 }, settings.animation_speed); }); }, svg : function (legend, settings) { var container = $(this.identifier(legend)), svg = $('svg', container), width = container.width(), height = width; if (svg.length > 0) { svg = Snap(svg[0]); } else { svg = Snap(width, height); } svg.node.setAttribute('width', width + settings.percent_offset); svg.node.setAttribute('height', height + settings.percent_offset); svg.node.setAttribute('viewBox', '-' + settings.percent_offset + ' -' + settings.percent_offset + ' ' + (width + (settings.percent_offset * 1.5)) + ' ' + (height + (settings.percent_offset * 1.5))); return svg; }, // http://stackoverflow.com/questions/11479185/svg-donut-slice-as-path-element-annular-sector annular_sector : function (path, options) { var opts = optionsWithDefaults(options); var p = [ // points [opts.cx + opts.r2*Math.sin(opts.startRadians), opts.cy - opts.r2*Math.cos(opts.startRadians)], [opts.cx + opts.r2*Math.sin(opts.closeRadians), opts.cy - opts.r2*Math.cos(opts.closeRadians)], [opts.cx + opts.r1*Math.sin(opts.closeRadians), opts.cy - opts.r1*Math.cos(opts.closeRadians)], [opts.cx + opts.r1*Math.sin(opts.startRadians), opts.cy - opts.r1*Math.cos(opts.startRadians)], ]; var angleDiff = opts.closeRadians - opts.startRadians; var largeArc = (angleDiff % (Math.PI*2)) > Math.PI ? 1 : 0; var cmds = []; cmds.push("M"+p[0].join()); // Move to P0 cmds.push("A"+[opts.r2,opts.r2,0,largeArc,1,p[1]].join()); // Arc to P1 cmds.push("L"+p[2].join()); // Line to P2 cmds.push("A"+[opts.r1,opts.r1,0,largeArc,0,p[3]].join()); // Arc to P3 cmds.push("z"); // Close path (Line to P0) path.setAttribute('d',cmds.join(' ')); function optionsWithDefaults(o){ // Create a new object so that we don't mutate the original var o2 = { cx : o.centerX || 0, cy : o.centerY || 0, startRadians : (o.startDegrees || 0), closeRadians : (o.endDegrees || 0), }; var t = o.thickness!==undefined ? o.thickness : 100; if (o.innerRadius!==undefined) o2.r1 = o.innerRadius; else if (o.outerRadius!==undefined) o2.r1 = o.outerRadius - t; else o2.r1 = 200 - t; if (o.outerRadius!==undefined) o2.r2 = o.outerRadius; else o2.r2 = o2.r1 + t; if (o2.r1<0) o2.r1 = 0; if (o2.r2<0) o2.r2 = 0; return o2; } }, identifier : function (legend) { return '#' + legend.data('pie-id'); }, throttle : function(fun, delay) { var timer = null; return function () { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function () { fun.apply(context, args); }, delay); }; } }; window.Pizza = Pizza; }($, this, this.document)); $(window).load(function() { Pizza.init(); })

Related: See More


Questions / Comments: