diff --git a/ipograph.js b/ipograph.js new file mode 100644 index 0000000..126851d --- /dev/null +++ b/ipograph.js @@ -0,0 +1,416 @@ +/** + * IPO图绘制方法集 + * 作者:刘广文 + * 邮箱:liugw@imut.edu.cn + * 版本:20230813 + * 功能:基于canvas实现IPO图的绘制 + */ +var tool = 1; +var current = -1; +var flag=false; +var mouseX= 0; +var mouseY= 0; +var mouseX1= 0; +var mouseY1= 0; +var aryObject = []; +var drawn = false; +var myText = null, okButton = null, cancelButton = null; +var w = 0, h = 0, x = 0, y = 0, pLeft = 0, pTop = 0; + +//加载工具 +function loadTools(target){ + target.innerHTML = "
文字框
" + + "
箭头
" + + ""; +} +//确定添加 +function okTool(){ + pushTool(tool, x, y, x + w, y + h, myText.value, color.value, bcolor.value, size.value, fcolor.value); + cancelTool(); + updateTools(cxt, tool, x, y, x + w, y + h, color.value, bcolor.value, size.value); +} +//取消添加 +function cancelTool(){ + if (myText != null) + { + myText.hidden = true; + okButton.hidden = true; + cancelButton.hidden = true; + } + drawn = false; +} +//鼠标抬起响应动作 +function myMouseUp(e, page){ + flag=false; + if (tool == 1 && !drawn) + { + // + drawn = true; + if (myText == null) + { + myText = document.getElementById("myText"); + okButton = document.getElementById("okButton"); + cancelButton = document.getElementById("cancelButton"); + } + myText.hidden = false; + okButton.hidden = false; + cancelButton.hidden = false; + x = mouseX; + y = mouseY; + if (mouseX1 < mouseX) + x = mouseX1; + if (mouseY1 < mouseY) + y = mouseY1; + w = Math.abs(mouseX - mouseX1); + h = Math.abs(mouseY - mouseY1); + var rect = canvas.getBoundingClientRect(); + pLeft = rect.left; + pTop = rect.top; + myText.style.left = (x + pLeft) + "px"; + myText.style.top = (y + pTop) + "px"; + myText.style.width = w + "px"; + myText.style.height = h + "px"; + cancelButton.style.left = (x + pLeft + w - 50) + "px"; + cancelButton.style.top = (y + pTop + h + 5) + "px"; + okButton.style.left = (x + pLeft + w - 110) + "px"; + okButton.style.top = (y + pTop + h + 5) + "px"; + textWidth = w; + textHeight = h; + myText.addEventListener("mousemove", function(){ + if(myText.clientWidth != w || myText.clientHeight != h){ + w = myText.clientWidth; + h = myText.clientHeight; + cancelButton.style.left = (x + pLeft + w - 50) + "px"; + cancelButton.style.top = (y + pTop + h + 5) + "px"; + okButton.style.left = (x + pLeft + w - 110) + "px"; + okButton.style.top = (y + pTop + h + 5) + "px"; + } + }); + myText.focus(); + } + else if (tool == 2){ + pushTool(tool, mouseX, mouseY, mouseX1, mouseY1, text.value, color.value, bcolor.value, size.value, fcolor.value); + } + updateTools(cxt); +} +//鼠标移动响应动作 +function myMouseMove(e, page){ + mouseX1= e.pageX-page.offsetLeft; + mouseY1= e.pageY-page.offsetTop; + if(flag){ + updateTools(cxt); + if (tool == 2 || !drawn) + drawTool(cxt, tool, mouseX, mouseY, mouseX1, mouseY1, "#000000", "#FFFFFF", size.value); + } +} + +//鼠标按下响应动作 +function myMouseDown(e, page) +{ + mouseX= e.pageX-page.offsetLeft; + mouseY= e.pageY-page.offsetTop; + flag=true; +} +//保存 +function save(){ + var ary = []; + var i = 0; + while(i <= current){ + ary.push(aryObject[i]); + i ++; + } + aryObject = ary; + updateButton(); + const fileName = 'ipograph.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对象来处理数据 + // 对于标签,只有 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) + { + var ary = aryObject[current].split('|'); + if (parseInt(ary[0]) == 2){ + current --; + updateTools(); + updateButton(); + return; + } + while(current > 0) + { + var ary = aryObject[current].split('|'); + if (parseInt(ary[0]) < 3) + break; + current --; + } + 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 (aryObject.length > 0 && current < aryObject.length - 1) + { + current ++; + var ary = aryObject[current].split('|'); + if (parseInt(ary[0]) == 2 || current == aryObject.length - 1) + { + updateTools(); + updateButton(); + return; + } + current ++; + while(current < aryObject.length) + { + ary = aryObject[current].split('|'); + if (parseInt(ary[0]) < 3) + { + current --; + break + } + if (current == aryObject.length - 1) break; + current ++; + } + updateTools(); + } + updateButton(); +} + +//选择工具 +function clickTool(o, tag){ + cancelTool(); + myText = null; + 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; +} + +//绘制所有工具 +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]; + var fcolor = ary[6]; + if(type == 1){ + drawTool(cxt, type, x1, y1, x2, y2, color, bcolor, size); + i ++; + var yy = y1; + while(i <= current){ + ary = aryObject[i].split('|'); + type = parseInt(ary[0]); + if (type > 2) + { + drawText(cxt, type, x1, yy, x2, y2, ary[2], fcolor); + yy += getFontHeight(cxt, ary[2]); + } + else break; + i ++; + } + } + else if(type == 2){ + drawTool(cxt, type, x1, y1, x2, y2, color, bcolor, size); + i ++; + } + } +} +//保存当前的绘制 +function pushTool(type, x1, y1, x2, y2, text, color, bcolor, size, fcolor){ + var ary = []; + var i = 0; + while(i <= current){ + ary.push(aryObject[i]); + i ++; + } + aryObject = ary; + if(type == 1) + { + var s = type + '|' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + '||' + color + '|' + bcolor + '|' + size + '|' + fcolor; + aryObject.push(s); + current ++; + ary = text.split("\n"); + i = 0; + while(i < ary.length){ + s = type + '0||' + ary[i]; + aryObject.push(s); + i ++; + current++; + } + } + if(type == 2) + { + var s = type + '|' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + '||' + color + '|' + bcolor + '|' + size + '|' + fcolor; + aryObject.push(s); + current ++; + } + updateButton(); +} +//绘制文本 +function drawText(context, type, x1, y1, x2, y2, text, color){ + if (text == "" || context == null) return; + context.fillStyle = color; + context.fillText(text, x1, y1 + 10); +} +//绘制当前工具 +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); + 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(); + if (type == 2) + text.value = Math.trunc(h / getFontHeight(aryTitle[0])); + break; + } + case 2:{ + //绘制连接箭头 + //计算箭头方向 + 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; + } + } +} + +//计算当前字体下的文本宽度 +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) { + var fontHeight = 20; + try{ + const metrics = context.measureText(txt); + fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; + } + catch{ + fontHeight = 20; + } + //所有字在这个字体下的高度 + //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; +} \ No newline at end of file