How to draw responsive lines between divs

喜夏-厌秋 提交于 2021-02-07 19:32:32

问题


Here's a photo of what I'm trying to translate into code.

I have a few more "Concept" areas I'm going to need to lay out and am having difficulty figuring out a method to embark upon in order to achieve this in a manner that works responsively. My original idea was to just lay everything out except the lines so I can adjust their positions accordingly to each screen size then come back and do a set of lines for each shift of elements using media queries to toggle them. For small tablet and cell phone sizes I figured I'd just make the rough sketch bigger and have little icons on each part the lines are pointing to that opens a popup window giving the explanation.

So far I've found a few posts on here that showed how to draw the lines both with SVG and Canvas, but I noticed that the examples they gave only worked for the window size they designed it for, when I re-sized the fiddle window everything went way off.

I came across a couple more posts where jsPlumb was heavily praised, though I am most definitely going to start learning and using it, so far it doesn't seem appropriate for what I want to do with this in particular other than the fact it creates and maintains a line between the source and target no matter where they're located. I feel the draggable nature of it was unnecessary for what I want to do and couldn't find out if there was a way to toggle it on and off, I don't want people trying to scroll down on their phone and getting stuck swiping the boxes around the screen.

It turns out that there really isn't much on jsPlumb, but looking into it led me to GoJS, which seems very similar to jsPlumb but once again, I wasn't able to find an extensive amount of info on it or many videos of people going in depth with how to do things with it.

I came across SVGjs and looked into it, but from the examples I've seen it seems pretty much the same thing as the first SVG and Canvas examples I saw which didn't appear to provide flexibility. On top of this I'm just now at a point where I can "baby talk" in javascript so though I can understand it enough to be able to identify what I don't understand and look it up, I'm not fluent enough to keep up with the tone of the info I am finding about these libraries which is written for those who already know the depths of JS well enough to not need more explaining.

I know normally you guys prefer to see an example of code we tinkered with so far to have actual coding issues to be resolved, vs. asking you how to do something n have you do all the coding work for us, but I'm at a point where I don't even know what to try in order to accomplish this. So I hope you guys can see I've genuinely tried to approach this the best way I'm able to so far and don't stone me with the down votes lol. I truly don't know where to go from here.


回答1:


Objectively, it looks like you are trying to draw a number of connector lines between two points arbitrarily anchored within HTML elements. In a similar project I used this recipe:

  • a transparent div with height of a few px, a 2px width coloured bottom border and with z-index placing it on top of other content.
  • some trig math on the points to be joined from which I calculated distance and angle
  • which I used as CSS div width and rotation. I also made it non-selectable so it would not interfere with other element selection / clicking.

Result was a div with one bottom corner at point A and the other at point B, producing a very nice line over which I had full control.

You would need to do that twice to get your elbow connection. You could trigger the draw after the page render or redraw etc.

Working example in snippet below - probably run best in full-screen, and at codepen http://codepen.io/JEE42/pen/aBOQGj in case you want to play.

/*
muConnector - a class to create a line joining two elements.

To use, call new with id's of both elements and optonal lineStyle (which must be a valid css border line def such as '1px solid #000' , e.g.
var c1=new Connector(id1, id2, lineStyle)

Default line style is '1px solid #666666'

Whatever you use for drag control, call moved(e, ele) per increment of movement, where e=event and ele=the jq element being moved.

*/

var Connector = function(params) {
if (typeof(params) == "undefined") { return false }; // If no params then abandon.
// Process input params.
var ele1=params.ele1 || '';   // First element to link
var ele2=params.ele2 || '';   // Second element to link
if ( ele1.length === 0 || ele2.length === 0)  { return false }; // If not two element id's then abandon. 

var className=params.class || 'muConnector'

var lineStyle=params.lineStyle || '1px solid #666666';   // CSS style for connector line.

this.gapX1=params.gapX1 || 0;  // First element gap before start of connector, etc
this.gapY1=params.gapY1 || 0;  
this.gapX2=params.gapX2 || 0;
this.gapY2=params.gapY2 || 0;


this.gap=params.gap || 0; // use a single gap setting.
if ( this.gap > 0 ) {
	this.gapX1 = this.gap
	this.gapY1 = this.gap
	this.gapX2 = this.gap
	this.gapY2 = this.gap
}

var pos = function() { // only used for standalone drag processing.
	this.left = 0;
	this.top = 0;
}
		
this.PseudoGuid = new (function() { // Make a GUID to use in unique id assignment - from and credit to http://stackoverflow.com/questions/226689/unique-element-id-even-if-element-doesnt-have-one
	this.empty = "00000000-0000-0000-0000-000000000000";
	this.GetNew = function() {
		var fC = function() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1).toUpperCase(); }
		return (fC() + fC() + "-" + fC() + "-" + fC() + "-" + fC() + "-" + fC() + fC() + fC());
	};
})();

this.id = this.PseudoGuid.GetNew(); // use guid to avoid id-clashes with manual code.
this.ele1=($('#'+ele1));
this.ele2=($('#'+ele2));

// Append the div that is the link line into the DOM
this.lineID='L' + this.id;
$('body').append("<div id='" + this.lineID + "' class='" + className + "' style=  ></div>")
this.line = $('#L'+ this.id);
this.line.css({ position: 'absolute', 'border-left': this.lineStyle, 'z-index': -100 }) 

// We may need to store the offsets of each element that we are connecting.
this.offsets=[];
this.offsets[ele1]=new pos;
this.offsets[ele2]=new pos;

this.link(); // show the initial link
}

/*
Approach: draw a zero width rectangle where the top left is located at the centre of ele1. 
Compute and make rectangle height equal to the distance between centres of ele1 and ele2.
Now rotate the rectangle to the angle between the points.
Note tracks the edges of the elements so as not to overlay / underlay.
Also can accommodate a gap between edge of element and start of line.

*/	
Connector.prototype.link=function link() {

	var originX = this.ele1.offset().left + this.ele1.outerWidth() / 2;
	var originY = this.ele1.offset().top + this.ele1.outerHeight() / 2;
  
	var targetX = this.ele2.offset().left + this.ele2.outerWidth() / 2;
	var targetY = this.ele2.offset().top + this.ele2.outerHeight() / 2;
	
	var l = this.hyp((targetX - originX),(targetY - originY));
	var angle = 180 / 3.1415 * Math.acos((targetY - originY) / l);
	if(targetX > originX) { angle = angle * -1 }
	
	// Compute adjustments to edge of element plus gaps.
	var adj1=this.edgeAdjust(angle, this.gapX1 + this.ele1.width() / 2, this.gapY1 + this.ele1.height() / 2)
	var adj2=this.edgeAdjust(angle, this.gapX2 + this.ele2.width() / 2, this.gapY2 + this.ele2.height() / 2)

 
	l = l - ( adj1.hp + adj2.hp)
	
	this.line.css({ left: originX, height: l, width: 0, top: originY +  adj1.hp })
		.css('-webkit-transform', 'rotate(' + angle + 'deg)')
		.css('-moz-transform', 'rotate(' + angle + 'deg)')
		.css('-o-transform', 'rotate(' + angle + 'deg)')
		.css('-ms-transform', 'rotate(' + angle + 'deg)')
		.css('transform', 'rotate(' + angle + 'deg)')
		.css('transform-origin', '0 ' + ( -1 * adj1.hp) + 'px');

}

Connector.prototype.Round = function(value, places) {
    var multiplier = Math.pow(10, places);
    return (Math.round(value * multiplier) / multiplier);
}
	
Connector.prototype.edgeAdjust = function (a, w1, h1) {
	var w=0, h=0
	
	// compute corner angles
	var ca=[]
	ca[0]=Math.atan(w1/h1) * 180 / 3.1415926 // RADIANS !!!
	ca[1]=180 - ca[0]
	ca[2]=ca[0] + 180
	ca[3]=ca[1] + 180
	
	// Based on the possible sector and angle combinations work out the adjustments.
	if ( (this.Round(a,0) === 0)  ) {
		h=h1
		w=0
	}
	else if ( (this.Round(a,0) === 180)  ) {
		h=h1
		w=0
	}
	else if ( (a > 0 && a <= ca[0]) || (a < 0 && a >= (-1*ca[0]))  ) {
		h=h1
		w=-1 * Math.tan(a * ( 3.1415926 / 180)) * h1
	}
	
	else if (  a > ca[0] && a <= 90 ) {
		h=Math.tan((90-a) * ( 3.1415926 / 180)) * w1
		w=w1
	}
	else if (  a > 90 && a <= ca[1]  ) {
		h=-1 * Math.tan((a-90) * ( 3.1415926 / 180)) * w1
		w=w1
	}
	else if (  a > ca[1] && a <= 180  ) {
		h=h1
		w=-1 * Math.tan((180 - a) * ( 3.1415926 / 180)) * h1
	}
	else if (  a > -180 && a <= (-1 * ca[1])  ) {
		h=h1
		w= Math.tan((a - 180) * ( 3.1415926 / 180)) * h1
	}	
	else if (  a > (-1 * ca[1])  && a <=  0 ) {
		h=Math.tan((a-90) * ( 3.1415926 / 180)) * w1
		w= w1
	}	

	// We now have the width and height offsets - compute the hypotenuse.
	var hp=this.hyp(w, h)
	
	return {hp: hp }	
}		

Connector.prototype.hyp = function hyp(X, Y) {
return Math.abs(Math.sqrt( (X * X) + ( Y * Y) ))
}


Connector.prototype.moved=function moved(e, ele) {
	var id=ele.attr('id');
	this.link()
}





var line;
$( document ).ready(function() {
    console.log( "ready!" );

	var c1=new Connector({ele1: 'a', ele2: 'b', lineStyle: '1px solid red' })
	Setup(c1, 'a');
	Setup(c1, 'b');
	
	var c2=new Connector({ele1: 'a', ele2: 'c', lineStyle: '1px solid red' })
	Setup(c2, 'a');
	Setup(c2, 'c');

	var c3=new Connector({ele1: 'a', ele2: 'd', lineStyle: '1px solid red' })
	Setup(c3, 'a');
	Setup(c3, 'd');
	
	var c4=new Connector({ele1: 'b', ele2: 'c'})
	Setup(c4, 'b');
	Setup(c4, 'c');	

	var c5=new Connector({ele1: 'b', ele2: 'd'})
	Setup(c5, 'b');
	Setup(c5, 'd');	
	
	var c6=new Connector({ele1: 'c', ele2: 'd'})
	Setup(c6, 'c');
	Setup(c6, 'd');


	function Setup(connector, id) {
		var ele=$('#'+id);
		ele.on('mousedown.muConnector', function(e){
			
			//#critical: tell the connector about the starting position when the mouse goes down.
			connector.offsets[id].left=e.pageX - ele.offset().left;
			connector.offsets[id].top=e.pageY - ele.offset().top;
		
			e.preventDefault();
			
			//hook the mouse move			
			ele.on('mousemove.muConnector', function(e){
			   ele.css({left: e.pageX - connector.offsets[id].left, top: e.pageY - connector.offsets[id].top}); //element position = mouse - offset
			  connector.moved(e, ele); // #critical: call the moved() function to update the connector position.
			});
			
			//define mouse up to cancel moving and mouse up, they are recreated each mousedown
			$(document).on('mouseup', function(e){
			  ele.off('mousemove.muConnector');
			  //$(document).off('.muConnector');
			});
			
		});
	}		

});
.thing {
  width: 200px;
  height: 100px;
  background: transparent;
  position: absolute;
  border: 1px solid #666666;
}

.thing span {
  display: block;
  margin-left: 95px;
  margin-top: 40px;
}

.muConnector {
  position: absolute;
  border-left: 1px dashed red;
  z-index: 100;
  -webkit-transform-origin: top left;
  -moz-transform-origin: top left;
  -o-transform-origin: top left;
  -ms-transform-origin: top left;
  transform-origin: top left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span>Drag any box...</span>

  <div id="a" class='thing' style='left:400px; top: 100px;'>
    <span>o</span>
  </div>
  
  
  <div id="b" class='thing' style='left:600px; top: 300px;'>
    <span>o</span>
  </div>

  <div id="c" class='thing' style='left:400px; top: 500px;'>
    <span>o</span>
  </div>
  
  
  <div id="d" class='thing' style='left:200px; top: 300px;'>
    <span>o</span>
  </div>



回答2:


To fix your problem with resizing and everything, position the element using percentage and also create the sizes in percentage. If you don't know how to use position things with percentage, use the javascript below:

var 
        window_width = $(window).width(),
        window_height = $(window).height(),
        unique_name,
        parallax_name_list = [],
        parallax_positions = [];

var vtg = {

    pos: function(obj){
        var posType = "offset";
        determineObject(obj, posType, window_width, window_height);
    },

    coord: function(){
        detectObjectCoords();
    },
}   
function determineObject(obj, posType, window_width, window_height){
        if (($("#"+obj).length == 0)&&($("."+obj).length == 0)){
            throw new Error("VTG_SCRIPT_ERROR: Could not find element '"+obj+"'");
            return;
        } else if (($("#"+obj).length == 1)&&($("."+obj).length == 1)){
            throw new Error("VTG_SCRIPT_ERROR: Classnames and IDs cannot not have the same name.");
            return;
        } else {
                detectObjectType(obj, posType, window_width, window_height);
        }
}
function detectObjectType(obj, posType, window_width, window_height){

var object_type;

if ($("#"+obj).length == 1){
    var object_type = "id";
    positionObject(obj, object_type, posType, window_width, window_height);
} else {
    var object_type = "classname";
    positionObject(obj, object_type, posType, window_width, window_height);
}
}
function positionObject(obj, type, posType, window_width, window_height){

var 
    draggable,
    pos = posType,
    idclass,
    type,
    dot = ".",
    name;

if (type == "id"){
    draggable = $("#"+obj).draggable();
    idclass = "#";
    name = idclass + obj;
    unique_name = name;
} else { 
    draggable = $("."+obj).draggable();
    idclass = ".";
    name = idclass + obj;
    unique_name = name;
}
var
    elem = $(name).position(),
    elem_top = elem.top/$(window).height() * 100,
    elem_left = elem.left/$(window).width() * 100;

        if ((isNaN(elem_top))&&(isNaN(elem_left))){
            console.log("%cCould not calculate the position of the requested object!","font-weight: bold; color: #CC181E;");
            return;
        } else {
            console.debug("$('"+name+"').css({ position: 'absolute', top: '"+elem_top+"%', left: '"+elem_left+"%'});"); 
        }
}
function detectObjectCoords( ){

    var 
        elm = $(unique_name).position(),
        coords_top = elm.top/$(window).height() * 100,
        coords_left = elm.left/$(window).width() * 100;

            console.clear();
            console.log("%cUpdating console","color: #34A953;");
            console.debug("$('"+unique_name+"').css({ position: 'absolute', top: '"+coords_top+"%', left: '"+coords_left+"%'});");

    return;
}
function error(msg){
throw new Error ("VTG_SCRIPT_ERROR: " + msg);
}
function checkType(name){
var new_name
        if ($("#" + name).length == 1){
            new_name = "#" + name;
            return new_name;
        } 
        else if ($("." + name).length == 1){
            new_name = "." + name;
            return new_name;
        } else {
            error("The element could not be found.");
            return false;
        }
}
function empty(word){
    if (word == ""){
        return true;
    } else {
        return false;
    }
}

This piece of code is from a framework i am currently working on. It requires the jQuery library. To use it, type in > vtg.pos("your object class/id"); and you should be able to drag your object around until you have refreshed the page. Once you are happy with the position, type vtg.coord(); in the console and copy the returned code and paste the code into your html/js file. If you are unsure what to copy, the returned code should look similar to this:

$('#your item').css({ position: 'absolute', top: '30.841007053291534%', left: '42.31259266123054%'});

You can use my code top position the lines you wanted




回答3:


I'm still unclear about exactly what you want to do, but perhaps the first diagram of the GoJS Introduction's second page demonstrates what you want: http://gojs.net/latest/intro/buildingObjects.html. That diagram is live! Now that diagram does not allow the user to draw new comments, but such functionality could be provided in an editor version of the diagram.

A sample also demonstrates a different style for comments, balloon comments, in http://gojs.net/latest/samples/comments.html. You can find a lot more samples at http://gojs.net.



来源:https://stackoverflow.com/questions/39720603/how-to-draw-responsive-lines-between-divs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!