流程设计器里面,最复杂的其实不是拖来拖去的图片,而是线条的绘制包括锚点、箭头,如果把线条绘制好了,那设计器就没什么难度了。
绘制线条会涉及到一些数学知识,主要是三角函数、极坐标等,如果不记得了,赶紧恶补一下吧,这个是必须的。
线条绘制又分多种场景:
1 在一个节点和鼠标所在坐标之间绘制(寻找目标节点过程),这个线条要跟随鼠标的移动而不断重绘。线条的起点为节点中心点坐标与鼠标位置的连线再与节点边缘的交点(有点抽象,等会看图就明白)
可能很多人都以为是在拖动线条,其实这是一个假象,当鼠标移动时,会先擦除线条,然后重新绘制一条节点和鼠标当前位置的连线,当鼠标移动一段距离后,实际上重绘了无数次,这样线条的“拖动”就有连贯性,如果只在鼠标移动结束之后再绘制一条也可以,这样只需要重绘一次,但视觉上的效果要查很多。
2 在两个节点之间绘制,起点和终点坐标分别为两个节点中心点坐标之间的连线分别与节点边缘的交点。
3 拖动锚点(一条线条被分成几段时,子线条之间的交点)绘制,和第1点有些类似,稍微复杂点。
下面举第1种场景中节点在鼠标左上方的例子进行说明:
上图中x的坐标即为线条的起点
X坐标的计算过程:
X横坐标X.x3=O.x+len
根据相似三角形,存在关系 (x2-x1)/(len/2)=(y2-y1)/H
H=(len/2)(y2-y1)/(x2-x1)
X点的纵坐标X.y3=O.y+len/2+H
计算X坐标的代码如下(考虑了各种场景):
/**
* 获取起始点与图片边框的交点坐标
* 需按矩形对角线划分四个区域(此处为正方形,每90度为一个边界)
* acrossNode 参照节点对象(为上图中的节点对象)
*/
public static function getLinkPoint(fromPoint:Point,targetPoint:Point,acrossNode:BaseNode):Point
{
var angle:Number=getAngel(fromPoint,targetPoint);
/**计算交点与中心点的垂直和水平距离*/
var distanceX:Number=Math.abs((targetPoint.x-fromPoint.x)*(acrossNode.width/2)/(targetPoint.y-fromPoint.y));
var distanceY:Number=Math.abs((targetPoint.y-fromPoint.y)*(acrossNode.width/2)/(targetPoint.x-fromPoint.x));
if(targetPoint.x<fromPoint.x)
{
distanceX=-distanceX;
}
if(targetPoint.y<fromPoint.y)
{
distanceY=-distanceY;
}
/**最终xy坐标*/
var x:Number=0;
var y:Number=0;
if(angle>=45&&angle<135)
{
x=fromPoint.x+distanceX;
y=acrossNode.y;
}
else if(angle>=135&&angle<225)
{
x=acrossNode.x;
y=fromPoint.y+distanceY;
}
else if(angle>=225&&angle<315)
{
x=fromPoint.x+distanceX;
y=fromPoint.y+acrossNode.height/2;
}
else
{
x=fromPoint.x+acrossNode.width/2;
y=fromPoint.y+distanceY;
}
return new Point(x,y);
}
/**获取两点夹角的角度*/
private static function getAngel(fromPoint:Point,targetPoint:Point):Number
{
/**为了与三角坐标一致,y坐标的值要反过来*/
/**通过反正切计算弧度*/
var radian:Number=Math.abs(Math.atan((targetPoint.x-fromPoint.x)/(targetPoint.y-fromPoint.y)));
/**计算角度*/
var angle:Number=radian*180/Math.PI;
if((targetPoint.x>=fromPoint.x)&&(targetPoint.y<=fromPoint.y))//0~90区域
{
angle=90-angle;
}
else if((targetPoint.x<=fromPoint.x)&&(targetPoint.y<=fromPoint.y))//90~180区域
{
angle=90+angle;
}
else if((targetPoint.x<=fromPoint.x)&&(targetPoint.y>=fromPoint.y))//180~270区域
{
angle=270-angle;
}
else if((targetPoint.x>=fromPoint.x)&&(targetPoint.y>=fromPoint.y))//270~360区域
{
angle=270+angle;
}
return angle;
}
具体绘制线条还有一个技巧,线条绘制出来不仅是摆看的,还要加事件,可以被选取,太细了选择太困难,太粗了又太难看,一个很简单的办法就是绘制两条线,一条1个像素的实线,一条比较粗的透明线,透明线实际上是为了扩大选择区域。
graphics.clear();
graphics.moveTo(fromPoint.x,fromPoint.y);
graphics.lineStyle(8,color,0,true,LineScaleMode.NORMAL,CapsStyle.NONE);
graphics.lineTo(targetPoint.x,targetPoint.y);
graphics.moveTo(fromPoint.x,fromPoint.y);
graphics.lineStyle(DesignerConstances.LINE_THICKNESS,color);
graphics.lineTo(targetPoint.x,targetPoint.y);
绘制箭头
为了简化可绘制等边三角形
三角形被线条平分,先计算3个顶点的坐标,然后绘制3条线,最后填充三角形
平分后的弧度各为PI/6。
1)以下为A点坐标的计算过程:
正切值tan(a)=(y2-y1)/(x2-x1)
通过反正切得弧度a=atan(tan(a))
角AP2O的弧度θ为a+b=a+PI/6
H=len*Sin(θ)
W=len*Cos(θ)
A.x3=P2.x2-W,A.y3=P2.y2-H
2)同理,通过弧度c=PI/6-a可计算出B的坐标.
/**
* 根据线条起始点绘制三角形
*/
public function draw(ui:UIComponent,fromPoint:Point,toPoint:Point,color:uint):void
{
var vDistance:Number= toPoint.y-fromPoint.y;//起始点垂直距离
var sDistance:Number=Point.distance(fromPoint,toPoint);//起始点直线距离
var sinValue:Number=vDistance/sDistance;//起始点间的夹角的sin值
/**两点间直线与水平线的角度(弧度)*/
var radian:Number=Math.asin(sinValue);
/**用于计算三角形顶点与目标点水平距离的夹角(弧度)*/
var hRadian:Number=radian-Math.PI/6;
/**用于计算三角形顶点与目标点垂直距离的夹角(弧度)*/
var vRadian:Number=radian+Math.PI/6;
/**上顶点与目标点的垂直距离*/
var topYDis:Number=8*Math.sin(vRadian);
/**上顶点与目标点的水平距离*/
var topXDis:Number=8*Math.cos(vRadian);
/**下顶点与目标点的垂直距离*/
var botYDis:Number=8*Math.sin(hRadian);
/**下顶点与目标点的水平距离*/
var botXDis:Number=8*Math.cos(hRadian);
/**计算三角形上下顶点坐标*/
var topPointX:Number=toPoint.x-topXDis;
var topPointY:Number=toPoint.y-topYDis;
var botPointX:Number=toPoint.x-botXDis;
var botPointY:Number=toPoint.y-botYDis;
if(toPoint.x<fromPoint.x)
{
topPointX=toPoint.x+topXDis;
botPointX=toPoint.x+botXDis;
}
ui.graphics.beginFill(color);
ui.graphics.moveTo(topPointX,topPointY);
ui.graphics.lineTo(toPoint.x,toPoint.y);
ui.graphics.lineTo(botPointX,botPointY);
ui.graphics.lineTo(topPointX,topPointY);
}
绘制锚点
以下列出开始节点在结束节点左上方时,起始位置的锚点为例,所有锚点坐标通过极坐标换算(x = r*cos(θ) y = r*sin(θ),r为半径)
前置条件:矩形为正方形,边长为len,矩形被连线平分,即两边中心点与两个个交叉点重合
1) 计算锚点与连线的第二个交叉点X的坐标
以下为X点的坐标的计算过程
正切值 tan(a)= (y2-y1)/(x2-x1)
由反正切得弧度a=atan(tan(a))
由平行线得出 b=a
极坐标半径 r=len
极坐标弧度θ=2PI-b
X的极坐标X.x3 = r*cos(θ),X.y3 = r*sin(θ)
X的实际坐标为 (P1.x1+ X.x3,P1.y1+X.x3)
计算四个顶点值
以下为计算A点坐标过程:
正切值 tan(a)=(y2-y1)/(x2-x1)
由反正切得弧度a=atan(tan(a));
由平行线得出 b=a
极坐标弧度c=PI/4-b
极坐标半径r即线AO为 len*len/4 的平方根
极坐标A.x = r*cos(c),A.y = r*sin(c)
实际坐标为 (x0+A.x,y0-A.y)
/**
* 极坐标计算矩形四个顶点
* x = r*cos(θ) y = r*sin(θ)
*/
private function drawAnchor(holder:UIComponent,fromPoint:Point,targetPoint:Point):void
{
var tan:Number=(fromPoint.x-targetPoint.x)/(targetPoint.y-fromPoint.y);
var leftTopRadian:Number=Math.PI-(Math.PI/4-Math.atan(tan));//左上顶点弧度
var r:Number=getRadius();//半径
var centerPoint:Point=this.getCenterPoint(fromPoint,targetPoint);
/**计算四个顶点坐标*/
var leftTopPoint:Point=new Point(r*Math.cos(leftTopRadian)+centerPoint.x,r*Math.sin(leftTopRadian)+centerPoint.y);
var leftBottomPoint:Point=new Point(r*Math.cos(leftTopRadian+Math.PI/2)+centerPoint.x,r*Math.sin(leftTopRadian+Math.PI/2)+centerPoint.y);
var rightBottomPoint:Point=new Point(r*Math.cos(leftTopRadian+Math.PI)+centerPoint.x,r*Math.sin(leftTopRadian+Math.PI)+centerPoint.y);
var rightTopPoint:Point=new Point(r*Math.cos(leftTopRadian+3*Math.PI/2)+centerPoint.x,r*Math.sin(leftTopRadian+3*Math.PI/2)+centerPoint.y);
/**画线*/
with(holder)
{
graphics.lineStyle(1,DesignerConstances.COLOR_LINE_SELECTION);
graphics.beginFill(0xFFFFFF);
graphics.moveTo(leftTopPoint.x,leftTopPoint.y);
graphics.lineTo(leftBottomPoint.x,leftBottomPoint.y);
graphics.lineTo(rightBottomPoint.x,rightBottomPoint.y);
graphics.lineTo(rightTopPoint.x,rightTopPoint.y);
graphics.lineTo(leftTopPoint.x,leftTopPoint.y);
graphics.endFill();
}
}
/**极坐标半径*/
private function getRadius():Number
{
return Math.sqrt(Math.pow(DesignerConstances.ANCHOR_LENGTH/2,2));
}
/**连线中心点坐标 */
private function getCenterPoint(fromPoint:Point,targetPoint:Point):Point
{
return new Point((targetPoint.x+fromPoint.x)/2,(targetPoint.y+fromPoint.y)/2);
}
- 大小: 4.9 KB
- 大小: 5.9 KB
- 大小: 5.2 KB
- 大小: 6.1 KB
分享到:
相关推荐
基于Flex的Web流程设计器开发
flex强大、优美的界面流程图制作,这只是一个比较简单的画流程图程序,希望能帮助大家!可以在这个基础上构造属于自己的web流程图制作工具。
OrchestraDesigner是由北京航空航天大学计算机学院新技术研究所自主开发的一款基于Flex技术的在线协同工作流编辑工具。该建模工具针对非技术人员,采用一种比BPEL更面向业务、更直观的图元作为建模基础,生成的模型...
不错的流程设计器,实现了连线,拖动,修改属性等,,,
NULL 博文链接:https://2621380266.iteye.com/blog/1774767
flex 流程图 制作 flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex 流程图 制作flex ...
flex 流程设计器,flex 流程设计器
Flex项目 流程设计器 源码,代码完整,直接运行WorkFlowDesigner,简单,可以进行修改开发
流程设计,包含extjs下的流程设计,OrchestraDesigner-3.2-bin(基于Flex的web流程设计器),流程设计(设计器版),web工作流管理系统开发等
flex 实现 的流程设计器 实现在网页上构建一个工作流图
flex 开发的可视化流程工具, 最终生成XML文件可与各种后台结合, 生成的xml格式合乎jbpm流程定义规范 不是为了分我才不会拿出来
flex开发的表单设计器,可以完成界面的基本的操作和容器操作
flex 工作流设计器flex 工作流设计器
最近在研究flex画流程图收集的资料 该控件简单易用 含源码哟!!! 内含该项目的SVN地址 很值得推荐给大家 希望有需要的朋友下载
分别用ArcGIS Flex API和SuperMap Flex API实现的军标箭头库,有需要源码的可回复邮箱索取。
该编辑器支持流程文件的图形界面输入方式编辑, 不需考虑流程具体如何定义, 以拖拽节点的形式自动完成流程的编辑, 支持本地和服务器读写; 支持单个节点的拖拽和流程节点的整体拖拽;曲线绘制方式是以自动计算...
基于flex的精典的工作流图形设计器源码
flex css 设计器,flex css 设计器,flex css 设计器flex css 设计器,flex css 设计器,flex css 设计器flex css 设计器,flex css 设计器,flex css 设计器flex css 设计器,flex css 设计器,flex css 设计器
flex 绘制流程图 算法是 dagre.js 提供 可以实现拖拽绘制流程图
flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式flex设计模式