I have a issue with d3.js i am trying to make a grid where every line have "two state" (two color) as you can see. I generate the elements and it works great (maybe i could do it easier). My problem is that i need a "transformation". Which would be that if you click to a rectangle every sides of the rectangle change color.
My question specifically, is how can i make this function?
Thank you in advance.
Here is where im stuck:
// grid basic variables
var dimension = 10,
width = 50,
height = 50;
function gridData() {
var data = new Array();
// rectangle variables
var rectXpos = 0,
rectYpos = 0,
rectWidth = width,
rectHeight = height;
click = 0;
// iterate for rows
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension; column++) {
// rectClass = "rect" + rectXpos.toString() + rectYpos.toString();
data.push({
x: rectXpos,
y: rectYpos,
width: rectWidth,
height: rectHeight,
// class: rectClass,
click: click
});
// increment the x position. I.e. move it over by 50 (width variable)
rectXpos += rectWidth;
}
// reset the x position after a row is complete
rectXpos = 0;
// increment the y position for the next row. Move it down 50 (height variable)
rectYpos += rectHeight;
}
return data;
}
var gridData = gridData();
// I like to log the data to the console for quick debugging
console.log(gridData);
var grid = d3.select("#grid")
.append("svg")
.attr("width", width*dimension)
.attr("height",height*dimension);
var rect = grid.selectAll(".square")
.data(gridData)
.enter().append("rect")
.attr("class","rect")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.width; })
.attr("height", function(d) { return d.height; })
.style("fill", "#f2f2f2")
.style("stroke", "#fff")
.on('click', function(d) {
d.click ++;
d3.select(".vline" + d.x.toString() + d.y.toString() + (d.x + 50).toString() + d.y.toString()).style("stroke","#f4363f");
// d3.select(".vline" + d.x.toString() + (d.y + 50).toString() + (d.x + 50).toString() + (d.y + 50).toString()).style("stroke","#f4363f");
// d3.select(".hline" + d.x.toString() + d.y.toString() + d.x.toString() + (d.y + 50).toString()).style("stroke","#f4363f");
// d3.select(".hline" + (d.x + 50).toString() + d.y.toString() + (d.x + 50).toString() + (d.y + 50).toString()).style("stroke","#f4363f");
});
function hlinegriddata() {
var data = new Array();
// line variables
var hlineX1 = 0,
hlineY1 = 0,
hlineX2 = 0,
hlineY2 = 50,
click = 0;
var lineLength = width;
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension + 1; column++) {
hlineClass = "hline" + hlineX1.toString() + hlineY1.toString() + hlineX2.toString() + hlineY2.toString();
data.push({
x1: hlineX1,
y1: hlineY1,
x2: hlineX2,
y2: hlineY2,
class: hlineClass,
click: click
});
// increment the x position for the next line
hlineX1 += lineLength;
hlineX2 += lineLength;
}
// reset the x position after a row is complete
hlineX1 = 0;
hlineX2 = 0;
// increment the y position for the next row. Move it down 50 (height variable)
hlineY1 += lineLength;
hlineY2 += lineLength;
}
return data;
}
var hlinegriddata = hlinegriddata();
// I like to log the data to the console for quick debugging
console.log(hlinegriddata);
var hline = grid.selectAll(".hline")
.data(hlinegriddata)
.enter().append("line")
.attr("class", function(d) { return d.class; })
.attr("x1", function(d) { return d.x1; })
.attr("y1", function(d) { return d.y1; })
.attr("x2", function(d) { return d.x2; })
.attr("y2", function(d) { return d.y2; })
.style("stroke", "#fff")
.style("stroke-width", "4")
.style("cursor", "pointer")
.on('click', function(d) {
d.click ++;
if ((d.click)%2 == 0 ) { d3.select(this).style("stroke","#fff"); }
if ((d.click)%2 == 1 ) { d3.select(this).style("stroke","#f4363f"); }
});
function vlinegriddata() {
var data = new Array();
// line variables
var vlineX1 = 0,
vlineY1 = 0,
vlineX2 = 50,
vlineY2 = 0,
click = 0;
var lineLength = width;
// iterate for rows
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension + 1; column++) {
vlineClass = "vline" + vlineX1.toString() + vlineY1.toString() + vlineX2.toString() + vlineY2.toString();
data.push({
x1: vlineX1,
y1: vlineY1,
x2: vlineX2,
y2: vlineY2,
class: vlineClass,
click: click
});
// increment the x position for the next line
vlineY1 += lineLength;
vlineY2 += lineLength;
}
// reset the x position after a row is complete
vlineY1 = 0;
vlineY2 = 0;
// increment the y position for the next row. Move it down 50 (height variable)
vlineX1 += lineLength;
vlineX2 += lineLength;
}
return data;
}
var vlinegriddata = vlinegriddata();
// I like to log the data to the console for quick debugging
console.log(vlinegriddata);
var vline = grid.selectAll(".vline")
.data(vlinegriddata)
.enter().append("line")
.attr("class", function(d) { return d.class; })
.attr("x1", function(d) { return d.x1; })
.attr("y1", function(d) { return d.y1; })
.attr("x2", function(d) { return d.x2; })
.attr("y2", function(d) { return d.y2; })
.style("stroke", "white")
.style("stroke-width", "4")
.style("cursor", "pointer")
// .on("click", function(){var nextColor = this.style.stroke == "white" ? "magenta" : "white";
// d3.select(this).style("stroke", nextColor);});
.on('click', function(d) {
d.click ++;
if ((d.click)%2 == 0 ) { d3.select(this).style("stroke","#fff"); }
if ((d.click)%2 == 1 ) { d3.select(this).style("stroke","#f4363f"); }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="grid"></div>
Not sure I understand the purpose of all those lines. You could just stroke the rect:
.on('click', function(d) {
s.click ++;
d3.select(this)
.style("stroke", "#f4363f")
.style("stroke-width", "1px");
});
Running code:
// grid basic variables
var dimension = 10,
width = 50,
height = 50;
function gridData() {
var data = new Array();
// rectangle variables
var rectXpos = 0,
rectYpos = 0,
rectWidth = width,
rectHeight = height;
click = 0;
// iterate for rows
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension; column++) {
// rectClass = "rect" + rectXpos.toString() + rectYpos.toString();
data.push({
x: rectXpos,
y: rectYpos,
width: rectWidth,
height: rectHeight,
// class: rectClass,
click: click
});
// increment the x position. I.e. move it over by 50 (width variable)
rectXpos += rectWidth + 1;
}
// reset the x position after a row is complete
rectXpos = 0;
// increment the y position for the next row. Move it down 50 (height variable)
rectYpos += rectHeight + 1;
}
return data;
}
var gridData = gridData();
// I like to log the data to the console for quick debugging
console.log(gridData);
var grid = d3.select("#grid")
.append("svg")
.attr("width", width*dimension)
.attr("height",height*dimension);
var rect = grid.selectAll(".square")
.data(gridData)
.enter().append("rect")
.attr("class","rect")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.width; })
.attr("height", function(d) { return d.height; })
.style("fill", "#f2f2f2")
.style("stroke", "#fff")
.on('click', function(d) {
d.click ++;
d3.select(this)
.style("stroke", "#f4363f")
.style("stroke-width", "1px");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="grid"></div>
Related
Legend Color not matching Screenshot I am Working on the d3js sequence sunburst chart.
My Sample Data is:
Shift-Mode-Machineid,MachineDuration
DayShift-auto-1091,1
DayShift-auto-1100,5
DayShift-auto-1100,117
DayShift-manual-1111,4
DayShift-manual-1112,6
DayShift-manual-1120,43
NightShift-auto-1150,9
NightShift-auto-1150,85
NightShift-auto-1150,6
NightShift-manual-1120,16
NightShift-manual-1120,1
NightShift-manual-1120,4
Please suggest how to match the legend color and the chart color. The text inside the legend is perfect but the color is a mismatch. I have to put correct colors. Please help.
// Dimensions of sunburst.[Screenshot of wrong color in legend][1]
var width = 750;
var height = 600;
var radius = Math.min(width, height) / 2;
// Breadcrumb dimensions: width, height, spacing, width of tip/tail.
var b = {
w: 75, h: 30, s: 3, t: 10
};
// Mapping of step names to colors.
var colors = {};
var color = d3.scale.category20b();
/*{
"home": "#5687d1",
"product": "#7b615c",
"search": "#de783b",
"account": "#6ab975",
"other": "#a173d1",
"end": "#bbbbbb"
}*/;
// Total size of all segments; we set this later, after loading the data.
var totalSize = 0;
var vis = d3.select("#chart").append("svg:svg")
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("id", "container")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var partition = d3.layout.partition()
.size([2 * Math.PI, radius * radius])
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
// Use d3.text and d3.csv.parseRows so that we do not need to have a header
// row, and can receive the csv as an array of arrays.
d3.text("Data.csv", function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);
/*After getting proper json, we are making colors variable to hold our data keys
*/
json.children.forEach(function(root, ind){
colors[root.name]=color(ind);
});
createVisualization(json);
});
// Main function to draw and set up the visualization, once we have the data.
function createVisualization(json) {
// Basic setup of page elements.
initializeBreadcrumbTrail();
drawLegend();
d3.select("#togglelegend").on("click", toggleLegend);
// Bounding circle underneath the sunburst, to make it easier to detect
// when the mouse leaves the parent g.
vis.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// For efficiency, filter nodes to keep only those large enough to see.
var nodes = partition.nodes(json)
.filter(function(d) {
return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
});
var path = vis.data([json]).selectAll("path")
.data(nodes)
.enter().append("svg:path")
.attr("display", function(d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
//.style("fill", function(d) { return colors[d.name]; })
.style("fill", function(d) { return color((d.parent ? d : d.children).name); })
.style("opacity", 1)
.on("mouseover", mouseover);
// Add the mouseleave handler to the bounding circle.
d3.select("#container").on("mouseleave", mouseleave);
// Get total size of the tree = value of root node from partition.
totalSize = path.node().__data__.value;
};
// Fade all but the current sequence, and show it in the breadcrumb trail.
function mouseover(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
d3.select("#percentage")
.text(percentageString);
d3.select("#explanation")
.style("visibility", "");
var sequenceArray = getAncestors(d);
updateBreadcrumbs(sequenceArray, percentageString);
// Fade all the segments.
d3.selectAll("path")
.style("opacity", 0.3);
// Then highlight only those that are an ancestor of the current segment.
vis.selectAll("path")
.filter(function(node) {
return (sequenceArray.indexOf(node) >= 0);
})
.style("opacity", 1);
}
// Restore everything to full opacity when moving off the visualization.
function mouseleave(d) {
// Hide the breadcrumb trail
d3.select("#trail")
.style("visibility", "hidden");
// Deactivate all segments during transition.
d3.selectAll("path").on("mouseover", null);
// Transition each segment to full opacity and then reactivate it.
d3.selectAll("path")
.transition()
.duration(1000)
.style("opacity", 1)
.each("end", function() {
d3.select(this).on("mouseover", mouseover);
});
d3.select("#explanation")
.style("visibility", "hidden");
}
// Given a node in a partition layout, return an array of all of its ancestor
// nodes, highest first, but excluding the root.
function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
return path;
}
function initializeBreadcrumbTrail() {
// Add the svg area.
var trail = d3.select("#sequence").append("svg:svg")
.attr("width", width)
.attr("height", 50)
.attr("id", "trail");
// Add the label at the end, for the percentage.
trail.append("svg:text")
.attr("id", "endlabel")
.style("fill", "#000");
}
// Generate a string that describes the points of a breadcrumb polygon.
function breadcrumbPoints(d, i) {
var points = [];
points.push("0,0");
points.push(b.w + ",0");
points.push(b.w + b.t + "," + (b.h / 2));
points.push(b.w + "," + b.h);
points.push("0," + b.h);
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
points.push(b.t + "," + (b.h / 2));
}
return points.join(" ");
}
// Update the breadcrumb trail to show the current sequence and percentage.
function updateBreadcrumbs(nodeArray, percentageString) {
// Data join; key function combines name and depth (= position in sequence).
var g = d3.select("#trail")
.selectAll("g")
.data(nodeArray, function(d) { return d.name + d.depth; });
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append("svg:g");
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
// .style("fill", function(d) { return colors[d.name]; });
.style("fill", function(d) { return color((d.parent ? d : d.children).name); });
entering.append("svg:text")
.attr("x", (b.w + b.t) / 2)
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; });
// Set position for entering and updating nodes.
g.attr("transform", function(d, i) {
return "translate(" + i * (b.w + b.s) + ", 0)";
});
// Remove exiting nodes.
g.exit().remove();
// Now move and update the percentage at the end.
d3.select("#trail").select("#endlabel")
.attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(percentageString);
// Make the breadcrumb trail visible, if it's hidden.
d3.select("#trail")
.style("visibility", "");
}
function drawLegend() {
// Dimensions of legend item: width, height, spacing, radius of rounded rect.
var li = {
w: 75, h: 30, s: 3, r: 3
};
var legend = d3.select("#legend").append("svg:svg")
.attr("width", li.w)
.attr("height", d3.keys(colors).length * (li.h + li.s));
var g = legend.selectAll("g")
.data(d3.entries(colors))
.enter().append("svg:g")
.attr("transform", function(d, i) {
return "translate(0," + i * (li.h + li.s) + ")";
});
g.append("svg:rect")
.attr("rx", li.r)
.attr("ry", li.r)
.attr("width", li.w)
.attr("height", li.h)
.style("fill", function(d) { return d.value; });
g.append("svg:text")
.attr("x", li.w / 2)
.attr("y", li.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.key; });
}
function toggleLegend() {
var legend = d3.select("#legend");
if (legend.style("visibility") == "hidden") {
legend.style("visibility", "");
} else {
legend.style("visibility", "hidden");
}
}
// Take a 2-column CSV and transform it into a hierarchical structure suitable
// for a partition layout. The first column is a sequence of step names, from
// root to leaf, separated by hyphens. The second column is a count of how
// often that sequence occurred.
function buildHierarchy(csv) {
var root = {"name": "root", "children": []};
for (var i = 0; i < csv.length; i++) {
var sequence = csv[i][0];
var size = +csv[i][1];
if (isNaN(size)) { // e.g. if this is a header row
continue;
}
var parts = sequence.split("-");
var currentNode = root;
for (var j = 0; j < parts.length; j++) {
var children = currentNode["children"];
var nodeName = parts[j];
var childNode;
if (j + 1 < parts.length) {
// Not yet at the end of the sequence; move down the tree.
var foundChild = false;
for (var k = 0; k < children.length; k++) {
if (children[k]["name"] == nodeName) {
childNode = children[k];
foundChild = true;
break;
}
}
// If we don't already have a child node for this branch, create it.
if (!foundChild) {
childNode = {"name": nodeName, "children": []};
children.push(childNode);
}
currentNode = childNode;
} else {
// Reached the end of the sequence; create a leaf node.
childNode = {"name": nodeName, "size": size};
children.push(childNode);
}
}
}
return root;
};
After doing some analysis, I could suggest you below code, try it once.
// Dimensions of sunburst.
var width = 750;
var height = 600;
var radius = Math.min(width, height) / 2;
// Breadcrumb dimensions: width, height, spacing, width of tip/tail.
var b = {
w: 75, h: 30, s: 3, t: 10
};
// Mapping of step names to colors.
var colors = {};/*{
"home": "#5687d1",
"product": "#7b615c",
"search": "#de783b",
"account": "#6ab975",
"other": "#a173d1",
"end": "#bbbbbb"
}*/;
// Total size of all segments; we set this later, after loading the data.
var totalSize = 0;
var vis = d3.select("#chart").append("svg:svg")
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("id", "container")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var partition = d3.layout.partition()
.size([2 * Math.PI, radius * radius])
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
// Use d3.text and d3.csv.parseRows so that we do not need to have a header
// row, and can receive the csv as an array of arrays.
d3.text("visit-sequences.csv", function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);
/*After getting proper json, we are making colors variable to hold our data keys
*/
json.children.forEach(function(root, ind){
colors[root.name]=color(root.name); //Changed ind to root.name
});
createVisualization(json);
});
// Main function to draw and set up the visualization, once we have the data.
function createVisualization(json) {
// Basic setup of page elements.
initializeBreadcrumbTrail();
drawLegend();
d3.select("#togglelegend").on("click", toggleLegend);
// Bounding circle underneath the sunburst, to make it easier to detect
// when the mouse leaves the parent g.
vis.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// For efficiency, filter nodes to keep only those large enough to see.
var nodes = partition.nodes(json)
.filter(function(d) {
return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
});
var path = vis.data([json]).selectAll("path")
.data(nodes)
.enter().append("svg:path")
.attr("display", function(d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", function(d) { return colors[d.name]; })
.style("opacity", 1)
.on("mouseover", mouseover);
// Add the mouseleave handler to the bounding circle.
d3.select("#container").on("mouseleave", mouseleave);
// Get total size of the tree = value of root node from partition.
totalSize = path.node().__data__.value;
};
// Fade all but the current sequence, and show it in the breadcrumb trail.
function mouseover(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
d3.select("#percentage")
.text(percentageString);
d3.select("#explanation")
.style("visibility", "");
var sequenceArray = getAncestors(d);
updateBreadcrumbs(sequenceArray, percentageString);
// Fade all the segments.
d3.selectAll("path")
.style("opacity", 0.3);
// Then highlight only those that are an ancestor of the current segment.
vis.selectAll("path")
.filter(function(node) {
return (sequenceArray.indexOf(node) >= 0);
})
.style("opacity", 1);
}
// Restore everything to full opacity when moving off the visualization.
function mouseleave(d) {
// Hide the breadcrumb trail
d3.select("#trail")
.style("visibility", "hidden");
// Deactivate all segments during transition.
d3.selectAll("path").on("mouseover", null);
// Transition each segment to full opacity and then reactivate it.
d3.selectAll("path")
.transition()
.duration(1000)
.style("opacity", 1)
.each("end", function() {
d3.select(this).on("mouseover", mouseover);
});
d3.select("#explanation")
.style("visibility", "hidden");
}
// Given a node in a partition layout, return an array of all of its ancestor
// nodes, highest first, but excluding the root.
function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
return path;
}
function initializeBreadcrumbTrail() {
// Add the svg area.
var trail = d3.select("#sequence").append("svg:svg")
.attr("width", width)
.attr("height", 50)
.attr("id", "trail");
// Add the label at the end, for the percentage.
trail.append("svg:text")
.attr("id", "endlabel")
.style("fill", "#000");
}
// Generate a string that describes the points of a breadcrumb polygon.
function breadcrumbPoints(d, i) {
var points = [];
points.push("0,0");
points.push(b.w + ",0");
points.push(b.w + b.t + "," + (b.h / 2));
points.push(b.w + "," + b.h);
points.push("0," + b.h);
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
points.push(b.t + "," + (b.h / 2));
}
return points.join(" ");
}
// Update the breadcrumb trail to show the current sequence and percentage.
function updateBreadcrumbs(nodeArray, percentageString) {
// Data join; key function combines name and depth (= position in sequence).
var g = d3.select("#trail")
.selectAll("g")
.data(nodeArray, function(d) { return d.name + d.depth; });
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append("svg:g");
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
.style("fill", function(d) { return colors[d.name]; });
entering.append("svg:text")
.attr("x", (b.w + b.t) / 2)
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; });
// Set position for entering and updating nodes.
g.attr("transform", function(d, i) {
return "translate(" + i * (b.w + b.s) + ", 0)";
});
// Remove exiting nodes.
g.exit().remove();
// Now move and update the percentage at the end.
d3.select("#trail").select("#endlabel")
.attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(percentageString);
// Make the breadcrumb trail visible, if it's hidden.
d3.select("#trail")
.style("visibility", "");
}
function drawLegend() {
// Dimensions of legend item: width, height, spacing, radius of rounded rect.
var li = {
w: 75, h: 30, s: 3, r: 3
};
var legend = d3.select("#legend").append("svg:svg")
.attr("width", li.w)
.attr("height", d3.keys(colors).length * (li.h + li.s));
var g = legend.selectAll("g")
.data(d3.entries(colors))
.enter().append("svg:g")
.attr("transform", function(d, i) {
return "translate(0," + i * (li.h + li.s) + ")";
});
g.append("svg:rect")
.attr("rx", li.r)
.attr("ry", li.r)
.attr("width", li.w)
.attr("height", li.h)
.style("fill", function(d) { return d.value; });
g.append("svg:text")
.attr("x", li.w / 2)
.attr("y", li.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.key; });
}
function toggleLegend() {
var legend = d3.select("#legend");
if (legend.style("visibility") == "hidden") {
legend.style("visibility", "");
} else {
legend.style("visibility", "hidden");
}
}
// Take a 2-column CSV and transform it into a hierarchical structure suitable
// for a partition layout. The first column is a sequence of step names, from
// root to leaf, separated by hyphens. The second column is a count of how
// often that sequence occurred.
function buildHierarchy(csv) {
var root = {"name": "root", "children": []};
for (var i = 0; i < csv.length; i++) {
var sequence = csv[i][0];
var size = +csv[i][1];
if (isNaN(size)) { // e.g. if this is a header row
continue;
}
var parts = sequence.split("-");
var currentNode = root;
for (var j = 0; j < parts.length; j++) {
var children = currentNode["children"];
var nodeName = parts[j];
var childNode;
if (j + 1 < parts.length) {
// Not yet at the end of the sequence; move down the tree.
var foundChild = false;
for (var k = 0; k < children.length; k++) {
if (children[k]["name"] == nodeName) {
childNode = children[k];
foundChild = true;
break;
}
}
// If we don't already have a child node for this branch, create it.
if (!foundChild) {
childNode = {"name": nodeName, "children": []};
children.push(childNode);
}
currentNode = childNode;
} else {
// Reached the end of the sequence; create a leaf node.
childNode = {"name": nodeName, "size": size};
children.push(childNode);
}
}
}
return root;
};
Try this code n tell me.
We need to change
json.children.forEach(function(root, ind){
colors[root.name]=color(ind); //Changed ind to root.name
});
so that
json.children.forEach(function(root, ind){
colors[root.name]=color(root.name);
});
Hope this is causing colors not to match.
Try n tell me now.
I have a visual of a two ringed pie chart.
What I'm building is the following:
- if a sector's radians are too small for the specified text then the text is hidden. This is done and seems to work ok
- if text is hidden then a label should appear outside the pie. This is also done when the visual is initially rendered.
When the radio button is pressed the labels outside the pie should transition accordingly. I've attempted the transition and slowed it to 3500 and these labels are just slowly transitioning off the screen.
How do I fix this transition?
The snippet I've added to try to create the transition starts at line 241:
var arcs2 = svg.data([json]).selectAll(".arcG");
arcs2.data(partition.nodes)
.transition()
.duration(3500)
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("text-anchor", "middle");
svg.selectAll(".theTxtsOuter")
.text(function(d, i) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return d.name;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return d.name;
} else {
return;
}
});
This is a plunk of the working(!) visual:
https://plnkr.co/edit/jYVPCL?p=preview
Here is the complete javascript used by the pie:
function pieChart(dataFile) {
var plot;
var vis;
var width = 400,
height = 400,
radius = Math.min(width, height) / 2.1,
color = d3.scale.ordinal()
.range(["#338ABA", "#016da9", "#4c96d5"])
.domain([0, 2]);
var labelr = radius + 5 // radius for label anchor
var div = d3.select("body")
.append("div")
.attr("class", "toolTip");
var arc = d3.svg.arc()
.startAngle(function(d) {
return d.x;
})
.endAngle(function(d) {
return d.x + d.dx;
})
.outerRadius(function(d) {
return (d.y + d.dy) / (radius);
})
.innerRadius(function(d) {
return d.y / (radius);
});
//check if the svg already exists
plot = d3.select("#svgPIEChart");
if (plot.empty()) {
vis = d3.select("#pieChart")
.append("svg")
.attr({
id: "svgPIEChart"
});
} else {
vis = d3.select("#svgPIEChart");
vis.selectAll("*").remove();
}
//group of the svg element
var svg = vis
.append("g")
.attr({
'transform': "translate(" + width / 2 + "," + height * .52 + ")"
});
//svg element
vis.attr({
//set the width and height of our visualization (these will be attributes of the <svg> tag
width: width,
height: height
});
d3.text(dataFile, function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);
// it seems d3.layout.partition() can be either squares or arcs
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) {
return d.SalesRev;
});
var path = svg.data([json]).selectAll(".theArc")
.data(partition.nodes)
.enter()
.append("path")
.attr("class", "theArc")
.attr("id", function(d, i) {
return "theArc_" + i;
}) //Give each slice a unique ID
.attr("display", function(d) {
return d.depth ? null : "none";
})
.attr("d", arc)
.style("stroke", "#fff")
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.attr("fill-rule", "evenodd")
.style("opacity", 0.01)
.style("stroke-opacity", 0.01)
.each(stash);
path.transition()
.duration(PIEOBJ.transTime)
.style("opacity", 1)
.style("stroke-opacity", 1)
path
.on("mouseout", mouseout)
.on("mousemove", function(d) {
div.style("left", d3.event.pageX + 10 + "px");
div.style("top", d3.event.pageY - 25 + "px");
div.style("display", "inline-block");
div.html(d.name + "<br>" + PIEOBJ.formatShrtInt(d.SalesRev));
})
var txts = svg.data([json]).selectAll(".theTxts")
.data(partition.nodes)
.enter()
.append("text");
txts
.attr("class", "theTxts")
.attr("dx", 10) //Move the text from the start angle of the arc
.attr("dy", 15) //Move the text down
.style("opacity", 0)
txts
.transition()
.duration(PIEOBJ.transTime)
.style("opacity", 1);
var txtPths = txts.append("textPath")
// .attr("xlink:href", function(d, i) {
.attr("href", function(d, i) {
return "#theArc_" + i;
})
.text(function(d) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return;
} else {
return d.name;
}
});
/* ------- TEXT LABELS OUTSIDE THE PIE-------*/
//var arcs = svg.selectAll(".theArc");
var arcs = svg.data([json]).selectAll(".arcG")
.data(partition.nodes)
.enter()
.append("g")
.attr("class", "arcG");
arcs.append("text")
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
console.log(c, h);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("text-anchor", "middle")
.text(function(d, i) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return d.name;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return d.name;
} else {
return;
}
})
.attr("class", "theTxtsOuter");
/* ----------------------------------------*/
d3.selectAll("input").on("change", function change() {
function createValueFunc(val) {
// currentMeasure = val;
return function(d) {
return d[val];
};
}
value = createValueFunc(this.value);
PIEOBJ.currentMeasure = this.value;
var path2 = svg.data([json]).selectAll(".theArc");
path2
.data(partition.value(value).nodes)
.transition()
.duration(1500)
.attrTween("d", arcTween)
.each("start", function() {
d3.select(this)
.on("mouseout", null) //CLEARING the listeners
.on("mousemove", null);
})
.each("end", function() {
d3.select(this)
.on("mouseout", mouseout) //attaching the listeners
.on("mousemove", function(d) {
div.style("left", d3.event.pageX + 10 + "px");
div.style("top", d3.event.pageY - 25 + "px");
div.style("display", "inline-block");
div.html(d.name + "<br>" + PIEOBJ.formatShrtInt(value(d)));
});
});
svg.selectAll("textPath")
.text(function(d) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return;
} else {
return d.name;
}
});
var arcs2 = svg.data([json]).selectAll(".arcG");
arcs2.data(partition.nodes)
.transition()
.duration(3500)
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("text-anchor", "middle");
svg.selectAll(".theTxtsOuter")
.text(function(d, i) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return d.name;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return d.name;
} else {
return;
}
});
// the following deletes what was originally created and then recreates the text
// svg.selectAll("#titleX").remove();
});
function mouseout() {
div.style("display", "none"); //<< gets rid of the tooltip <<
}
// Stash the old values for transition.
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
}
// Interpolate the arcs in data space.
function arcTween(a) {
var i = d3.interpolate({
x: a.x0,
dx: a.dx0
}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
}
});
}
// // Take a 2-column CSV and transform it into a hierarchical structure suitable
// // for a partition layout.
function buildHierarchy(csv) {
var root = {
"name": "root",
"children": []
};
for (var i = 0; i < csv.length; i++) {
var sequence = csv[i][0];
// var APD = +csv[i][1];
var SalesRev = +csv[i][1];
var Amount = +csv[i][2];
if (isNaN(SalesRev)) { // e.g. if this is a header row
continue;
}
var parts = sequence.split("-");
var currentNode = root;
for (var j = 0; j < parts.length; j++) {
var children = currentNode.children;
var nodeName = parts[j];
var childNode;
if (j + 1 < parts.length) {
// Not yet at the end of the sequence; move down the tree.
var foundChild = false;
for (var k = 0; k < children.length; k++) {
if (children[k].name == nodeName) {
childNode = children[k];
foundChild = true;
break;
}
}
// If we don't already have a child node for this branch, create it.
if (!foundChild) {
childNode = {
"name": nodeName,
"children": []
};
children.push(childNode);
}
currentNode = childNode;
} else {
// Reached the end of the sequence; create a leaf node.
childNode = {
"name": nodeName,
// "APD": APD,
"SalesRev": SalesRev,
"Amount": Amount
};
children.push(childNode);
}
}
}
root.children.forEach(function(v) {
v.SalesRev = 0;
v.Amount = 0;
v.children.forEach(function(a) {
v.SalesRev += a.SalesRev;
v.Amount += a.Amount;
});
});
return root;
}
When you initially position them you are transforming the text elements. When you transition them you are positioning the outer g elements. These causes conflicting transforms. Use:
arcs2.data(partition.nodes)
.select('text') //<-- apply on child text
.transition()
.duration(3500)
.attr("transform", function(d) {
...
});
Updated plunker.
I'm trying to put two (or even more -> that depends on the users settings) d3 Wordclouds to one page. However I'll get the following result:
It seems, that the word list won't be parsed correct.
My code looks like this:
(The php $Block variable specifies the position, where the recent wordcloud should be shown.)
var container = "svg_<?php echo $Block;?>";
var w = $('#word_cloud_<?php echo $Block;?>').width();
var h = w*0.75;
if($( window ).width()<400){
var maxRange=20;
}else if($( window ).width()<800){
var maxRange=40;
}else if($( window ).width()<1200){
var maxRange=60;
}else if($( window ).width()>=1200){
var maxRange=95;
}
var list_<?php echo $Block;?>=<?php echo $jsonWort; ?>;
var wordSize=12;
var layout;
generate_<?php echo $Block;?>(list_<?php echo $Block;?>);
function generate_<?php echo $Block;?>(list) {
//Blacklist wird gefiltert!
var blacklist=["ein","sind"];
list=list.filter(function(x) { return blacklist.indexOf(x) < 0 });
// Liste wird verarbeitet
result = { };
for(i = 0; i < list.length; ++i) {
if(!result[list[i]])
result[list[i]] = 0;
++result[list[i]];
}
var newList = _.uniq(list);
var frequency_list = [];
var len = newList.length;
for (var i = 0; i < len; i++) {
var temp = newList[i];
frequency_list.push({
text : temp,
freq : result[newList[i]],
time : 0
});
}
frequency_list.sort(function(a,b) { return parseFloat(b.freq) - parseFloat(a.freq) } );
for(var t = 0 ; t < len ; t++)
{
var addTime = (100 * t) +500;
frequency_list[t].time=addTime;
}
for(i in frequency_list){
if(frequency_list[i].freq*wordSize > 160)
wordSize = 3;
}
var sizeScale = d3.scale.linear().domain([0, d3.max(frequency_list, function(d) { return d.freq} )]).range([5, maxRange]);
layout= d3.layout.cloud().size([w, h])
.words(frequency_list)
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return sizeScale(d.freq); })
.on("end",draw)
.start();
}
function draw(words) {
var fill = d3.scale.category20();
d3.select(container).remove();
d3.select("#word_cloud_<?php echo $Block;?>").append(container)
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + [w/2, h/2] + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.transition()
.duration(function(d) { return d.time} )
.attr('opacity', 1)
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "rotate(" + d.rotate + ")";
})
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
I believe that the mistake has to be somewhere in
var sizeScale = d3.scale.linear().domain([0, d3.max(frequency_list, function(d) { return d.freq} )]).range([5, maxRange]);
layout= d3.layout.cloud().size([w, h])
.words(frequency_list)
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return sizeScale(d.freq); })
.on("end",draw)
.start();
But I'm not able to find it.
You overwrite it because of the line
d3.select(container).remove();
With d3 you are basically creating complex SVGs on the fly. You can have as many elements as you want. As long as you do not remove them, they'll remain there. But you need to also make sure that they do not overlap. See here and (very cool!) here
I have made a bar chart based on revenue(x axis) and country(y axis) basis.My bar chart is done but I want to select multiple values of country from a dropdown and according to those values the bars of only that country should be shown others hide...For single selection i have done but for multiple values of country how to filter and show in D3 Js bar chart I am stuck.
The localDataJson contains the data:
localdatajson=[
{"Country";"USA","Revenue":"12","TurnOver":"16"},
{"Country";"Brazil","Revenue":"4.5","TurnOver":"16"},
{"Country";"Belzium","Revenue":"4.8","TurnOver":"16"},
{"Country";"Britain","Revenue":"20","TurnOver":"16"},
{"Country";"Canada","Revenue":"6.5","TurnOver":"16"},
{"Country";"DenMark","Revenue":"7.5","TurnOver":"16"}
]
text parameter would be an array in case of multiple selection like
for eg. text=["USA","Brazil","Britain"]
I want to show bars only for these three countries...
Here is my code
function revenueBar(localDataJson, text) {
var w = 400;
var h = 400;
var barPadding = 1;
var maxRevenue = 0;
var maxTurnOver = 0;
var padding = {
left: 45, right: 10,
top: 40, bottom: 60
}
var maxWidth = w - padding.left - padding.right;
var maxHeight = h - padding.top - padding.bottom;
for (var j = 0; j < localDataJson.length; j++) {
if (localDataJson[j].Revenue > maxRevenue) {
maxRevenue = localDataJson[j].Revenue;
}
}
for (var j = 0; j < localDataJson.length; j++) {
if (localDataJson[j].TurnOver > maxTurnOver) {
maxTurnOver = localDataJson[j].TurnOver;
}
}
var convert = {
x: d3.scale.ordinal(),
y: d3.scale.linear()
};
// Define your axis
var axis = {
x: d3.svg.axis().orient('bottom')
//y: d3.svg.axis().orient('left')
};
// Define the conversion function for the axis points
axis.x.scale(convert.x);
// axis.y.scale(convert.y);
// Define the output range of your conversion functions
convert.y.range([maxHeight, 0]);
convert.x.rangeRoundBands([0, maxWidth]);
convert.x.domain(localDataJson.map(function (d) {
return d.Country;
})
);
convert.y.domain([0, maxRevenue]);
$('#chartBar').html("");
var svg = d3.select("#chartBar")
.append("svg")
.attr("width", w)
.attr("height", h);
// The group node that will contain all the other nodes
// that render your chart
$('.bar-group').html("");
var chart = svg.append('g')
.attr({
class: 'container',
transform: function (d, i) {
return 'translate(' + padding.left + ',' + padding.top + ')';
}
});
chart.append('g') // Container for the axis
.attr({
class: 'x axis',
transform: 'translate(0,' + maxHeight + ')'
})
.call(axis.x)
.selectAll("text")
.attr("x", "-.8em")
.attr("y", ".15em")
.style("text-anchor", "end")
.attr("transform", "rotate(-65)");// Insert an axis inside this node
$('.axis path').css("fill", "none");
chart.append('g') // Container for the axis
// .attr({
// class: 'y axis',
// height: maxHeight,
// })
//.call(axis.y);
var bars = chart
.selectAll('g.bar-group')
.data(localDataJson)
.enter()
.append('g') // Container for the each bar
.attr({
transform: function (d, i) {
return 'translate(' + convert.x(d.Country) + ', 1)';
},
class: 'bar-group'
});
//Here goes filter thing ,bar of filter values will be shown others hide
if (text != "All" && text != "Clear Filter") {
svg.selectAll('g.bar-group')
.filter(function (d) {
return text != d.Country;
})
.attr("display", "none");
svg.selectAll('g.bar-group')
.filter(function (d) {
return text == d.Country;
})
.attr("display", "inline");
}
var color = d3.scale.category20();
// var color = d3.scale.ordinal()
// .range(['#f1595f', '#79c36a', '#599ad3', '#f9a65a', '#9e66ab','#cd7058']);
bars.append('rect')
.attr({
y: maxHeight,
height: 0,
width: function (d) { return convert.x.rangeBand(d) - 3; },
class: 'bar'
})
.transition()
.duration(1500)
.attr({
y: function (d, i) {
return convert.y(d.Revenue);
},
height: function (d, i) {
return maxHeight - convert.y(d.Revenue);
}
})
.attr("fill", function (d, i) {
// return color(i);
return color(d.Country);
})
// for (var i = 0; i < text.length; i++) {
// }
svg.append("text")
.attr("x", (w + padding.left + padding.right) / 2)
.attr("y", 25)
.attr("class", "title")
.attr("text-anchor", "middle")
.text("Revenue Bar Chart")
;
var svgs = svg.select("g.container").selectAll("text.label")
// svgs.selectAll("text")
.data(localDataJson)
.enter()
.append("text")
.classed("label", true)
//.transition() // <-- This is new,
// .duration(5000)
.text(function (d) {
return (d.Revenue);
})
.attr("text-anchor", "middle")
//// Set x position to the left edge of each bar plus half the bar width
.attr("x", function (d, i) {
// return (i * (w / localDataJson.length)) + ((w / localDataJson.length - barPadding) / 2);
return convert.x(d.Country) + (convert.x.rangeBand(d) - 3) / 2;
})
.attr({
y: function (d, i) {
return convert.y(d.Revenue) +20;
// return maxHeight;
},
})
.attr("font-family", "sans-serif")
.attr("font-size", "13px")
.attr("fill", "white")
}
Garima you need to do it like this in the section where you hiding the bars using display:none
svg.selectAll('g.bar-group')
.filter(function (d) {
if(text.indexOf(d.Country) == -1)
return true;
else
return false;
})
.attr("display", "none");
Note: In this i am assuming that text is an array of selected countries even when its a single selection.
Hope this helps
I am attempting to create a bar graph that can be updated by dragging the bars. I have been able to create such a graph but the dragging is not smooth. View my attempt in fiddle
Here is my code:
var data = [{x:-6.1, y: "I"}, {x:-4.5, y: "H"}, {x: -0.4, y: "G"},{x:0.8, y: "F"}, {x:3.8, y: "E"}, {x: 5.3, y: "D"}];
drawDraggableBarGraph= function(data, drawSmoothCurve, element) {
var bar, barHeight, datae, h, height, j, len, lineFunc, margin, objects, parsedX, svg, w, width, xAxis, xMax, xMin, xScale;
$(element).empty();
svg = d3.select(element).append("svg");
margin = {
top: 10,
bottom: 20,
left: 30,
right: 10
};
w = $(element).width();
h = 200;
width = w - margin.left - margin.right;
height = h - margin.top - margin.bottom;
for (j = 0, len = data.length; j < len; j++) {
datae = data[j];
parsedX = parseFloat(datae.x);
datae.x = parsedX;
}
svg = svg.attr("width", w).attr("height", h).append("g").attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
xMin = d3.min(data, function(d) {
return d.x;
});
xMax = d3.max(data, function(d) {
return d.x;
});
xScale = d3.scale.linear().domain([xMin > 0 ? xMin * 0.95 : xMin * 1.05, xMax * 1.05]).range([0, width]);
barHeight = (height / data.length) * 0.9;
xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(-height);
svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis);
objects = svg.append('svg').attr('width', width).attr('height', height);
bar = objects.selectAll("g").data(data).enter().append("g").attr("transform", function(d, i) {
return "translate(0," + (i * barHeight + height * 0.05) + ")";
}).attr('fill', function(d, i) {
return d3.scale.category20().range()[i];
});
bar.append('rect').attr("x", 0).attr('width', width).attr('height', barHeight - 1).attr('fill', 'transparent');
bar.append("rect").attr("x", function(d) {
if (d.x > 0) {
return xScale(0);
} else {
return xScale(d.x);
}
}).attr("width", function(d) {
if (d.x > 0) {
return xScale(d.x) - xScale(0);
} else {
return xScale(0) - xScale(d.x);
}
}).attr("height", barHeight - 1).attr('class', function(d) {
return "drag-bar-" + d.y;
});
bar.on('mouseover', function(d) {
jQuery("line.default-hidden").hide();
return $(this).children('line').show();
}).on('mouseout', function(d) {
return jQuery("line.default-hidden").hide();
});
bar.append("text").attr("dy", ".75em").attr("y", barHeight / 2 - 7).attr("x", function(d) {
if (d.x > 0) {
return xScale(0) - 10;
} else {
return xScale(0) + 10;
}
}).attr("text-anchor", "middle").text(function(d) {
return d.y;
});
if (drawSmoothCurve) {
// Try commenting out from here
lineFunc = d3.svg.line().x(function(d) {
return xScale(d.x);
}).y((function(_this) {
return function(d, i) {
return i * barHeight + barHeight / 2 + height * 0.05;
};
})(this)).interpolate("basis");
objects.append("path").attr("d", lineFunc(data)).attr("stroke", "#666666").attr("stroke-width", 1).attr("fill", "none");
// Till here
bar.append('line').attr('x1', function(d) {
return xScale(d.x);
}).attr('x2', function(d) {
return xScale(d.x);
}).attr('y1', -1).attr('y2', barHeight).attr('stroke', 'black').attr('stroke-width', '6').attr('class', function(d) {
return "default-hidden drag-line-" + d.y;
}).on("mousedown", function(d) {
return d3.select(this).classed('drag-enabled', true);
}).on('mousemove', function(d) {
var newX, p;
if ($(this).attr('class').indexOf('drag-enabled') < 0) {
return;
}
p = d3.mouse(this);
newX = p[0];
return d3.select(this).attr('x1', newX).attr('x2', newX);
}).on('mouseup', (function(_this) {
return function(d) {
var attrY, classList, k, klass, l, len1, len2, newBarX;
d3.select(this).classed('drag-enabled', false);
newBarX = d3.mouse(this)[0];
classList = this.classList;
attrY = null;
for (k = 0, len1 = classList.length; k < len1; k++) {
klass = classList[k];
if (klass.indexOf('drag-line') >= 0) {
attrY = klass.slice(klass.lastIndexOf('-') + 1);
}
}
data = data;
for (l = 0, len2 = data.length; l < len2; l++) {
datae = data[l];
if (datae.y === attrY) {
datae.x = xScale.invert(newBarX).toFixed(2);
}
}
return drawDraggableBarGraph(data, true,".sliding-bar");
};
})(this));
}
}
drawDraggableBarGraph(data, true,".sliding-bar");
I am trying to make the process of updating values user-friendly but halts while dragging kills it somehow.
Any suggestions to make it smooth