You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

616 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 流程图绘制方法集
* 作者:刘广文
* 邮箱liugw@imut.edu.cn
* 版本20230813
* 功能基于canvas实现流程图的绘制
*/
var tool = 1;
var current = -1;
var flag=false;
var mouseX= 0;
var mouseY= 0;
var mouseX1= 0;
var mouseY1= 0;
var aryPoint = [];
var aryObject = [];
//加载工具
function loadTools(target){
target.innerHTML = "<div id=\"begin\" onclick=\"clickTool(this, 1);\" title=\"绘制开始框\">开始</div>"
+ "<div id=\"process\" onclick=\"clickTool(this, 2);\" title=\"绘制处理框\">处理</div>"
+ "<div id=\"arrow\" onclick=\"clickTool(this, 3);\" title=\"绘制直线连接箭头\"></div>"
+ "<div id=\"arrow1\" onclick=\"clickTool(this, 4);\" title=\"绘制折线连接箭头\"></div>"
+ "<div id=\"judge\" onclick=\"clickTool(this, 5);\" title=\"绘制判断框\">条件?</div>"
+ "<div id=\"end\" onclick=\"clickTool(this, 6);\" title=\"绘制结束框\">结束</div>"
+ "<div id=\"led\" onclick=\"clickTool(this, 7);\" title=\"绘制显示框\">显示</div>"
+ "<div id=\"link\" onclick=\"clickTool(this, 8);\" title=\"绘制连接框\">1</div>";
}
//鼠标抬起响应动作
function myMouseUp(e, page){
flag=false;
pushTool(tool, mouseX, mouseY, mouseX1, mouseY1, text.value, color.value, bcolor.value, size.value);
updateTools(cxt);
}
//鼠标移动响应动作
function myMouseMove(e, page){
mouseX1= e.pageX-page.offsetLeft;
mouseY1= e.pageY-page.offsetTop;
if(flag){
var aryPoint1 = [];
var i = 0;
while(i < aryPoint.length){
aryPoint1.push(aryPoint[i]);
i ++;
}
updateTools(cxt);
aryPoint = [];
i = 0;
while(i < aryPoint1.length){
aryPoint.push(aryPoint1[i]);
i ++;
}
if (tool == 4){
var s = aryPoint[aryPoint.length - 1];
ary = s.split(",");
var x = parseFloat(ary[0]), y = parseFloat(ary[1]);
if (Math.abs(x - mouseX1) > 10 || Math.abs(y - mouseY1) > 10)
aryPoint.push(mouseX1 + "," + mouseY1);
}
drawTool(cxt, tool, mouseX, mouseY, mouseX1, mouseY1, color.value, bcolor.value, size.value);
}
}
//鼠标按下响应动作
function myMouseDown(e, page)
{
mouseX= e.pageX-page.offsetLeft;
mouseY= e.pageY-page.offsetTop;
aryPoint = [];
aryPoint.push(mouseX + "," + mouseY);
flag=true;
}
//保存
function save(){
var ary = [];
var i = 0;
while(i <= current){
ary.push(aryObject[i]);
i ++;
}
aryObject = ary;
updateButton();
const fileName = 'flowchart.txt';
var str = '';
i = 0;
while(i < aryObject.length){
if (i == 0)
str = aryObject[i]
else{
str += "\r\n"
str += aryObject[i]
}
i ++;
}
const content = str
const blob = new Blob([content], {type: 'application/text'});
// 构造一个blob对象来处理数据
// 对于<a>标签,只有 Firefox 和 Chrome内核 支持 download 属性
// IE10以上支持blob但是依然不支持download
if ('download' in document.createElement('a'))
{
// 支持a标签download的浏览器
const link = document.createElement('a')
// 创建a标签
link.download = fileName
// a标签添加属性
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
document.body.appendChild(link)
link.click()
// 执行下载
URL.revokeObjectURL(link.href)
// 释放url
document.body.removeChild(link)
// 释放标签
} else {
// 其他浏览器
navigator.msSaveBlob(blob, fileName)
}
}
//撤销
function undo(){
if (current >= 0)
{
current --;
updateTools();
}
updateButton();
}
//刷新按钮状态
function updateButton(){
var undo = document.getElementById("undo");
var redo = document.getElementById("redo");
var save = document.getElementById("save");
if (current >= 0)
{
save.disabled = false;
undo.disabled = false;
}
else
{
undo.disabled = true;
save.disabled = true;
}
if (current < aryObject.length - 1)
{
redo.disabled = false;
}
else {
redo.disabled = true;
}
}
//重做
function redo(){
if (current < aryObject.length - 1)
{
current ++;
updateTools();
}
updateButton();
}
//选择工具
function clickTool(o, tag){
tool = tag;
for(var i = 0; i < tools.children.length; i ++){
tools.children[i].style.border = '1px solid black';
}
o.style.border = '2px solid red';
text.hidden = true;
text.value = "";
if (tag == 1)
{
text.hidden = false;
text.value = "开始";
}
if (tag == 2)
{
text.hidden = false;
text.value = "处理";
}
if (tag == 3 || tag == 4){
text.hidden = false;
text.value = "";
}
if (tag == 5)
{
text.hidden = false;
text.value = "条件";
}
if (tag == 6)
{
text.hidden = false;
text.value = "结束";
}
if (tag == 7)
{
text.hidden = false;
text.value = "显示信息";
}
if (tag == 8)
{
text.hidden = false;
text.value = "1";
}
}
//绘制所有工具
function updateTools(context){
canvas.width = canvas.width;
var i = 0;
while (i <= current){
var ary = aryObject[i].split('|');
var type = parseInt(ary[0]);
var ary1 = ary[1].split(',');
var x1 = parseFloat(ary1[0]);
var y1 = parseFloat (ary1[1]);
var x2 = parseFloat(ary1[2]);
var y2 = parseFloat(ary1[3]);
var size = parseFloat(ary[5]);
var color = ary[3];
var bcolor = ary[4];
if(type == 4){
x2 = parseFloat(ary1[ary1.length - 2]);
y2 = parseFloat(ary1[ary1.length - 1]);
aryPoint = [];
var j = 0;
while(j < ary1.length){
var x = parseFloat(ary1[j]), y = parseFloat(ary1[j + 1]);
aryPoint.push(x + "," + y);
j += 2;
}
}
drawTool(cxt, type, x1, y1, x2, y2, color, bcolor, size);
drawText(cxt, type, x1, y1, x2, y2, ary[2], color);
i ++;
}
}
//保存当前的绘制
function pushTool(type, x1, y1, x2, y2, text, color, bcolor, size){
var ary = [];
var i = 0;
while(i <= current){
ary.push(aryObject[i]);
i ++;
}
aryObject = ary;
var s = type + '|' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + '|' + text + '|' + color + '|' + bcolor + '|' + size;
if(type == 4){
s = type + '|';
i = 0;
while(i < aryPoint.length){
if (i > 0)
s += ",";
s += aryPoint[i];
i ++;
}
s = s + '|' + text + '|' + color + '|' + bcolor + '|' + size;
}
aryObject.push(s);
current ++;
updateButton();
}
//绘制文本
function drawText(context, type, x1, y1, x2, y2, text, color){
if (text == "" || context == null || type < 1) return;
switch(type){
case 1:
case 2:
case 5:
case 6:
case 7:
case 8:{
//绘制开始框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.font = "10pt SimSun";
//updateFont(context, w, h, text, 100);
var w1 = getFontWidth(context, text);
var h1 = getFontHeight(context, text);
context.fillStyle = color;
context.fillText(text, l + (w - w1) / 2, t + h1 / 4 + h / 2);
break;
}
case 3:{
//绘制箭头
//计算文字大小
context.font = "10pt SimSun";
var w = getFontWidth(context, text);
var h = getFontHeight(context, text);
//获得输出坐标
var x = (x2 + x1) / 2;
var y = (y2 + y1) / 2;
context.fillStyle = color;
context.fillText(text, x, y);
break;
}
case 4:{
//绘制箭头
//计算文字大小
context.font = "10pt SimSun";
var w = getFontWidth(context, text);
var h = getFontHeight(context, text);
//获得输出坐标
var px1 = x1, py1 = y1;
var i = 1;
var pp = 0;
while(i < aryPoint.length)
{
var ary = aryPoint[i].split(',');
var px = parseFloat(ary[0]), py = parseFloat(ary[1]);
var pp1 = 2;
if (Math.abs(px - px1) > Math.abs(py - py1)) pp1 = 1;
if (pp != 0 && pp1 != pp) break;
pp = pp1;
px1 = px;
py1 = py;
i ++;
}
var x = (px1 + x1) / 2;
var y = (py1 + y1) / 2;
context.fillStyle = color;
context.fillText(text, x, y);
break;
}
}
}
//绘制当前工具
function drawTool(context, type, x1, y1, x2, y2, color, bcolor, size){
if (context == null || type < 1) return;
switch(type){
case 1:{
//绘制开始框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.strokeStyle = color;
context.lineWidth = size;
context.beginPath();
context.moveTo(l, t + h / 2);
context.lineTo(l + w * 0.15, t);
context.lineTo(l + w - w * 0.15, t);
context.lineTo(l + w, t + h / 2);
context.lineTo(l + w - w * 0.15, t + h);
context.lineTo(l + w * 0.15, t + h);
context.lineTo(l, t + h / 2);
context.closePath();
context.fillStyle = bcolor;
context.fill();
context.stroke();
break;
}
case 2:{
//绘制处理框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.strokeStyle = color;
context.lineWidth = size;
context.beginPath();
context.moveTo(l, t);
context.lineTo(l + w, t);
context.lineTo(l + w, t + h);
context.lineTo(l, t + h);
context.lineTo(l, t);
context.closePath();
context.fillStyle = bcolor;
context.fill();
context.stroke();
break;
}
case 3:{
//绘制连接箭头
//计算箭头方向
var ll = 10, ss = 30;
var a = Math.atan2((y2 - y1), (x2 - x1));
var x3 = x2 - ll * Math.cos(a + ss * Math.PI/180);
var y3 = y2 - ll * Math.sin(a + ss * Math.PI/180);
var x4 = x2 - ll * Math.cos(a - ss * Math.PI/180);
var y4 = y2 - ll * Math.sin(a - ss * Math.PI/180);
context.strokeStyle = color;
context.lineWidth = size;
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x3, y3);
context.moveTo(x2, y2);
context.lineTo(x4, y4)
context.closePath();
context.stroke();
break;
}
case 4:{
//绘制折线连接箭头
if (aryPoint.length < 3)
break;
var ary = aryPoint[aryPoint.length - 2].split(',');
var px1 = parseFloat(ary[0]), py1 = parseFloat(ary[1]);
ary = aryPoint[aryPoint.length - 1].split(',');
var px2 = parseFloat(ary[0]), py2 = parseFloat(ary[1]);
if (Math.abs(px2 - px1) > Math.abs(py2 - py1))
py1 = py2;
else px1 = px2;
//计算箭头方向
var ll = 10, ss = 30;
var a = Math.atan2((py2 - py1), (px2 - px1));
var x3 = px2 - ll * Math.cos(a + ss * Math.PI/180);
var y3 = py2 - ll * Math.sin(a + ss * Math.PI/180);
var x4 = px2 - ll * Math.cos(a - ss * Math.PI/180);
var y4 = py2 - ll * Math.sin(a - ss * Math.PI/180);
context.strokeStyle = color;
context.lineWidth = size;
context.beginPath();
context.lineTo(px2, py2);
context.lineTo(x3, y3);
context.moveTo(px2, py2);
context.lineTo(x4, y4)
context.moveTo(px2, py2);
context.lineTo(px1, py1);
var i = aryPoint.length - 3;
while(i >= 0){
ary = aryPoint[i].split(',');
px2 = parseFloat(ary[0]), py2 = parseFloat(ary[1]);
if (Math.abs(px2 - px1) > Math.abs(py2 - py1))
py2 = py1;
else px2 = px1;
context.moveTo(px1, py1);
context.lineTo(px2, py2);
px1 = px2;
py1 = py2;
i --;
}
context.closePath();
context.stroke();
break;
}
case 5:{
//绘制判断框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.strokeStyle = color;
context.lineWidth = size;
context.beginPath();
context.moveTo(l, t + h / 2);
context.lineTo(l + w / 2, t);
context.lineTo(l + w, t + h / 2);
context.lineTo(l + w / 2, t + h);
context.lineTo(l, t + h / 2);
context.closePath();
context.fillStyle = bcolor;
context.fill();
context.stroke();
break;
}
case 6:{
//绘制结束框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.strokeStyle = color;
context.lineWidth = size;
var r = w * 0.4;
if (h < w)
r = h * 0.4;
context.beginPath();
context.moveTo(l + r, t);
context.arcTo(l + w, t, l + w, t + h, r);
context.arcTo(l + w, t + h, l, t + h, r);
context.arcTo(l, t + h, l, t, r);
context.arcTo(l, t, l + w, t, r);
context.closePath();
context.fillStyle = bcolor;
context.fill();
context.stroke();
break;
}
case 7:{
//绘制信息显示框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.strokeStyle = color;
context.lineWidth = size;
var r = w * 0.4;
if (h < w)
r = h * 0.4;
context.beginPath();
context.moveTo(l, t + h / 2);
context.lineTo(l + r, t);
context.lineTo(l + w - r, t);
context.arcTo(l + w, t, l + w, t + h, r);
context.arcTo(l + w, t + h, l, t + h, r);
context.lineTo(l + w - r, t + h);
context.lineTo(l + r, t + h);
context.lineTo(l, t + h / 2);
//context.arcTo(l, t + h, l, t, r);
//context.arcTo(l, t, l + w, t, r);
context.closePath();
context.fillStyle = bcolor;
context.fill();
context.stroke();
break;
}
case 8:{
//绘制连接框
//计算宽高
var l = x1, t = y1;
var w = x2 - x1, h = y2 - y1;
if (w < 0)
{
l = x2;
w = x1 - x2;
}
if (h < 0){
t = y2;
h = y1 - y2;
}
context.strokeStyle = color;
context.lineWidth = size;
context.beginPath();
context.arc(l + w / 2, t + h / 2, Math.min(w, h), 0, 360);
context.closePath();
context.fillStyle = bcolor;
context.fill();
context.stroke();
break;
}
}
}
//计算当前字体下的文本宽度
function getFontWidth(context, txt) {
const metrics = context.measureText(txt);
const actual = Math.abs(metrics.actualBoundingBoxLeft ?? 0) + Math.abs(metrics.actualBoundingBoxRight ?? 0);
const width = Math.max(metrics.width, actual);
return width;
}
//计算当前字体下的文本高度
function getFontHeight(context, txt) {
const metrics = context.measureText(txt);
const fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
//所有字在这个字体下的高度
//const actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
return fontHeight;
}
//自动调整字体大小
function updateFont(context, w, h, text, size){
context.font = size + "pt SimSun";
w1 = getFontWidth(context, text) * 2;
h1 = getFontHeight(context, text) * 2;
var s = size;
while (w1 > w || h1 > h){
s = s - 1;
context.font = s + "pt SimSun";
w1 = getFontWidth(context, text) * 2;
h1 = getFontHeight(context, text) * 2;
}
return s;
}