有个需求就是页面上随机生成几个点,然后可以用手把这几个点通过手势连起来。这个现在没有思路,谁做过,提供一下思路。。。
来个常规方法。
使用 canvas 绘图(其实也不一定,你可以继续使用 div ,不过没啥必要而已。
很显然实现思路是捕捉用户按下的起始点和结束点,符合一定范围即可视为从起始到结束完成了一次连线。
符合这个条件,就可以根据预设的两个点,调用 ctx 来画一根直线,实现连线。
我个人有日子没做了,以前那时候用 touch.js (需要配合 zepto)就可以实现。
没做过,不说怎么画线(svg,canvas都行),主要说下怎么确定两点,提供两个思路:
方案一:
1.原生JS监听mousedown
事件,当鼠标按下,拿到Event
事件对象,判断点击的位置是否在某个点的范围内,比如第一个点坐标是(x, y)px
,那么可能鼠标点击到(x±5, y±5)px
都是代表点击到了这个点。这里就确定了第一个点。
2.监听mouseup
事件,鼠标松开的时候,认为是第二个点的位置,同样拿到Event
对象,判断点击位置。确定第二个点的位置
3.连线即可
方案二:
如果说预设点的位置不知道x,y
,那你可以直接通过点击目标的class
类名之类的标识去判断也是可以的,但是这样可能就无法做到一定区域范围的容错。如果想要做到区域范围的容错,还需要在DOM结构上下功夫。
把方块看成是点,把线条变细,就是放几个点连线了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Work Flow Demo</title>
<style type="text/css">
.clear-fix:after {content:'\200B';display:block;height:0;clear:both;}
.toolbar {font-size:24px;background-color:#F5F5F5;border:1px solid #CCC;}
.toolbar .item {float:left;margin:0.5em 0.25em;width:1.5em;height:1.5em;line-height:1.5em;text-align:center;border:1px solid #CCC;background-color:#FFF;box-sizing:border-box;}
.workspace {position:relative;height:500px;font-size:24px;border:1px solid #CCC;border-top:0 none transparent;background-color:#F5F5F5;
}
.workspace .item {position:absolute;width:1.5em;height:1.5em;line-height:1.5em;text-align:center;border:1px solid #CCC;background-color:#FFF;}
.workspace .item:after {content:attr(data-id);display:block;font-size:12px;height:1em;line-height:1.2em;}
.line {position:absolute;height:2px;
background-image: -moz-linear-gradient(0deg, #FFF 25%, #000 25%, #000 50%, #FFF 50%, #FFF 75%, #000 75%, #000);background-size:40px 40px;box-shadow:0 0 2px 0 #000;
-webkit-transform-origin:left center;
-moz-transform-origin:left center;
-ms-transform-origin:left center;
-o-transform-origin:left center;
transform-origin:left center;
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-animation: flow 2s linear infinite;
-moz-animation: flow 2s linear infinite;
-o-animation: flow 2s linear infinite;
animation: flow 2s linear infinite;
}
/*.line:after {position:absolute;content:'\200B';width:0;height:0;top:50%;right:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-o-transform: translateY(-50%);
transform: translateY(-50%);;
}/**/
@keyframes flow {
0% {background-position-x:0;}
100% {background-position-x:39px;}
}
</style>
<script>
window.addEventListener('load', function(){
var instanceId = 1;
var domWorkspace = document.querySelector('.workspace');
function line_by_id($itemId)
{
return {
from : Array.prototype.slice.call(document.querySelectorAll('[data-id-from="'+$itemId+'"]'))
,to : Array.prototype.slice.call(document.querySelectorAll('[data-id-to="'+$itemId+'"]'))
};
}
function line_get($fromId, $toId)
{
var selector = '[data-id-from="'+$fromId+'"][data-id-to="'+$toId+'"]';
var dom = document.querySelector(selector);
if(dom instanceof HTMLElement == false)
{
dom = document.createElement('DIV');
dom.className = 'line';
dom.setAttribute('data-id-from', $fromId);
dom.setAttribute('data-id-to', $toId);
domWorkspace.appendChild(dom);
}
return dom;
}
function line_update($domLine, $domBase)
{
var fromId = $domLine.getAttribute('data-id-from');
var toId = $domLine.getAttribute('data-id-to');
var domFrom = $domBase.querySelector('.item[data-id="'+fromId+'"]');
var domTo = $domBase.querySelector('.item[data-id="'+toId+'"]');
var rectBase = domWorkspace.getBoundingClientRect();
var rectFrom = domFrom.getBoundingClientRect();
var rectTo = domTo.getBoundingClientRect();
var sx, sy, ex, ey;
if(rectFrom.right < rectTo.left)
{
sx = rectFrom.right - rectBase.left;
sy = rectFrom.top + Math.floor(rectFrom.height/2) - rectBase.top;
ex = rectTo.left - rectBase.left;
ey = rectTo.top + Math.floor(rectTo.height/2) - rectBase.top;
}
else if(rectFrom.left > rectTo.right)
{
sx = rectFrom.left - rectBase.left;
sy = rectFrom.top + Math.floor(rectFrom.height/2) - rectBase.top;
ex = rectTo.right - rectBase.left;
ey = rectTo.top + Math.floor(rectTo.height/2) - rectBase.top;
}
else
{
sx = rectFrom.right - rectBase.left;
sy = rectFrom.top + Math.floor(rectFrom.height/2) - rectBase.top;
ex = rectTo.left - rectBase.left;
ey = rectTo.top + Math.floor(rectTo.height/2) - rectBase.top;
}
var deg = Math.atan2((ey-sy),(ex-sx))*180/Math.PI;
$domLine.style.top = sy + 'px';
$domLine.style.left = sx + 'px';
$domLine.style.width = Math.sqrt( Math.pow(ex-sx, 2) + Math.pow(ey-sy, 2) ) + 'px';
$domLine.style.transform = 'rotate('+deg+'deg)';
}
var domDoc = document.querySelector('.toolbar .item[data-type="doc"]');
var domGear = document.querySelector('.toolbar .item[data-type="gear"]');
domDoc.addEventListener('dragstart', function($evt){
$evt.dataTransfer.effectAllowed = 'copy';
$evt.dataTransfer.setData('text/plain', 'doc');
var json = {mx:$evt.offsetX||$evt.layerX,my:$evt.offsetY||$evt.layerY};
$evt.dataTransfer.setData('text/json', JSON.stringify(json));
}, false);
domGear.addEventListener('dragstart', function($evt){
$evt.dataTransfer.effectAllowed = 'copy';
$evt.dataTransfer.setData('text/plain', 'gear');
var json = {mx:$evt.offsetX||$evt.layerX,my:$evt.offsetY||$evt.layerY};
$evt.dataTransfer.setData('text/json', JSON.stringify(json));
}, false);
domWorkspace.addEventListener('dragenter', function($evt){
}, false);
domWorkspace.addEventListener('dragleave', function($evt){
}, false);
domWorkspace.addEventListener('dragover', function($evt){
$evt.preventDefault();
}, false);
domWorkspace.addEventListener('drop', function($evt){
$evt.preventDefault();
var rect = domWorkspace.getBoundingClientRect();
var type = $evt.dataTransfer.getData('text/plain');
var json = JSON.parse($evt.dataTransfer.getData('text/json'));
var dom;
if($evt.dataTransfer.effectAllowed == 'copy')
{
switch(type)
{
case 'doc':
dom = domDoc.cloneNode(true);
dom.setAttribute('data-id', (instanceId++).toString());
dom.style.top = ($evt.pageY - rect.top - json.my) + 'px';
dom.style.left = ($evt.pageX - rect.left - json.mx) + 'px';
domWorkspace.appendChild(dom);
dom.addEventListener('dragstart', function($evt){
$evt.dataTransfer.effectAllowed = 'move';
$evt.dataTransfer.setData('text/plain', 'doc');
var json = {id:this.getAttribute('data-id'),mx:$evt.offsetX||$evt.layerX,my:$evt.offsetY||$evt.layerY};
$evt.dataTransfer.setData('text/json', JSON.stringify(json));
});
dom.addEventListener('drop', function($evt) {
$evt.preventDefault();
var json = JSON.parse($evt.dataTransfer.getData('text/json'));
var fromId = json.id;
var toId = this.getAttribute('data-id');
if(fromId == toId)
return;
var domLine = line_get(fromId, toId);
line_update(domLine, domWorkspace);
});
break;
case 'gear':
dom = domGear.cloneNode(true);
dom.setAttribute('data-id', (instanceId++).toString());
dom.style.top = ($evt.pageY - rect.top - json.my) + 'px';
dom.style.left = ($evt.pageX - rect.left - json.mx) + 'px';
domWorkspace.appendChild(dom);
dom.addEventListener('dragstart', function($evt){
$evt.dataTransfer.effectAllowed = 'move';
$evt.dataTransfer.setData('text/plain', 'gear');
var json = {id:this.getAttribute('data-id'),mx:$evt.offsetX||$evt.layerX,my:$evt.offsetY||$evt.layerY};
$evt.dataTransfer.setData('text/json', JSON.stringify(json));
});
dom.addEventListener('drop', function($evt) {
$evt.preventDefault();
var json = JSON.parse($evt.dataTransfer.getData('text/json'));
var fromId = json.id;
var toId = this.getAttribute('data-id');
if(fromId == toId)
return;
var domLine = line_get(fromId, toId);
line_update(domLine, domWorkspace);
});
break;
}
}
else if($evt.dataTransfer.effectAllowed == 'move')
{
if($evt.target.classList.contains('workspace') == true)
{
var itemId = json.id;
dom = domWorkspace.querySelector('.item[data-id="'+itemId+'"]');
if(dom instanceof HTMLElement)
{
dom.style.top = ($evt.pageY - rect.top - json.my) + 'px';
dom.style.left = ($evt.pageX - rect.left - json.mx) + 'px';
var i;
var map = line_by_id(itemId);
if(Array.isArray(map.from) == true)
{
for(i=0; i<map.from.length; i++)
{
line_update(map.from[i], domWorkspace);
}
}
if(Array.isArray(map.to) == true)
{
for(i=0; i<map.to.length; i++)
{
line_update(map.to[i], domWorkspace);
}
}
}
}
}
}, false);
});
</script>
</head>
<body>
<div class="toolbar clear-fix">
<div class="item" data-type="doc" draggable="true">🗎</div>
<div class="item" data-type="gear" draggable="true">⚙</div>
</div>
<div class="workspace">
</div>
</body>
</html>
10 回答11.1k 阅读
6 回答3k 阅读
5 回答4.8k 阅读✓ 已解决
4 回答3.1k 阅读✓ 已解决
2 回答2.7k 阅读✓ 已解决
3 回答3.2k 阅读✓ 已解决
3 回答1.4k 阅读✓ 已解决
写了个demo供你参考