javascript - 如何在div之间绘制响应线

标签 javascript html svg html5-canvas

这是我试图将其转化为代码的照片。

enter image description here

我还有一些“概念”区域需要布局,但我很难想出一种方法来着手以响应式的方式实现这一目标。我最初的想法是只布置除线条以外的所有内容,这样我就可以根据每个屏幕尺寸调整它们的位置,然后返回并使用媒体查询来切换元素的每次移动做一组线条。对于小尺寸的平板电脑和手机,我想我只是把粗略的草图做得更大,并且在线条指向的每个部分上都有小图标,打开一个弹出窗口给出解释。

到目前为止,我在这里找到了一些展示如何使用 SVG 和 Canvas 绘制线条的帖子,但我注意到他们提供的示例仅适用于他们设计的窗口大小,当我重新调整 fiddle 窗口的大小,一切都结束了。

我看到了更多的帖子,其中 jsPlumb 受到了高度赞扬,尽管我肯定会开始学习和使用它,但到目前为止,它似乎不适合我想用它做的事情,特别是除了事实上,无论它们位于何处,它都会在源和目标之间创建并维护一条线。我觉得它的可拖动特性对于我想做的事情来说是不必要的,并且无法确定是否有办法打开和关闭它,我不希望人们试图在手机上向下滚动并卡住滑动屏幕周围的框。

事实证明,关于 jsPlumb 的内容确实不多,但对其进行调查后,我想到了 GoJS,它看起来与 jsPlumb 非常相似,但我又一次找不到大量关于它的信息,或者人们深入了解如何使用它做事的许多视频。

我遇到了 SVGjs 并对其进行了研究,但从我看到的示例来看,它似乎与我看到的第一个 SVG 和 Canvas 示例几乎相同,后者似乎没有提供灵 active 。最重要的是,我现在正处于可以用 javascript 进行“婴儿谈话”的阶段,所以尽管我可以充分理解它以能够识别我不理解的内容并进行查找,但我还不够流利跟上我发现的关于这些库的信息的基调,这些库是为那些已经足够了解 JS 的人编写的,不需要更多解释。

我知道通常你们更愿意看到我们为解决实际编码问题而修改过的代码示例,而不是问你如何做某事 n 你是否为我们完成了所有编码工作,但我我什至不知道要尝试什么才能做到这一点。所以我希望你们能看到我已经真正尝试以目前为止我能做到的最好方式来解决这个问题,不要用反对票来吓唬我哈哈。我真的不知道从这里到哪里去。

最佳答案

客观地说,您似乎在尝试在 HTML 元素中任意锚定的两点之间绘制许多连接线。在一个类似的项目中,我使用了这个配方:

  • 一个高度为几像素的透明 div,一个 2 像素宽的彩色底部边框,并带有 z-index,将其置于其他内容之上。
  • 关于要连接的点的一些三 Angular 数学,我从中计算距离和 Angular
  • 我将其用作 CSS div 宽度和旋转。我还让它不可选择,这样它就不会干扰其他元素的选择/点击。

结果是一个 div,一个底 Angular 位于 A 点,另一个底 Angular 位于 B 点,生成了一条非常漂亮的线,我可以完全控制它。

你需要做两次才能连接肘部。您可以在页面渲染或重绘等之后触发绘制。

下面代码片段中的工作示例 - 可能在全屏和 codepen 上运行得最好 http://codepen.io/JEE42/pen/aBOQGj如果你想玩。

/*
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>

关于javascript - 如何在div之间绘制响应线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39720603/

相关文章:

javascript - 如何使用谷歌共享按钮包含小图像

javascript - 如何操作现有 SVG 中元素的图层?

javascript - 如何在单击按钮时添加或附加文本框?

javascript - 如何使用 javascript 使按钮仅在用户使用 IE 浏览时隐藏

jquery - 列表项移动时向下滑动导航

css - 如何从中心点开始制作 svg 线的动画?

javascript - SVG Circle 元素在比例变换时跳跃

javascript - d3 - 如何显示持续时间间隔而不是tickValues

javascript - 如何有条件地更新 React 中集合中项目的属性?

javascript - 分离 JavaScript 代码的缺点?