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.

416 lines
13 KiB

/**
* 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 = "<div id=\"single\" onclick=\"clickTool(this, 1);\" title=\"绘制文字框\">文字框</div>"
+ "<div id=\"arrow1\" onclick=\"clickTool(this, 2);\" title=\"绘制箭头\">箭头</div>"
+ "<textarea id=\"myText\" hidden></textarea><input type=\"button\" id=\"okButton\" value=\"确定\" onclick=\"okTool();\" hidden/><input type=\"button\" id=\"cancelButton\" value=\"取消\" onclick=\"cancelTool();\" hidden/>";
}
//确定添加
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对象来处理数据
// 对于<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)
{
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;
}