Skip to content

Instantly share code, notes, and snippets.

@mrsweaters
Created May 21, 2014 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrsweaters/023a5bd9ba1aed5fc54a to your computer and use it in GitHub Desktop.
Save mrsweaters/023a5bd9ba1aed5fc54a to your computer and use it in GitHub Desktop.
Custom Pizza Line Labels
$.extend(Pizza, {
line : function (legend) {
var settings = legend.data('settings'),
svg = this.svg(legend, settings),
container = $(this.identifier(legend)),
width = container.outerWidth(),
height = container.outerHeight(),
data = legend.data('graph-data'),
max_x = max_y = min_x = min_y = total_x = total_y = 0,
i = data.length,
points = '';
for (var i = 0; i < data.length; i++) {
if (data[i].x > max_x) max_x = data[i].x;
if (data[i].y > max_y) max_y = data[i].y;
if (min_x > data[i].x) min_x = data[i].x;
if (min_y > data[i].y) min_y = data[i].y;
total_x += data[i].x;
total_y += data[i].y;
}
var existing_group = $('g[data-id=line]', svg);
if (existing_group.length > 0) {
var line_g = $('g[data-id=line]', svg)[0],
circle_g = $('g[data-id=points]', svg)[0],
polyline = $('path[data-id=path]', line_g)[0];
} else {
var polyline = this.svg_obj('path'),
line_g = this.svg_obj('g'),
circle_g = this.svg_obj('g');
line_g.setAttribute('data-id', 'line');
circle_g.setAttribute('data-id', 'points');
polyline.setAttribute('data-id', 'path');
}
for (var i = 0; i < data.length; i++) {
if (existing_group.length > 0) {
var circle = $('circle[data-id=c' + i + ']', circle_g)[0];
} else {
var circle = this.svg_obj('circle');
circle.setAttribute('data-id', 'c' + i);
}
var x = (data[i].x / max_x) * width,
y = (data[i].y / max_y) * height;
points += x + ',' + y + ' ';
this.set_attr(circle, {cx: x, cy: y,r: 0,fill: data[i.color],
'data-value': data[i].x + ', ' + data[i].y,
'data-tooltip': '',
'title': data[i].x + ', ' + data[i].y,
'class': 'has-tip tip-top'});
Snap(circle).animate({
r: 4
}, 1500, mina[settings.animation_type]);
this.animate(Snap(circle), x, y, settings, 2);
if (existing_group.length < 1) {
circle_g.appendChild(circle);
}
}
this.flip(circle_g, height);
this.flip(line_g, height);
if (settings.show_grid) {
if (settings.line_no_x_labels) {
this.assemble_grid_x(svg, min_x, max_x, width, height, settings, data.labelX);
}
if (settings.line_no_y_labels) {
this.assemble_grid_y(svg, min_y, max_y, width, height, settings, data.labelY);
}
}
var v = this.points_to_path(points);
this.set_attr(polyline, {d:v, fill: 'none', stroke: 'black', 'stroke-width': 2});
if (existing_group.length < 1) {
line_g.appendChild(polyline);
svg.appendChild(line_g);
}
if (existing_group.length < 1) {
svg.appendChild(circle_g);
}
return [legend, svg];
},
assemble_grid_x : function (svg, min, max, width, height, settings, labelX) {
var existing_group = $('g[data-id=gridx]', svg);
if (existing_group.length > 0) {
var line_g = existing_group[0],
text_g = $('g[data-id=labelx]', svg)[0];
} else {
var line_g = this.svg_obj('g'),
text_g = this.svg_obj('g');
line_g.setAttribute('data-id', 'gridx');
text_g.setAttribute('data-id', 'labelx');
}
var ticks = this.ticks(min, max, settings.bar_intervals).reverse(),
ticks_length = i = ticks.length,
total_tick_width = 0,
interval = width/(ticks_length-1);
while (i--) {
if (existing_group.length > 0) {
var line = $('line[data-id=l' + i + ']', line_g)[0],
text = $('text[data-id=t' + i + ']', text_g)[0];
} else {
var line = this.svg_obj('line'),
text = this.svg_obj('text');
line.setAttribute('data-id', 'l' + i);
text.setAttribute('data-id', 't' + i);
}
var line_width = total_tick_width + interval;
this.set_attr(line, {
x1 : line_width,
x2 : line_width,
y1 : 0,
y2 : height,
stroke : 'gray',
'stroke-width' : 1,
'stroke-dasharray' : '5,5'
})
.set_attr(text, {
y: height + 20,
x: line_width - interval,
'text-anchor': 'middle'
});
if (existing_group.length < 1) {
text.innerHTML = labelX || ticks[i];
text_g.appendChild(text);
line_g.appendChild(line);
}
total_tick_width = line_width;
}
line_g.setAttribute('transform', 'translate(-' + interval + ', 0)');
if (existing_group.length < 1) {
svg.appendChild(line_g);
svg.appendChild(text_g);
}
},
assemble_grid_y : function (svg, min, max, width, height, settings, labelY) {
var existing_group = $('g[data-id=gridy]', svg);
if (existing_group.length > 0) {
var line_g = existing_group[0],
text_g = $('g[data-id=labely]', svg)[0];
} else {
var line_g = this.svg_obj('g'),
text_g = this.svg_obj('g');
line_g.setAttribute('data-id', 'gridy');
text_g.setAttribute('data-id', 'labely');
}
var ticks = this.ticks(min, max, settings.bar_intervals),
ticks_length = i = ticks.length,
total_tick_height = 0;
while (i--) {
if (existing_group.length > 0) {
var line = $('line[data-id=l' + i + ']', line_g)[0],
text = $('text[data-id=t' + i + ']', text_g)[0];
} else {
var line = this.svg_obj('line'),
text = this.svg_obj('text');
line.setAttribute('data-id', 'l' + i);
text.setAttribute('data-id', 't' + i);
}
var line_height = total_tick_height + (height/(ticks_length-1));
this.set_attr(line, {
x1 : 0,
x2 : width,
y1 : line_height,
y2 : line_height,
stroke : 'gray',
'stroke-width' : 1,
'stroke-dasharray' : '5,5'
})
.set_attr(text, {
x : -8,
y : line_height + 5,
'text-anchor': 'end'
});
if (existing_group.length < 1) {
text_g.appendChild(text);
line_g.appendChild(line);
text.innerHTML = labelY || ticks[i];
}
total_tick_height = line_height;
}
line_g.setAttribute('transform', 'translate(0, -' + total_tick_height / ticks_length + ')');
text_g.setAttribute('transform', 'translate(0, -' + total_tick_height / ticks_length + ')');
if (existing_group.length < 1) {
svg.appendChild(line_g);
svg.appendChild(text_g);
}
},
points_to_path : function (points) {
var points = points.split(/\s+|,/);
var x0=points.shift(), y0=points.shift();
var pathdata = 'M'+x0+','+y0+'L'+points.join(' ');
return ['M'+x0+','+y0+'L'].concat(points).join(' ');
},
line_events : function () {
$(this.scope).on('mouseenter.pizza mouseleave.pizza touchstart.pizza', '[data-line-id] li', function (e) {
var parent = $(this).parent(),
path = $('#' + parent.data('line-id') + ' circle[data-id="c' + $(this).index() + '"]')[0],
settings = $(this).parent().data('settings');
if (/start/i.test(e.type)) {
$(path).siblings('circle').each(function () {
if (this.nodeName) {
Snap(path).animate({
transform: 's1 1 ' + path.getAttribute('cx') + ' ' + path.getAttribute('cy')
}, settings.animation_speed, mina[settings.animation_type]);
}
});
}
if (/enter|start/i.test(e.type)) {
Snap(path).animate({
transform: 's2 2 ' + path.getAttribute('cx') + ' ' + path.getAttribute('cy')
}, settings.animation_speed, mina[settings.animation_type]);
$(path).trigger('mouseenter')
} else {
Snap(path).animate({
transform: 's1 1 ' + path.getAttribute('cx') + ' ' + path.getAttribute('cy')
}, settings.animation_speed, mina[settings.animation_type]);
$(path).trigger('mouseout')
}
});
}
});
var Pizza = {
version : '0.2.2',
settings : {
donut: false,
donut_inner_ratio: 0.4, // between 0 and 1
percent_offset: 35, // relative to radius
show_text: true, // show or hide the percentage on the chart.
animation_speed: 500,
always_show_text: false,
show_grid: true,
bar_spacer: 100,
bar_intervals: 6,
line_no_y_labels: true,
line_no_x_labels: false,
animation_type: 'elastic' // options: backin, backout, bounce, easein,
// easeinout, easeout, linear
},
NS : 'http://www.w3.org/2000/svg',
init : function (scope, options) {
var self = this;
this.scope = scope || document.body;
var charts = $('[data-pie-id], [data-line-id], [data-bar-id]', this.scope);
$.extend(true, this.settings, options);
if (charts.length > 0) {
charts.each(function () {
return self.build($(this), options);
});
} else if ($(this.scope).is('[data-pie-id]')
|| $(this.scope).is('[data-line-id]')
|| $(this.scope).is('[data-bar-id]')) {
this.build($(this.scope), options);
}
this.events();
},
events : function () {
var self = this;
$(window).off('.pizza').on('resize.pizza', self.throttle(function () {
self.init();
}, 500));
$(this.scope).off('.pizza');
this.pie_events();
this.line_events();
this.bar_events();
},
build : function(legend, options) {
legend.data('settings', $.extend({}, this.settings, options, legend.data('options')));
this.data(legend, options || {});
if (legend.data('pie-id')) {
this.update_DOM(this.pie(legend));
} else if (legend.data('line-id')) {
this.update_DOM(this.line(legend));
} else if (legend.data('bar-id')) {
this.update_DOM(this.bar(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()],
text: segment.data('text'),
color: segment.css('color'),
segment: segment
});
} else {
data.push({
x : segment.data('x'),
y : segment.data('y'),
labelX: segment.data('label-x'),
labelY: segment.data('label-y'),
value: segment.data('value'),
text: segment.data('text'),
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);
},
animate : function (el, cx, cy, settings, scale) {
var self = this,
scale = scale || 1.05;
el.hover(function (e) {
var path = Snap(e.target),
text = Snap(path.node.nextSibling);
path.animate({
transform: 's' + scale + ' ' + scale + ' ' + cx + ' ' + cy
}, settings.animation_speed, mina[settings.animation_type]);
if (!/text/.test(text.node.nodeName)) return;
text.touchend(function () {
Snap(path).animate({
transform: 's' + scale + ' ' + scale + ' ' + cx + ' ' + cy
}, settings.animation_speed, mina[settings.animation_type]);
});
if (settings.show_text) {
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.nextSibling);
path.animate({
transform: 's1 1 ' + cx + ' ' + cy
}, settings.animation_speed, mina[settings.animation_type]);
if (!/text/.test(text.node.nodeName)) return;
text.animate({
opacity: 0
}, settings.animation_speed);
});
},
parse_options : function (string, percent, value) {
var percentStr = Math.ceil(percent) + '%',
output = string.replace(/{{ *percent *}}/ig, percentStr)
.replace(/{{ *value *}}/ig, value);
return output;
},
svg : function (legend, settings) {
var container = $(this.identifier(legend)),
svg = $('svg', container),
width = container.width(),
pie = legend.attr('data-pie-id'),
height = container.height();
if (svg.length > 0) {
svg = svg[0];
} else {
var svg = this.svg_obj('svg');
svg.width = width;
svg.height = height;
}
if (pie) {
var view_box = '-' + settings.percent_offset + ' -' + settings.percent_offset + ' ' +
(width + (settings.percent_offset * 1.5)) + ' ' +
(width + (settings.percent_offset * 1.5));
} else {
var view_box = '-' + settings.percent_offset + ' -' + settings.percent_offset + ' ' +
(width + (settings.percent_offset * 1.6)) + ' ' +
(height + (settings.percent_offset * 1.6));
}
this.set_attr(svg, {width: '100%', height: '100%', viewBox: view_box});
return svg;
},
identifier : function (legend) {
id = legend.data('pie-id') || legend.data('bar-id') || legend.data('line-id');
return '#' + 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);
};
},
svg_obj : function (type) {
return document.createElementNS(this.NS, type);
},
ticks: function (min, max, count) {
var span = max - min,
step = Math.pow(10, Math.floor(Math.log(span / count) / Math.LN10)),
err = count / span * step;
// Filter ticks to get closer to the desired count.
if (err <= .15) step *= 10;
else if (err <= .35) step *= 5;
else if (err <= .75) step *= 2;
// Round start and stop values to step interval.
var tstart = Math.ceil(min / step) * step,
tstop = Math.floor(max / step) * step + step * .5,
ticks = [],
x;
// now generate ticks
for (i=tstart; i < tstop; i += step) {
ticks.push(i);
}
return ticks;
},
set_attr : function (node, attrs) {
for (attr in attrs) {
node.setAttribute(attr, attrs[attr]);
}
return this;
},
flip : function (node, h) {
node.setAttribute('transform', 'translate(0, ' + h +') scale(1, -1)');
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment