I am trying to design an association miminap using d3.js. My goal is to position different items according to data and associate them using a drag function.
I try to calculate distance to different elements and if distance is lower than total radius, I consider this condition as a dragover. However drag function keeps selecting the node I drag instead of the node I drag it to.
var drag = d3.behavior.drag()
.on('dragstart', function() {})
.on('drag', function(d) {
nodes=d3.select(this.parentNode).selectAll("circle")[0];
nodes.pop(this);
var minDist=1000;
for (i=0;i<nodes.length;i++) {
var currentNode=d3.select(nodes[i]);
var r1=parseInt(this.style.r);
var r2=parseInt(currentNode.style("r"));
var dx=d3.event.x-parseInt(currentNode.style("cx"));
var dy=d3.event.y-parseInt(currentNode.style("cy"));
var dist=Math.sqrt(dx*dx+dy*dy);
if(dist<(r1+r2)) {
d.con=currentNode.attr("id");
currentNode.style("fill","red");
console.log(currentNode[0][0]);
}
}
d3.select(this)
.style("cx",d3.event.x)
.style("cy",d3.event.y);
})
Why does my futile attempt to remove the node I drag by entering nodes.pop(this) does not work ?
I have added an editable codepen version :
https://codepen.io/TeaCult/pen/ezJVyz?editors=1111
Thank you for reading.
Related
I have a simple visual of many rects, over 100 I'd say. For aesthetic purposes I want to create a high light effect on mouse click. I also wanted to make this effect somewhat intuitive by removing that effect once the user clicks on a new rect. However I couldn't get this to work without resorting to a d3.selectAll() call, so I'm thinking this approach might not be ideal if this project gets any bigger. Here is the code:
.on('click.highlight', function() {
//set any previously highlighted rects back to normal color/brightness
d3.selectAll('.highlight').transition().duration(250)
.style('fill', function(d) { return d3.rgb(d.color)})
d3.select(this).classed('highlight',true);
//now it's safe to assign the current highlighted rect a brighter hue... i think
d3.select(this).transition().duration(250)
.style('fill', function(d) { return d3.rgb(d.color).brighter(.5)})
})
Though this code does what I wanted it to do, but presumably there could only ever be 1 other highlight rect to worry about at any give time. So again, I'm not sure that using d3.selectAll() is warranted here.
So anyway, is there a more efficient way? I'd like to keep it all within one .on('click') function if possible.
If you are looking to avoid use of .selectAll, you could create a selection of one rect that contains the last clicked rectangle. Each time you click on a rectangle:
unhighlight the previously selected highlighted rect
update that selection to reflect the most recently clicked rectangle
highlight the newly selected rect
I use the variable highlightedRect to hold the selection that will allow the above workflow:
var svg = d3.select("body").append("svg")
.attr("width",600)
.attr("height",400);
var highlightedRect = d3.select(null);
var rects = svg.selectAll("rect")
.data(d3.range(1600))
.enter()
.append("rect")
.attr("y",function(d) { return Math.floor(d/50)*12; })
.attr("x",function(d) { return d%50 * 12 })
.attr("width",11)
.attr("height",11)
.attr("stroke","white")
.on("click",function(d) {
// Recolor the last clicked rect.
highlightedRect.attr("fill","black");
// Color the new one:
highlightedRect = d3.select(this);
highlightedRect.attr("fill","steelblue");
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
How to make the links start from the edge of the rectangle.
I am using Extjs modern framework 6.2.1. I am using sencha architect premium controls. I have overridden the control to bring this custom view. Now i am facing problem on links. I dont know how to make start it from the edge of the rectangle. Any help will be greatly appreciated
var rectSize=246;
this.setDiagonal(d3.svg.diagonal()
.source(function(d) { return {"x":d.source.x, "y":(d.source.y+rectSize)}; })
.target(function(d) { return {"x":(d.target.x), "y":d.target.y}; }) .projection(function(d) { return [d.y, d.x]; }));
Set the new diagonal with the new node y value. add the width of the node to source node y attr (Set it at the source). It will reflect for all the nodes
I'm trying to extend one rectangle using a drag handle without altering the other rectangles. I've tried to pass on the selected one using (this) but I can't get it to work. http://jsfiddle.net/sjp700/gmLaY/ -drag right hand black bar. Any help with this issue or alternatives?
var dragright = d3.behavior.drag()
.origin(function() {
var t = d3.select(this);
I could really use some guidance setting up a transition on my multiseries line chart. As an example of what I need, I've started with this great multiseries line chart: http://bl.ocks.org/mbostock/3884955. To that code, I've added an update() function that's called once using setInterval(). I've also created a new data set called data2.csv which is similar to data.tsv but has different values.
The update function should change the data that the line chart is displaying. Forget about making a nice smooth transition, I can't even get the data to update in the chart view. When I try using the update function, it looks like the new data is loaded properly into the javascript variables, but the lines on the chart don't change at all.
I've seen variations on this question asked a few times but haven't found an answer yet. Can anyone help me figure out how to transition this multi-series line chart to a new dataset (also multiseries)?
function update() {
d3.csv("data2.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
// format the date
data.forEach(function(d) {
d.date = parseDate(d.date);
});
// rearrange the data, same as in the original example code
var cities2 = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
// update the .city g's to the new dataset
var city2 = svg.selectAll(".city")
.data(cities2);
// redraw the lines with the new data???
city2.selectAll("path")
.attr("d", function(d) { return line(d.values); });
clearInterval(transitionInterval);
});
}
UPDATE: NikhilS's answer contains the key to the solution in the comment trail.
You should make sure you are following the enter + update process as outlined by Mike Bostock in his stuff on the General Update Pattern. It looks like you haven't invoked any kind of d3 transition. You also haven't specified an enter or exit for the update function, which will cause problems if you have new data coming in and/or old data going out. Try changing this:
var city2 = svg.selectAll(".city")
.data(cities2);
city2.selectAll("path")
.attr("d", function(d) { return line(d.values); });
to the following:
var city2 = svg.selectAll('.city')
.data(cities2);
var cityGroups = city2.enter().append('g')
.attr('class', 'city');
cityGroups.append('path')
.attr('class', 'line');
d3.transition().selectAll('.line')
.attr('d', function(d) { return line(d.values); });
city2.exit().remove();
I made a basic data re-join and update demo a while back, which you can view here.
use d3 Transition, you can make some sort of animation.
If you want to select a sub-interval of the data to plot the graph, no need manipulation on the data, just use a d3 brush and clip the graph
For a multi-series line graph with most of the line graph elements, you could refer to this example: http://mpf.vis.ywng.cloudbees.net/
In my d3js app, when the user hovers over a particular circle, I have it enlarge. That is no problem. At the same time, I want to select "all the others" and make them smaller. What is a good query to grab "all the other circles"?
You can use selection.filter or the lesser known functional form of the commonly used selection.select depending on your needs.
If you bind your DOM elements to data using key functions, which is the recommended way, then you can filter on a selection's key: http://jsfiddle.net/9TmXs/
.on('click', function (d) {
// The clicked element returns to its original size
d3.select(this).transition() // ...
var circles = d3.selectAll('svg circle');
// All other elements resize randomly.
circles.filter(function (x) { return d.id != x.id; })
.transition() // ...
});
Another general approach is comparing the DOM elements themselves: http://jsfiddle.net/FDt8S/
.on('click', function (d) {
// The clicked element returns to its original size
d3.select(this).transition() // ..
var self = this;
var circles = d3.selectAll('svg circle');
// All other elements resize randomly.
circles.filter(function (x) { return self != this; })
.transition()
// ...
});