Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restrict the movement of the element, when we drag it

Tags:

svg

d3.js

Objective: Move the red rectangle within the group of black rectangles. Black rectangles form a figure which is restricted to move the red rectangle.

window.onload=function(){

var inter = false;

//Make an SVG Container
var svgContainer = d3.select("body").append("svg")
    .attr("width", 800)
    .attr("height", 600);

//draw some rects
var r1 = svgContainer.append("rect")
    .attr("class", "interactive")
    .attr("x", 10)
    .attr("y", 223)
    .attr("width", 50)
    .attr("height", 150);

var r2 = svgContainer.append("rect")
    .attr("class", "interactive")
    .attr("x", 223)
    .attr("y", 10)
    .attr("width", 50)
    .attr("height", 300)
    .attr("transform", "rotate(45 220,10)");

//group of elements for limit red rect drag
var interactive = d3.selectAll(".interactive")
    .on("mouseover", function(d){
        inter = true;
    })
    .on("mouseleave", function(d){
        inter = false;
    });

// dragging function
var drag = d3.behavior.drag()
    .on("drag", function(d,i) {
        if(inter){
            d.x += d3.event.dx;
            d.y += d3.event.dy;
            d3.select(this).attr("transform", function(d,i){
                return "translate(" + [ d.x,d.y ] + ")"
            });
        }
    })
    .on("dragstart", function() {
        d3.select(this).style("pointer-events", "none")
    })
    .on("dragend", function() {
        d3.select(this).style("pointer-events", "auto")
    });

// red rectangle for draging
var r = svgContainer.append("rect")
    .attr("x", 150)
    .attr("y", 100)
    .attr("width", 20)
    .attr("height", 20)
    .attr("fill", "red")
    .data([ {"x":0, "y":0} ])
    .call(drag);

}

http://codepen.io/anon/pen/pjorBb

Here's my example of a subject. But it is not working properly. Maybe someone has a similar example of a correct or give clue how to do it correctly.

like image 558
Vasiliy Letuyev Avatar asked Sep 01 '15 06:09

Vasiliy Letuyev


2 Answers

This is not a perfect solution. But you may get the idea of how to implement the functionality from this demo.

//Make an SVG Container
var svgContainer = d3.select("body").append("svg")
	.attr("width", 800)
	.attr("height", 600);

//draw some rects
var r1 = svgContainer.append("rect")
	.attr("class", "interactive")
	.attr("x", 10)
	.attr("y", 223)
	.attr("width", 50)
	.attr("height", 150);

var r2 = svgContainer.append("rect")
	.attr("class", "interactive")
	.attr("x",223)
	.attr("y", 10)
	.attr("width", 50)
	.attr("height", 300)
	.attr("transform", "rotate(45 220,10)");

function pointRectangleIntersection(p, r) {
    return p.x >= r.x1 && p.x <= r.x2 && p.y >= r.y1 && p.y <= r.y2;
}
// dragging function
var drag = d3.behavior.drag()         
	.on("drag", function(d,i) {           
            var pt1 = d3.mouse(r1.node());
            var point1 = {x: pt1[0], y: pt1[1]};
            var bbox1 = r1.node().getBBox();
            var rect1 = { x1: bbox1.x, x2: bbox1.x+bbox1.width, y1: bbox1.y, y2: bbox1.y+bbox1.height };
            var pt2 = d3.mouse(r2.node());
            var point2 = {x: pt2[0], y: pt2[1]};
            var bbox2 = r2.node().getBBox();
            var rect2 = { x1: bbox2.x, x2: bbox2.x+bbox2.width, y1: bbox2.y, y2: bbox2.y+bbox2.height };  
            
            if(pointRectangleIntersection(point1, rect1) || pointRectangleIntersection(point2, rect2)){ 
                if(pointRectangleIntersection(point1, rect1)){
                   d.x = Math.max(0, Math.min(rect1.x2 - 20, d3.event.x));
                   d.y = Math.max(0, Math.min(rect1.y2 - 20, d3.event.y));
                } else{
                   d.x = Math.max(0, Math.min(rect2.x2 - 20, d3.event.x));
                   d.y = Math.max(0, Math.min(rect2.y2 - 20, d3.event.y));
                }              
                d3.select(this).attr("x", d.x);
                d3.select(this).attr("y", d.y);
                d3.event.sourceEvent.stopPropagation();             
            }
		
	});	
	
// red rectangle for draging
var r = svgContainer.append("rect")
	.attr("x", 150)
	.attr("y", 100)
	.attr("width", 20)
	.attr("height", 20)
	.attr("fill", "red")
	.datum({"x":0, "y":0})
	.call(drag);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
like image 64
Gilsha Avatar answered Nov 15 '22 12:11

Gilsha


I have came up with a similar situation in the past, This is how I overcame the issue.

When I drag the small element I use a getXY() function to determine the x, y of the element being dragged.

getXY() will take the mouse coordinates ( d3.mouse(this) ) and x, y , width, height of the inner & outer objects. Then it will figure out whether to return the mouse coordinates or not (in this case border coordinates of the outer object).

I hope you will get the idea. I think you can use it to solve your problem.

This is my original post. d3js transforming nested group images

like image 23
Fawzan Avatar answered Nov 15 '22 10:11

Fawzan