For instance. I changed the task for my needs. And he wrote a solution that may be useful to someone. The following is a code in which a rectangle can only move along a line. Lines can be hidden with transparent color (.attr ("fill", "rgba (0, 0, 0, 0)");), and you can draw something else for the user.
http://codepen.io/anon/pen/pjoGOr
var deltaMax = 15; // max distance to line var svgx = 0; var svgy = 0; var limiters = []; //array of lines for align var svgContainer = d3.select("body").append("svg") .attr("width", 700) .attr("height", 700); var line1 = svgContainer.append("line") .style("stroke", "black") .attr("x1", 100) .attr("y1", 50) .attr("x2", 100) .attr("y2", 200); var line2 = svgContainer.append("line") .style("stroke", "blue") .attr("x1", 100) .attr("y1", 50) .attr("x2", 300) .attr("y2", 50); var line3 = svgContainer.append("line") .style("stroke", "green") .attr("x1", 100) .attr("y1", 50) .attr("x2", 300) .attr("y2", 200); limiters.push(line1); limiters.push(line2); limiters.push(line3); function distance2points(x1,y1,x2,y2){ //distance between 2 points return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } //find nearest intersection point function stick(x1,y1,x2,y2,x3,y3) { //intersection point var ipoint = { x:0, y:0 }; var onSegment = false; if((x2 - x1) == 0){ ipoint.x = x1; ipoint.y = y3; }else if((y2-y1) == 0){ ipoint.x = x3; ipoint.y = y1; }else{ var k = (y2-y1)/(x2-x1); var b = y2-k*x2; var kp = -1/k; var bp = y3-kp*x3; ipoint.x = (bp-b)/(k-kp); ipoint.y = ipoint.x*k+b; } //xxx helper point.attr("cx",ipoint.x); point.attr("cy",ipoint.y); if(distance2points(x3,y3,ipoint.x,ipoint.y) > deltaMax){ return false; } //intersectionn point on segment? if( (( x1 >= ipoint.x ) && ( x2 <= ipoint.x ) || ( x1 <= ipoint.x ) && ( x2 >= ipoint.x )) && (( y1 >= ipoint.y ) && ( y2 <= ipoint.y ) || ( y1 <= ipoint.y ) && ( y2 >= ipoint.y )) ){ onSegment = true; }else if(distance2points(x1,y1,ipoint.x,ipoint.y) < deltaMax){ ipoint.x = x1; ipoint.y = y1; onSegment = true; }else if(distance2points(x2,y2,ipoint.x,ipoint.y) < deltaMax){ ipoint.x = x2; ipoint.y = y2; onSegment = true; }else{ onSegment = false; } if(onSegment){ point.attr("fill","blue"); return ipoint; }else{ point.attr("fill","red"); return false; } } //mouse position svgContainer.on('mousemove', function () { svgx = d3.mouse(this)[0]; svgy = d3.mouse(this)[1]; }); // dragging function var drag = d3.behavior.drag() .on("drag", function(d,i) { for (i = 0; i < limiters.length; i++) { var obj = limiters[i]; var p = stick( obj.attr("x1"), obj.attr("y1"), obj.attr("x2"), obj.attr("y2"), svgx,//mouse position svgy ); if(p !== false){ d3.select(this).attr("transform", function(d,i){ return "translate(" + [ px,py ] + ")" }); break; } } }); var r = svgContainer.append("rect") .attr("x", 0) .attr("y", 0) .attr("width", 20) .attr("height", 20) .attr("fill", "red") .data([ {"x":0, "y":0} ]) .call(drag); //xxx helper - nearest intersection point var point = svgContainer.append("circle") .attr("cx", 10) .attr("cy", 30) .attr("r", 5) .attr("fill", "green");