前台使用canvas根据后台传入的数据画图
session; java; jsp; jQuery; javascript; HTML5; canvas;
需求介绍:
后台传入节点个数,结点间的连线关系,节点名称等,前台模拟后台算法动态演示连线过程,最后根据后台传过来的连线
关系绘制最终的结点间连线结果。细节要求,根据后台传入节点个数绘制节点,节点圆形分布。
对HTML5 canvas的一点理解
在画的图像比较复杂的情况下,建议使用多层画布,比如我,就是每个功能模块都单独设置一层画布。还有一点,就是stroke()十分耗费资源,如果不是特别需要,最后统一调用stroke函数,不要每次都调用。
功能划分与具体代码
变量初始化模块
<html>
<script>
//接收aaa数据,并处理之
var aaa="${aaa}";
testSet=aaa.split("-");
var node_set="${node_set}";
node_name_set=node_set.split("-");//节点名集合
//alert(node_name_set);
//alert(node_name_set.length);
//alert(node_name_set[0]);
for(var i=0;i<testSet.length;i++)
for(var j=0;j<node_name_set.length;j++)
if(testSet[i]==node_name_set[j]) testSet[i]=j;
//接收bbb数据,并处理之
var bbb="${bbb}";
dirTestSet=bbb.split("-");
//alert(dirTestSet);
for(var i=0;i<dirTestSet.length;i++)
for(var j=0;j<=node_name_set.length;j++)
if(dirTestSet[i]==node_name_set[j]) dirTestSet[i]=j;
//建立最底层画布canvas0/ctx0
var canvas0 = document.getElementById("canvas");
var canvasWidth = canvas0.width;
var canvasHeight = canvas0.height;
var ctx0 = canvas0.getContext("2d");
//初始化中间变量及其他
var x = new Array();
var y = new Array();
var testSetLength = testSet.length-1;
var dirTestSetLength = dirTestSet.length-1;
var dgr=0;
var r=200;//设置圆弧半径
var n="${count}";//取出节点个数
var mx = 300; // 圆弧的中心点 x 坐标
var ny = 300; // 圆弧的中心点 y 坐标
</script>
</html>
节点初始化模块
<html>
<script>
//初始化节点的位置
function initNodes(x,y,n){
for(var i = 0; i < n;i++) {
x.push(0);
y.push(0);
}
}
//自动设定节点位置
function gyNodeSets(x,y,n){
var dgrInterval=Math.floor(360/n);//每两个节点之间增加的角度
while(n!=0){(Copyright All Rights Reserved)
dgr+=dgrInterval;//每次加完节点后增加度数
for(var i = 0; i < n; i++) {
x[i] = mx+Math.sin( dgr*Math.PI/180 ) * r;
y[i] = ny+Math.cos( dgr*Math.PI/180 ) * r;
}
n--;
}
}
//节点绘制
function drawNodes(x,y,n) {
for(var i = 0; i < n; i++) {
ctx0.beginPath();
ctx0.fillStyle = "#06a8f3";
ctx0.strokeStyle = "#000";
ctx0.lineWidth = 0.5;
ctx0.arc(x[i], y[i], 6, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
ctx0.fill();
ctx0.stroke();
ctx0.font = "normal 16px Arial";
ctx0.fillStyle="#000";
ctx0.textAlign="left";
ctx0.fillText(node_name_set[i],x[i],y[i]);
ctx0.closePath();
}
}
</script>
</html>
带箭头线条绘制模块
<html>
<script>
//canvas无箭头线标准函数,所以sgy我自己写一个函数,不过网上有不少参考,直接顶点坐标+角度+起点坐标+箭头短线长度就搞定了
function drawArrow(ctx, fromX, fromY, toX, toY,theta,headlen,width,color){
theta = typeof(theta) != 'undefined' ? theta : 30;
headlen = typeof(theta) != 'undefined' ? headlen : 10;
width = typeof(width) != 'undefined' ? width : 1;
color = typeof(color) != 'color' ? color : '#000'; // 计算各角度和对应的P2,P3坐标
var angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI,
angle1 = (angle + theta) * Math.PI / 180,
angle2 = (angle - theta) * Math.PI / 180,
topX = headlen * Math.cos(angle1),
topY = headlen * Math.sin(angle1),
botX = headlen * Math.cos(angle2),
botY = headlen * Math.sin(angle2);
ctx.save();
ctx.beginPath();
var arrowX = fromX - topX,
arrowY = fromY - topY;
ctx.moveTo(arrowX, arrowY);
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
arrowX = toX + topX;
arrowY = toY + topY;
ctx.moveTo(arrowX, arrowY);
ctx.lineTo(toX, toY);
arrowX = toX + botX;
arrowY = toY + botY;
ctx.lineTo(arrowX, arrowY);
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.stroke();
ctx.restore();
}
</script>
</html>
动态画图演示模块
<html>
<script>
//应用技术为setInterval,定时执行代码,结束条件自动为到达数组末尾。
//javascript的定时器与循环有难以解释的逻辑冲突,或者说是sgy我暂时没深入想的编写失误,反正每次把定时器和循环写在一起必然得不出想要实现的结果。后面我会再写一篇博客来详细展示javascript中的两个定时器的用法与区别。
setInterval("dynamicDrawLines_1(x,y,n)",300);
var i1=1;
function dynamicDrawLines_1(x,y,n){
var itvl=1;(Copyright All Rights Reserved)
var canvas1 = document.getElementById("canvas");
var ctx1 = canvas1.getContext("2d");
ctx1.beginPath();
ctx1.strokeStyle="#FFA500";//orange
ctx1.moveTo(x[i1],y[i1]);
ctx1.lineTo(x[i1+itvl],y[i1+itvl]);
ctx1.stroke();
ctx1.closePath();
itvl++;
i1++;
}
setInterval("dynamicDrawLines_2(x,y,n)",300);
var i2=1;
function dynamicDrawLines_2(x,y,n){
var itvl=1;
var canvas2 = document.getElementById("canvas");
var ctx2 = canvas2.getContext("2d");
ctx2.beginPath();
ctx2.strokeStyle="#00FF00";//green
ctx2.moveTo(x[i2],y[i2]);
//ctx2.lineTo(x[i2+2],y[i2+2]);
ctx2.lineTo(x[i2+itvl+1],y[i2+itvl+1]);
ctx2.stroke();
ctx2.closePath();
itvl++;
i2++;
}
</script>
</html>
清理画布模块
<html>
<script>
//应用到的技术是xor
//多次调用setTimeout函数实现伪动态清理
setTimeout("xorLines(x,y,n)",n*500);
setTimeout("xorLines(x,y,n)",n*500+500);
function xorLines(x,y,n){ (Copyright All Rights Reserved)
var canvas9 = document.getElementById("canvas");
var ctx9 = canvas9.getContext("2d");
for(var i = 2;i<n;i++){
for(var j=i+1;j<=n;j++){
ctx9.beginPath();
ctx9.strokeStyle="#FFFFFF";
ctx9.globalCompositeOperation="xor";
ctx9.moveTo(x[i],y[i]);
ctx9.lineTo(x[j],y[j]);
ctx9.stroke();
ctx9.closePath();
}
}
}
</script>
</html>
最终结果之无向图模块
<html>
<script>
function drawUndirResult(x,y,testSet){
var canvas10 = document.getElementById("canvas");
var ctx10 = canvas10.getContext("2d");
for(var i = 0;i<testSetLength/2;i++){
ctx10.beginPath();(Copyright All Rights Reserved)
ctx10.strokeStyle="#00008B";
ctx10.lineWidth="1.5";
var tmp_x = testSet[i]
//alert(testSet[i]);
//alert("tmp_x="+tmp_x);
var tmp_y = testSet[(i+(testSetLength/2))]
//alert("tmp_y="+tmp_y);
// ctx10.moveTo(x[parseInt(testSet[i])],y[parseInt(testSet[i])]);
// ctx10.lineTo(x[testSet[parseInt(i+(testSet.length/2))]],y[testSet[parseInt(i+(testSet.length/2))]]);
ctx10.moveTo(x[tmp_x],y[tmp_x]);
ctx10.lineTo(x[tmp_y],y[tmp_y]);
ctx10.stroke();
ctx10.closePath();
}
}
</script>
</html>
最终结果之有向图模块
<html>
<script>
function drawDirResult(x,y,dirTestSet){
var canvas11 = document.getElementById("canvas");
var ctx11 = canvas11.getContext("2d");
for(var i = 0;i<dirTestSetLength/2;i++){ (Copyright All Rights Reserved)
ctx11.beginPath();
ctx11.strokeStyle="#000";
ctx11.lineWidth="1.5";
var tmp_x = dirTestSet[i]
//alert("tmp_x="+tmp_x);
var tmp_y = dirTestSet[(i+(dirTestSetLength/2))]
drawArrow(ctx11, x[tmp_x],y[tmp_x],x[tmp_y],y[tmp_y],15,15,2,'#f36');
ctx11.closePath();
}
}
</script>
</html>
最终的效果demo,gif图片的形式呈现:(Copyright All Rights Reserved)