Built with blockbuilder.org
forked from giorgi-ghviniashvili's block: radial line v4
license: mit |
Built with blockbuilder.org
forked from giorgi-ghviniashvili's block: radial line v4
function renderChart(params) { | |
// Exposed variables | |
var attrs = { | |
id: "ID" + Math.floor(Math.random() * 1000000), // Id for event handlings | |
svgWidth: 400, | |
svgHeight: 400, | |
marginTop: 0, | |
marginBottom: 0, | |
marginRight: 0, | |
marginLeft: 0, | |
container: 'body', | |
defaultTextFill: '#2C3E50', | |
defaultFont: 'Helvetica', | |
data: null | |
}; | |
//InnerFunctions which will update visuals | |
var updateData; | |
//Main chart object | |
var main = function (selection) { | |
selection.each(function scope() { | |
//Calculated properties | |
var calc = {} | |
calc.id = "ID" + Math.floor(Math.random() * 1000000); // id for event handlings | |
calc.chartLeftMargin = attrs.marginLeft; | |
calc.chartTopMargin = attrs.marginTop; | |
calc.chartWidth = attrs.svgWidth - attrs.marginRight - calc.chartLeftMargin; | |
calc.chartHeight = attrs.svgHeight - attrs.marginBottom - calc.chartTopMargin; | |
//Drawing containers | |
var container = d3.select(this); | |
//Add svg | |
var svg = container.patternify({ tag: 'svg', selector: 'svg-chart-container' }) | |
.attr('width', attrs.svgWidth) | |
.attr('height', attrs.svgHeight) | |
.attr('font-family', attrs.defaultFont); | |
//Add container g element | |
var chart = svg.patternify({ tag: 'g', selector: 'chart' }) | |
.attr('transform', 'translate(' + (calc.chartLeftMargin) + ',' + calc.chartTopMargin + ')'); | |
var line = d3.line() | |
.curve(d3.curveCatmullRom) | |
.x(function(d){ return d.x }) | |
.y(function(d){ return d.y }) | |
chart.patternify({tag: "path", selector: "outerCircle", data: [attrs.data.outerPoints.concat(attrs.data.outerPoints[0])] }) | |
.attr("d",line) | |
.style("fill", "none") | |
.style("stroke-dasharray", "5, 5") | |
.style("stroke", "#000080") | |
chart.patternify({tag: "path", selector: "innerCircle", data: [attrs.data.innerPoints.concat(attrs.data.innerPoints[0])] }) | |
.attr("d", line) | |
.style("fill", "none") | |
.style("stroke-dasharray", "5, 5") | |
.style("stroke", "#000080") | |
chart.patternify({ tag: "path", selector: "line", data: attrs.data.links }) | |
.style("fill", "none") | |
.style("stroke", "#fff") | |
.attr("data-source", function(d){ | |
return d.s; | |
}) | |
.attr("d", function(d){ | |
var source = attrs.data.outerPoints[d.s]; | |
var target = attrs.data.innerPoints[d.t]; | |
return link({source: source, target: target}); | |
}) | |
var pointsData = getPointsData(); | |
chart.patternify({ tag: "circle", selector: 'nodes', data: pointsData }) | |
.attr("r", 5) | |
.attr("cx",function(d) {return d.x;}) | |
.attr("cy", function(d) {return d.y;}) | |
.style("stroke-width", 2) | |
.style("fill", "white") | |
.style("stroke", "#000080") | |
.on("mouseenter", function(d){ | |
var i = attrs.data.outerPoints.indexOf(d); | |
d3.select(this) | |
.transition() | |
.duration(100) | |
.style("fill", "#000080"); | |
chart.selectAll('path[data-source="' + i + '"]') | |
.transition() | |
.duration(100) | |
.style("stroke", "#000080"); | |
}) | |
.on("mouseleave", function(d){ | |
var i = pointsData.indexOf(d); | |
d3.select(this) | |
.transition() | |
.duration(100) | |
.style("fill", "#fff"); | |
chart.selectAll('path[data-source="' + i + '"]') | |
.transition() | |
.duration(100) | |
.style("stroke", "#fff"); | |
}); | |
// Smoothly handle data updating | |
updateData = function () { | |
} | |
//######################################### UTIL FUNCS ################################## | |
function link(d) { | |
return "M" + d.source.x + "," + d.source.y | |
+ "C" + d.source.x + "," + (d.source.y + d.target.y) / 2 | |
+ " " + d.target.x + "," + (d.source.y + d.target.y) / 2 | |
+ " " + d.target.x + "," + d.target.y; | |
} | |
function getPointsData () { | |
return attrs.data.outerPoints.concat(attrs.data.innerPoints); | |
} | |
function handleWindowResize() { | |
d3.select(window).on('resize.' + attrs.id, function () { | |
setDimensions(); | |
}); | |
} | |
function setDimensions() { | |
setSvgWidthAndHeight(); | |
container.call(main); | |
} | |
function setSvgWidthAndHeight() { | |
var containerRect = container.node().getBoundingClientRect(); | |
if (containerRect.width > 0) | |
attrs.svgWidth = containerRect.width; | |
if (containerRect.height > 0) | |
attrs.svgHeight = containerRect.height; | |
} | |
function debug() { | |
if (attrs.isDebug) { | |
//Stringify func | |
var stringified = scope + ""; | |
// Parse variable names | |
var groupVariables = stringified | |
//Match var x-xx= {}; | |
.match(/var\s+([\w])+\s*=\s*{\s*}/gi) | |
//Match xxx | |
.map(function(d) { return d.match(/\s+\w*/gi).filter(s => s.trim()) }) | |
//Get xxx | |
.map(function(v) { return v[0].trim() }) | |
//Assign local variables to the scope | |
groupVariables.forEach(function(v) { | |
main['P_' + v] = eval(v) | |
}) | |
} | |
} | |
debug(); | |
}); | |
}; | |
//----------- PROTOTYEPE FUNCTIONS ---------------------- | |
d3.selection.prototype.patternify = function (params) { | |
var container = this; | |
var selector = params.selector; | |
var elementTag = params.tag; | |
var data = params.data || [selector]; | |
// Pattern in action | |
var selection = container.selectAll('.' + selector).data(data, function(d,i) { | |
if (typeof d === "object") { | |
if (d.id) { | |
return d.id; | |
} | |
} | |
return i; | |
}) | |
selection.exit().remove(); | |
selection = selection.enter().append(elementTag).merge(selection) | |
selection.attr('class', selector); | |
return selection; | |
} | |
//Dynamic keys functions | |
Object.keys(attrs).forEach(function(key) { | |
// Attach variables to main function | |
return main[key] = function (_) { | |
var string = `attrs['${key}'] = _`; | |
if (!arguments.length) { return eval(` attrs['${key}'];`); } | |
eval(string); | |
return main; | |
}; | |
}); | |
//Set attrs as property | |
main.attrs = attrs; | |
//Debugging visuals | |
main.debug = function (isDebug) { | |
attrs.isDebug = isDebug; | |
if (isDebug) { | |
if (!window.charts) window.charts = []; | |
window.charts.push(main); | |
} | |
return main; | |
} | |
//Exposed update functions | |
main.data = function (value) { | |
if (!arguments.length) return attrs.data; | |
attrs.data = value; | |
if (typeof updateData === 'function') { | |
updateData(); | |
} | |
return main; | |
} | |
// Run visual | |
main.run = function () { | |
d3.selectAll(attrs.container).call(main); | |
return main; | |
} | |
return main; | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Chart</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<style> | |
body { | |
background-color: #E1E5EC; | |
} | |
#myGraph { | |
width: 500px; | |
height: auto; | |
margin: auto; | |
} | |
</style> | |
</head> | |
<body translate="no"> | |
<div class="container centered"> | |
<div id="myGraph"></div> | |
</div> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="d3script.js"></script> | |
<script> | |
var width = document.getElementById('myGraph').getBoundingClientRect().width; | |
var height = width; | |
var center = { | |
x: width / 2, | |
y: height / 2 | |
}; | |
var pointsOuter = getPoints(24, 220); | |
var pointsInner = getPoints(12, 100); | |
var links = [ | |
{s: 0, t: 8}, | |
{s: 0, t: 2}, | |
{s: 1, t: 4}, | |
{s: 18, t: 9}, | |
{s: 12, t: 5}, | |
{s: 22, t: 10}, | |
{s: 21, t: 10}, | |
{s: 9, t: 4}, | |
{s: 19, t: 5}, | |
{s: 4, t: 3}, | |
{s: 16, t: 8}, | |
{s: 16, t: 1}, | |
{s: 14, t: 7}, | |
{s: 14, t: 6}, | |
]; | |
var dataset = { | |
outerPoints: pointsOuter, | |
innerPoints: pointsInner, | |
links: links | |
}; | |
var chart = renderChart() | |
.svgHeight(height) | |
.svgWidth(width) | |
.container('#myGraph') | |
.data(dataset) | |
.debug(true) | |
.run() | |
function getPoints(n, r){ | |
var coords = []; | |
var radian = (2 * Math.PI) / n; | |
for(var i = 0; i < n; i++){ | |
var angle = i * radian; | |
var x = center.x + r * Math.cos(angle); | |
var y = center.y + r * Math.sin(angle); | |
coords.push({ | |
x: x, | |
y: y | |
}); | |
} | |
return coords; | |
} | |
</script> | |
</body> | |
</html> |