关于Raphaël
Raphaël是一个在网页上绘图的js类库,非常小压缩版只有89k左右
官方宣称兼容各种主流浏览器,据笔者测试在IE6下尚有一些问题(不过这些与本文无关)
他是使用js来创建vml或svg来绘图的
缘起
项目中不能使用Silverlight或者flash来解决绘图和拖动的问题
而且为了项目效果较好,要求拖动的时候箭头能动态改变起点和重点,别且箭头要改变方向
所以只能考虑JS了
效果
演示
http://www.mrlh.net/flowchart/demo.htm[已经不能访问了]
源码
引用
这两个东西是不相干的,引用先后顺序也无所谓
页面加载完成后的代码
$(function () {//用来存储节点的顺序var connections &#61; [];//拖动节点开始时的事件var dragger &#61; function () {this.ox &#61; this.attr("x");this.oy &#61; this.attr("y");this.animate({ "fill-opacity": .2 }, 500);};//拖动事件var move &#61; function (dx, dy) {var att &#61; { x: this.ox &#43; dx, y: this.oy &#43; dy };this.attr(att);$("#test" &#43; this.id).offset({ top: this.oy &#43; dy &#43; 10, left: this.ox &#43; dx &#43; 10 });for (var i &#61; connections.length; i--; ) {r.drawArr(connections[i]);}};//拖动结束后的事件var up &#61; function () {this.animate({ "fill-opacity": 0 }, 500);};//创建绘图对象var r &#61; Raphael("holder", $(window).width(), $(window).height());//绘制节点var shapes &#61; [r.rect(190, 100, 60, 40, 4),r.rect(290, 80, 60, 40, 4),r.rect(290, 180, 60, 40, 4),r.rect(450, 100, 60, 40, 4)];//定位节点上的文字$("#test1").offset({ top: 100 &#43; 10, left: 190 &#43; 10 });$("#test2").offset({ top: 80 &#43; 10, left: 290 &#43; 10 });$("#test3").offset({ top: 180 &#43; 10, left: 290 &#43; 10 });$("#test4").offset({ top: 100 &#43; 10, left: 450 &#43; 10 });//为节点添加样式和事件&#xff0c;并且绘制节点之间的箭头for (var i &#61; 0, ii &#61; shapes.length; i 这些代码注释比较详细&#xff0c;就不多说了 在这些代码中涉及到操作的界面元素HTML代码如下 其中关键元素的样式如下 #holder{top: 0px;left: 0px;right: 0px;bottom: 0px;position: absolute;z-index: 999;}test{position: absolute;width: 80px;height: 30px;top: 0px;z-index: 0;} 在拖动事件中&#xff0c;动态改变了节点文本元素的位置 并且重绘了节点和箭头 drawArr是一个自定义方法&#xff0c;负责修改箭头的方向&#xff0c;代码如下 //随着节点位置的改变动态改变箭头Raphael.fn.drawArr &#61; function (obj) {var point &#61; getStartEnd(obj.obj1, obj.obj2);var path1 &#61; getArr(point.start.x, point.start.y, point.end.x, point.end.y, 8);if (obj.arrPath) {obj.arrPath.attr({ path: path1 });} else {obj.arrPath &#61; this.path(path1);}return obj;}; 首先需要确定箭头的起始位置&#xff0c; point包含两个点&#xff0c; point.start为起点, point.end为终点&#xff0c; 然后需要确定箭头的绘图路径 一个箭头包含三个线段&#xff0c;四个点 1:起点&#xff0c;2:终点&#xff0c;3:箭头终点1&#xff0c;4:箭头终点2 在此函数中&#xff0c;判断如果箭头已经被绘制过&#xff0c; 只要修改属性即可 如果没有被绘制过&#xff0c;则需要重新绘制 下面来看一下动态确定起点和终点的代码 function getStartEnd(obj1, obj2) {var bb1 &#61; obj1.getBBox(),bb2 &#61; obj2.getBBox();var p &#61; [{ x: bb1.x &#43; bb1.width / 2, y: bb1.y - 1 },{ x: bb1.x &#43; bb1.width / 2, y: bb1.y &#43; bb1.height &#43; 1 },{ x: bb1.x - 1, y: bb1.y &#43; bb1.height / 2 },{ x: bb1.x &#43; bb1.width &#43; 1, y: bb1.y &#43; bb1.height / 2 },{ x: bb2.x &#43; bb2.width / 2, y: bb2.y - 1 },{ x: bb2.x &#43; bb2.width / 2, y: bb2.y &#43; bb2.height &#43; 1 },{ x: bb2.x - 1, y: bb2.y &#43; bb2.height / 2 },{ x: bb2.x &#43; bb2.width &#43; 1, y: bb2.y &#43; bb2.height / 2 }];var d &#61; {}, dis &#61; [];for (var i &#61; 0; i <4; i&#43;&#43;) {for (var j &#61; 4; j <8; j&#43;&#43;) {var dx &#61; Math.abs(p[i].x - p[j].x),dy &#61; Math.abs(p[i].y - p[j].y);if ((i &#61;&#61; j - 4) ||(((i !&#61; 3 && j !&#61; 6) || p[i].x p[j].x) &&((i !&#61; 0 && j !&#61; 5) || p[i].y > p[j].y) &&((i !&#61; 1 && j !&#61; 4) || p[i].y 这段代码来自Raphael官方demo 不是我写的 也一时半会说不清楚&#xff0c; 大家还是自己去研究吧 确定箭头路径的代码如下 //获取组成箭头的三条线段的路径function getArr(x1, y1, x2, y2, size) {var angle &#61; Raphael.angle(x1, y1, x2, y2);//得到两点之间的角度var a45 &#61; Raphael.rad(angle - 45);//角度转换成弧度var a45m &#61; Raphael.rad(angle &#43; 45);var x2a &#61; x2 &#43; Math.cos(a45) * size;var y2a &#61; y2 &#43; Math.sin(a45) * size;var x2b &#61; x2 &#43; Math.cos(a45m) * size;var y2b &#61; y2 &#43; Math.sin(a45m) * size;var result &#61; ["M", x1, y1, "L", x2, y2, "L", x2a, y2a, "M", x2, y2, "L", x2b, y2b];return result;} 此函数把箭头路径作为数组反馈给调用函数 数组中 M表示画笔起点移动到此点 L表示从某点绘制到某点&#xff0c;绘制直线 以上函数反馈结果的意思是&#xff1a; 画笔从&#xff08;x1,y1&#xff09;开始绘制直线到(x2,y2),然后从(x2,y2)绘制直线到(x2a,y2a)然后画笔移动到(x2,y2)然后从(x2,y2)绘制直线到(x2b,y2b) 在确定这几个点的过程中 用到了一些数学知识&#xff0c;具体原理也不多说了 喜欢的朋友请点支持&#xff01;谢谢大家&#xff01;