extjs draggable panel d3js pan zoom don't work - extjs

#mbostock
I have a draggable panel Extjs and i have included a graph d3js, customized drag and drop and added the zoom function.
In a simple html page all work fine, but in Extjs panel when drag a node in addition to move that, also activates pan, i would not disable the pan.
where I'm wrong?
This is the ExtjsPanel:
Ext.namespace("libs");
CSP.prj.tresprj.trestest.libs.MainPanel = Ext.extend(Ext.Panel, {
initComponent : function(){
this.addEvents({
'afterrender': true
});
Ext.apply(this,{title:'My Custom Panel2'});
Ext.apply(this,{html:'<div id="content"><div id="buttonDiv"><p>Selezionare il test da effettuare</p></div><!--buttonDiv--><div id="svgContent"></div><!--svgContent--></div><!--content-->'});
this.addListener('afterrender',function(){
Xxx.createButton("buttonDiv");
});
libs.MainPanel.superclass.initComponent.call(this);
}
});
and this is the Graph code:
var initSvg = function (target) {
//Activate context menu on right click
d3.select("body").attr("oncontextmenu", "return true");
//Create SVG element
if (jQuery(target + " svg").length) {
clearSvgDiv();
}
var svg = d3.select(target)
.append("svg")
.attr("width", width)
.attr("height", heigth)
.attr("id", "drawSvg")
.attr('preserveAspectRatio', 'xMinYMin slice')
.append('g');
return svg;
};
var clearSvgDiv = function () {
d3.select("svg")
.remove();
};
// Init force layout
var initLayout = function (svg) {
svg.append('svg:rect')
.attr('width', width)
.attr('height', heigth)
.attr('fill', 'white');
force = d3.layout.force()
.nodes(graph.getNodes())
.links(graph.getEdges())
.size([width, heigth])
.linkDistance([50])
.charge([-300])
.start();
};
// Creates nodes and edge to draw them on the page, calculated positions and repulsions.
var createNodesEdges = function (svg) {
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, heigth])
.range([heigth, 0]);
svg.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", rescale));
var path = svg.append("svg:g").selectAll("path")
.data(graph.getEdges())
.enter()
.insert("svg:path")
.attr("class", "line")
.style("stroke", "#ccc");
var node_drag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove)
.on("dragend", dragend);
var nodes = svg.selectAll("circle")
.data(graph.getNodes(), function (d) { return d.id; })
.enter()
.append("circle")
.attr("id", function (d) { return d.id; })
.attr("r", 5)
.call(node_drag);
force.on("tick", tick);
function tick() {
path.attr("d", function (d) {
var coordinatesP = findEdgeControlPoints(d);
return "M" + d.source.x + "," + d.source.y + "S" + coordinatesP.xp + "," + coordinatesP.yp + " " + d.target.x + "," + d.target.y;
});
nodes.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
}
function dragstart(d, i) {
force.stop(); // stops the force auto positioning before you start dragging
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick();
}
function dragend(d, i) {
d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
tick();
force.resume();
}
};
var rescale = function () {
var trans = d3.event.translate;
var scale = d3.event.scale;
svg.attr("transform","translate(" + trans + ")" + " scale(" + scale + ")");
};

i've found the problem, isn't extjs but minified version of d3js library downloaded by http://d3js.org/, with extended version i've no problem.

Related

application not working after chrome update to version 66 but works in firefox and edge

link to error message2 days back google chrome automatically updated to version 66. Since then my site which was running perfectly stopped working in google chrome. But it works perfectly in firefox and edge. So i started debugging in google chrome, then i found that the problem is with ajax calls. So initially for the main page, many ajax call will be hit. in that list of calls ,the last call executes the success part more than once. So when the susscess of response is executing for the second time it throws aw snap,page cannot be displayed error in chrome. Then i restricted the success call to once using some flag,then the main page was loading. But after submitting in left panel,the same ajax call will get hit, so now at this point i'm getting the same aw snap error. i'm using angularjs ajax. So please help me with this. when i debug i get below error message in console
error message in console
As discussed in comment this function of d3.js code causes the application to crash.So when i commented this except this chart all other things are working
var carbon = new Charts();
var chart = d3.select(this.selector);
var width = carbon.pixToVal(chart.style("width"));
var height = carbon.pixToVal(chart.style("height"));
/*var width=475;
var height=133;*/
/*var maxVal =100;*/
var maxVal =d3.max(jsonData,function(d){return d.current;})+ 15;
var length1=jsonData.length;
var newWidthParts=newWidth/10;
/*console.log(length1);
console.log(newWidth);*/
var newHeightParts=height/10;
var conatinerRectHt=newHeightParts*6.5;
/*var boxHeight=(newHeightParts*9-newHeightParts*2.5)/5;*/
var margin={
top: 15, left:15,right:15,bottom:20
}
var availableWidth = width - margin.left - margin. right;
var newWidth= availableWidth/length1;
var availableHeight = height - margin.top - margin.bottom;
var HeightForTopLabel =15;
var HeightForBottomLabel = 15;
var HeightOfAbsValue = 15;
var HeightForMidLabel = 15;
var OuterRectHeight= availableHeight - (HeightForBottomLabel) - HeightForTopLabel; /*- HeightOfAbsValue;*/
/* var BottomTextYPosition = margin.top + HeightForTopLabel + OuterRectHeight + HeightForBottomLabel ;
*/
var outerRectBottomYPosition = margin.top + HeightForTopLabel +OuterRectHeight;
//outerRectTopYPosition + OuterRectHeight
var outerRectTopYPosition = margin.top + HeightForTopLabel;
var BottomTextYPosition = height - margin.bottom;
var outerRectWidth = 50;
var innerRectWidth =8;
var outerRectDistFromLeft = newWidth/2 - outerRectWidth/2 + margin.left ;
var innerRectDistFromLeft = newWidth/2 - innerRectWidth/2 + margin.left ;
var creatSvg = chart.append("div").attr("class", "chartBox9")
.style("position", "absolute")
.style("height", "100%")
.style("width", "100%")
.append("svg").attr('id','defsGrad')
.attr("width", width)
.attr("height", height);
d3.select("#defsGrad")
.append("text").attr("x",availableWidth - 1.8*(margin.left + margin.right) )
.attr("y",height-5)
.attr("id","shareContritext")
.text("% Room Revenue")
.attr("font-size",10.5);
d3.select("#defsGrad").append("defs").append("linearGradient").attr('id','grad1')
.attr({'x1':'0%','y1':'100%','x2':'0%' ,'y2':'0%'}).append('stop')
.attr('offset',"0%").style({'stop-color':'rgb(215,214,214)','stop-opacity':'0.7'});
d3.select('#grad1').append('stop')
.attr('offset',"100%").style({'stop-color':'rgb(249,249,249)','stop-opacity':'0.9'});
svgContainer=creatSvg.selectAll('g')
.data(jsonData).enter()
.append("g")
.attr("transform",function(d,i){
var res = "translate(";
var val = i*newWidth;
return res+val+" 0)";
});
svgContainer.append("text")
.attr("y", margin.top)
.attr("font-size",fontsize)
.text(function(d) {
if( d.current == null){
d3.select(this).attr("x",outerRectDistFromLeft + outerRectWidth/5 + 5);
return "N/A";
}else
{
var xposition = outerRectDistFromLeft + (outerRectWidth/5+4);
var nbr = d.current;
// var decimals = (nbr!=Math.floor(nbr))?(nbr.toString()).split('.')[0].length:0;
var decimals;
if(nbr % 1 !== 0)
{
decimals = (nbr!=Math.floor(nbr))?(nbr.toString()).split('.')[0].length:0;
}else
{
decimals = nbr.toString().length;
}
d3.select(this).attr("x",xposition-(decimals-1)*4);
if(d.change==null)
{
d3.select(this).attr("x",outerRectDistFromLeft + outerRectWidth/5 + 5);
}
return d.current_prefix + d.current.toFixed(1) + d.current_suffix;}
});
var rectangle1 = svgContainer.append("rect")
.attr("class","outerRect9")
.attr("x", outerRectDistFromLeft)
.attr("y",outerRectTopYPosition)
.attr("width", outerRectWidth)
.attr("height", OuterRectHeight)
.attr("fill", "url(#grad1)");
svgContainer.append("text")
.attr("y", margin.top + HeightForTopLabel -2 )
.attr("fill",
function(d){
var colorChange="";
if(d.change>=0.0)
colorChange="#06C10C";
else
colorChange="#F7063A";
return colorChange;
}
)
.attr("font-size",fontsize)
.text(function(d) {
if( d.change == null){
d3.select(this).attr("fill","black");
d3.select(this).attr("x",outerRectDistFromLeft + outerRectWidth/5 + 7);
return "N/A";
}else{
var pre ="";
if(d.change>=0.0)
pre="+";
/*if(d.current!=null)
{
d3.select(this).attr("x", outerRectDistFromLeft + outerRectWidth/5+3 );
}*/
var xposition = outerRectDistFromLeft + outerRectWidth/5;
/* if(d.current =! null){
d3.select(this).attr("x", xposition);
}
else
{
d3.select(this).attr("x", xposition);
}*/
d3.select(this).attr("x", xposition);
return pre+d.change.toFixed(1)/*+d.change_suffix*/;}
});
var rectangle0=svgContainer.append('rect') .attr("x", innerRectDistFromLeft)
.attr("y",outerRectTopYPosition).attr('width',8).attr('height',OuterRectHeight)
.attr('fill','#BDBDB7').attr('opacity',0.5);
var rectangle2=svgContainer.append('rect') .attr("x", innerRectDistFromLeft)
.attr("y", function(d) {
if(d.current == null){return (margin.top + HeightForTopLabel +OuterRectHeight);}
else{ return (margin.top + HeightForTopLabel +OuterRectHeight) - ((d.current/maxVal)*OuterRectHeight);}
}).attr('width',8).attr('height',function(d) {
if(d.current == null){
return 0;
}
else{ return ((d.current/maxVal)*OuterRectHeight);}
}).attr('fill',function(d,i){return d.color_code;});
var data1 = d3.range(5)
var c = d3.scale.ordinal()
.domain(data1)
.rangeBands([outerRectBottomYPosition + 1.5 ,outerRectTopYPosition ])
var innerRect= svgContainer.selectAll('.inners')
.data(data1)
.enter()
.append('rect').attr('class','inners')
.attr('y',function(d) { return (c(d));/*return Math.round(c(d))*/ })
.attr('x',innerRectDistFromLeft )
.attr('width',8)
.attr('height',(OuterRectHeight/5) )/*+ (1.5*3)*/
.style('stroke','#F6F6F6')
.style('stroke-width','1.2')
.style('fill', "none");
svgContainer.append("text")
.attr("x",function(d,i){
var name =d.name;
var lengthOfText = name.length;
if(lengthOfText > 2){
return outerRectDistFromLeft + outerRectWidth/3 - lengthOfText*2;
}
else {
return outerRectDistFromLeft + outerRectWidth/3;
}
} )
.attr("y", BottomTextYPosition)
.attr("font-size",fontsize)
.text(function(d) {
return d.name;
})
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);
function handleMouseOver(d, i) {
creatSvg.append("rect").attr({
id: "tooltipvaluebox",
y: BottomTextYPosition,
height: 20,
fill:"#CACACE",
})
.attr("x",function(){
var len = (d.fullName.length*10)+20;
var val = newWidth*i;
if((val+len)>width){
var diff = (val+len) - width;
val = val - (diff+5);
}
return val+8;
} )
.attr("width",function(){
var len = d.fullName.length;
return (len*10)+20;
});
creatSvg.append("text").attr({
id: "tooltipvalue",
y: BottomTextYPosition+15
})
.attr("x",function(){
var len = (d.fullName.length*10)+20;
var val = newWidth*i;
if((val+len)>width){
var diff = (val+len) - width;
val = val - (diff+5);
}
return val+8;
} )
.text(function() {
return d.fullName ;
})
.attr('fill','black')
.style("font-family", "roboto");
};
function handleMouseOut(d, i) {
d3.select("#tooltipvaluebox").remove();
d3.select("#tooltipvalue").remove();
};
Chrome Version 66 for Windows seems to have a critical Bug in the DEV Engine. I think it's related to the meltdown hotfix in version 66. Its already fixed in Dev Channel V 68.x

3D Scatter chart scrolling if I scroll the scrollbar

I am rendering Highcharts 3D scatter graph and enabled scrolling as follows
xAxis: {
categories: xlist,
min: 0,
max: 4,
scrollbar: {
enabled: true
},
}
Also having chart scrolling with below code
$(chart.container).on('mousedown.hc touchstart.hc', function (eStart) {
eStart = chart.pointer.normalize(eStart);
var posX = eStart.chartX,
posY = eStart.chartY,
alpha = chart.options.chart.options3d.alpha,
beta = chart.options.chart.options3d.beta,
newAlpha,
newBeta,
sensitivity = 5; // lower is more sensitive
$(document).on({
'mousemove.hc touchmove.hc': function (e) {
e = chart.pointer.normalize(e);
newBeta = beta + (posX - e.chartX) / sensitivity;
chart.options.chart.options3d.beta = newBeta;
newAlpha = alpha + (e.chartY - posY) / sensitivity;
chart.options.chart.options3d.alpha = newAlpha;
chart.redraw(false);
},
'mouseup touchend': function () {
$(document).off('.hc');
}
});
});
If I scroll through scrollbar then the chart also moves. Is there any way to avoid chart scrolling if I scroll the scrollbar and if I take mouse on chart then the chart should move.
I made a hack and updated the above function as below, where I have restricted the chart rotation with if(e.target.classList.length == 0) when we use scrollbar.
$(chart.container).on('mousedown.hc touchstart.hc', function (eStart) {
eStart = chart.pointer.normalize(eStart);
var posX = eStart.chartX,
posY = eStart.chartY,
alpha = chart.options.chart.options3d.alpha,
beta = chart.options.chart.options3d.beta,
newAlpha,
newBeta,
sensitivity = 5; // lower is more sensitive
$(document).on({
'mousemove.hc touchmove.hc': function (e) {
if (e.target.classList.length == 0) {
e = chart.pointer.normalize(e);
newBeta = beta + (posX - e.chartX) / sensitivity;
chart.options.chart.options3d.beta = newBeta;
newAlpha = alpha + (e.chartY - posY) / sensitivity;
chart.options.chart.options3d.alpha = newAlpha;
chart.redraw(false);
}
},
'mouseup touchend': function () {
$(document).off('.hc');
}
});
});

Unit Test d3v4 zoom behavior in React with Chai

Using d3v4, react and chai (chai-enzyme, chai-jquery) for unit testing.
So I have a zoom behavior attached to my graph.
const zoom = d3
.zoom()
.scaleExtent([1, Infinity])
.on('zoom', () => {
this.zoomed()
})
And the zoom behavior is attached to the svg element.
const svg = d3
.select(this.node)
.select('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', `0 0 ${svgWidth} ${svgHeight}`)
.classed('svg-content-responsive', true)
.select('g')
.attr('transform', `translate(${margin.left},${margin.top})`)
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.call(zoom)
The on('zoom') callback is defined as
zoomed () {
const {gXAxis, plotPlanText, plotZones, width, xAxis, xScale} = this.graphObjects
const transform = d3.event.transform
// get xMin and xMax in the viewable world
const [xMin, xMax] = xScale.domain().map(d => xScale(d))
// Get reverse transform for xMin and xMax.
if (transform.invertX(xMin) < 0) {
transform.x = -xMin * transform.k
}
if (transform.invertX(xMax) > width) {
transform.x = xMax - width * transform.k
}
// transform the bars for zones
if (!isNaN(transform.x) && !isNaN(transform.y) && !isNaN(transform.k)) {
// rescale the x linear scale so that we can draw the x axis
const xNewScale = transform.rescaleX(xScale)
xAxis.scale(xNewScale)
// rescale xaxis
gXAxis.call(xAxis)
plotZones
.selectAll('rect')
.attr('x', (d, i) => transform.applyX(xScale(d.maxFrequency)))
.attr('width', (d) => -(transform.applyX(xScale(d.maxFrequency)) - transform.applyX(xScale(d.minFrequency))))
// transform the flow text
plotPlanText
.selectAll('.plan-text-src')
.attr('x', d => (transform.applyX(xScale(d.maxFrequency)) + transform.applyX(xScale(d.minFrequency))) / 2)
plotPlanText
.selectAll('.plan-text-dest')
.attr('x', d => (transform.applyX(xScale(d.maxFrequency)) + transform.applyX(xScale(d.minFrequency))) / 2)
}}
My unit test for firing up the "zoom" event is
describe('d3 Event handling', function () {
beforeEach(function () {
$.fn.triggerSVGEvent = function (eventName, delta) {
let event = new Event(eventName, {'bubbles': true, 'cancelable': false})
if (delta) {
event.deltaX = delta.x
event.deltaY = delta.y
}
this[0].dispatchEvent(event)
return $(this)
}
})
describe('When the chart is zoomed', function () {
let initialX
beforeEach(function () {
$('.flow-zone-container > svg > g').triggerSVGEvent('wheel')
})
it('should update elements when zoomed', function () {
...
})
})
})
When I use chai-jquery to trigger the "wheel" event, the zoom event is fired but the d3.event.tranform in the zoomed() method gives NaN for x, y and k. I want to test the zoomed() callback such that I have the d3.event.transform values to test the logic in the zoomed() method.
How can I fire the zoomEvent using "wheel" event or any other event such that d3.event is fired with some values?
Solved this. Instead of using the jquery to fire the event, I am now using zoomBehaviour.scaleBy(selection, scale) to fire the callback.

Google Maps - Fixed source and draggable destination in website

I`m using the API to integrate Google Maps in our website. But we did not met the requirement by using them.
I tried using different options. Our requirement is that - the source is set to fixed location(should not be dragged) and the accurate destination is to be entered in the text area and the end user should have an option to point out the exact location(may be near to the location entered in the destination text area).
calculation of distance and the remaining part is working fine for me. Pointing the exact location in the destination also achieved.
Only thing to be worked on is that fixing the source location(End user should not drag/move the source location).
<script type="text/javascript">
var source, destination;
var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
google.maps.event.addDomListener(window, 'load', function () {
new google.maps.places.SearchBox(document.getElementById('txtSource'));
new google.maps.places.SearchBox(document.getElementById('txtDestination'));
directionsDisplay = new google.maps.DirectionsRenderer({ 'draggable': true });
});
function GetRoute() {
var mapOptions = {
zoom: 4,
};
map = new google.maps.Map(document.getElementById('dvMap'), mapOptions);
directionsDisplay.setMap(map);
directionsDisplay.setPanel(document.getElementById('dvPanel'));
//*********DIRECTIONS AND ROUTE**********************//
source = document.getElementById("txtSource").value;
destination = document.getElementById("txtDestination").value;
var request = {
origin: source,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
//*********DISTANCE AND DURATION**********************//
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
origins: [source],
destinations: [destination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, function (response, status) {
if (status == google.maps.DistanceMatrixStatus.OK && response.rows[0].elements[0].status != "ZERO_RESULTS") {
var distance = response.rows[0].elements[0].distance.text;
var duration = response.rows[0].elements[0].duration.text;
var dvDistance = document.getElementById("dvDistance");
dvDistance.innerHTML = "";
dvDistance.innerHTML += "Distance: " + distance + "<br />";
dvDistance.innerHTML += "Duration:" + duration;
} else {
alert("Unable to find the distance via road.");
}
});
}
</script>
By using above code, Both the source and destination are draggable.
Google maps marker are not moveable by default, I think, when you set their coordinates on map they stay on the same location.
If still moving you can remove the event listener on the marker
<script type="text/javascript">
google.maps.event.addDomListener(window, 'load', function () {
new google.maps.places.SearchBox(document.getElementById('txtSource'));
new google.maps.places.SearchBox(document.getElementById('txtDestination'));
});
var gmarkers = [];
var map = null;
var startLocation = null;
var endLocation = null;
var directionsService = null;
var polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
//google.maps.event.addListener(map, 'click', function () {
// infowindow.close();
//});
directionsService = new google.maps.DirectionsService();
var request = {
origin: document.getElementById("txtSource").value,
destination: document.getElementById("txtDestination").value,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, RenderCustomDirections);
}
function RenderCustomDirections(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
var summaryPanel = document.getElementById("directions_panel");
startLocation = new Object();
endLocation = new Object();
summaryPanel.innerHTML = "";
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += route.legs[i].start_address + "<br />" + " to " + "<br />";
summaryPanel.innerHTML += route.legs[i].end_address + "<br />";
summaryPanel.innerHTML += route.legs[i].distance.text + "<br /><br />";
}
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i == 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
startLocation.marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green", false);
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
var dist_dur = "";
if (steps[j].distance && steps[j].distance.text) dist_dur += " " + steps[j].distance.text;
if (steps[j].duration && steps[j].duration.text) dist_dur += " " + steps[j].duration.text;
for (k = 0; k < nextSegment.length; k++) {
polyline.getPath().push(nextSegment[k]);
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
map.fitBounds(bounds);
endLocation.marker = createMarker(endLocation.latlng, "end", endLocation.address, "red", true);
}
else alert("Please try again " + status);
}
function createMarker(latlng, label, html, color, draggable) {
//alert("createMarker(" + latlng + "," + label + "," + html + "," + color + ")");
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
draggable: draggable,
map: map,
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
google.maps.event.addListener(marker, 'dragend', function () {
var request = {
origin: startLocation.marker.getPosition(),
destination: endLocation.marker.getPosition(),
travelMode: google.maps.DirectionsTravelMode.WALKING
};
startLocation.marker.setMap(null);
endLocation.marker.setMap(null);
gmarkers = [];
polyline.setMap(null);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
directionsService.route(request, RenderCustomDirections);
});
return marker;
}
</script>

Why does the d3 force layout seem to crash and how should I overcome it?

I was playing around with a force layout and when I drag an item aggressively around it seems to eventually cause things to freeze.
So the questions are,
Why does this happen?
Is there a way to catch this and restart it?
Example
$scope.render = function() {
_this.changeForce();
var circles = $scope.svg
.selectAll("circle")
.data(items)
circles.exit().remove();
circles.enter()
.append("circle")
.attr("r", function(d) {
return d.radius;
})
.attr("fill", "blue")
.call(d3.behavior.drag()
.origin(function(d) { return d; })
.on("drag", function(d) {
d.x = d3.event.x, d.y = d3.event.y;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
}))
}
On drag you are missing a call to make the force layout "tick" again:
.call(d3.behavior.drag()
.origin(function(d) { return d; })
.on("drag", function(d) {
d.x = d3.event.x, d.y = d3.event.y;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
_this.force.resume(); //<-- get it moving again
})
)
Updated code.

Resources