繁体   English   中英

在 Canvas、JavaScript 上关闭窗口

[英]Closing windows on Canvas, JavaScript

我试图想出一个解决这个问题的方法。 我需要检测鼠标点击小红色矩形以关闭窗口。 只有当没有其他窗口“在它上面”时,点击的窗口才应该关闭。

我想检测红色的点击,但这并不能很好地工作。

附注。 我不能使用弹出窗口,他们需要这样做。

有人可以帮我弄这个吗? 谢谢!

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"></script> </head> <body> <input type="radio" name="choice" id="open"> Open new window: Height = <input type="text" id="txt_height">, width = <input type="text" id="txt_width"> <br> <input type="radio" name="choice" id="close"> Closing window <br> <br> <canvas id="canvas" width="600" height="600" style="border: 1px solid black"></canvas> <br> <br> Currently, there are <span id="details_1"></span> windows open. <br> <span id="details_2"></span> <script> var A = 0; var i = 0; $("#details_1").text(i); $('input[type=radio]').click(function(e) { var value = $(this).val(); if(this.id === 'open') A=1; if(this.id === 'close') A=2; }); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext("2d"); function mouse_position(canvas, event){ const rect = canvas.getBoundingClientRect(); const x = Math.floor(event.clientX - rect.left); const y = Math.floor(event.clientY - rect.top); if ( A === 1 ) open(x,y); if ( A === 2 ) close(); } $('#canvas').click(function(e){ mouse_position(canvas, event);}) function open(x,y){ i++; var width = $("#txt_width").val(); var height = $("#txt_height").val(); var new_x = x + (width - 30); if( !width.match(/^\\d+$/) || !height.match(/^\\d+$/)) alert('Natural numbers only!'); if ( height < 30) alert('Must be greater than 30!'); ctx.restore(); ctx.rect(x, y, width, height); ctx.stroke(); ctx.save(); ctx.rect(x, y, width, 30); ctx.fillStyle = "gray"; ctx.fillRect(x, y, width, 30); ctx.font = "15px Arial"; ctx.fillStyle = "black"; ctx.fillText("Window no. " + i, x + 10, y + 20); ctx.stroke(); ctx.save(); ctx.rect(new_x, y, 30, 30); ctx.fillStyle = "red"; ctx.fillRect(new_x, y, 30, 30); ctx.font = "15px Arial"; ctx.fillStyle = "black"; ctx.fillText("X", new_x + 10, y + 20); ctx.stroke(); ctx.save(); $("#details_1").text(i); $("#details_2").text('Those are: '); for (var j = 0; j < i; j++) $("#details_2").append('Window ' + (j+1) + ' '); } function close(){ //need help here } </script> </body> </html>

这是一个更简单的例子:

 var canvas, ctx; var win_list = []; var A = 0; var cnt = 0, i = 0; function win (width, height) { var x = 0, y = 0; return {'id':++i, 'name':"Window no. "+i ,'open':open, 'show':show ,'isInside':isInside, 'isInsideClose':isInsideClose}; function open (xx,yy) { x = xx; y = yy; this.show(); return this; } function show () { ctx.save(); ctx.fillStyle = 'white'; ctx.fillRect(x, y, width, height); ctx.strokeStyle = 'black'; ctx.strokeRect(x, y, width, height); ctx.fillStyle = 'gray'; ctx.fillRect(x, y, width, 30); ctx.strokeStyle = 'black'; ctx.strokeRect(x, y, width, 30); ctx.font = '15px Arial'; ctx.fillStyle = 'black'; ctx.fillText(this.name, x+10, y+20); var new_x = x + (width - 30); ctx.fillStyle = 'red'; ctx.fillRect(new_x, y, 30, 30); ctx.strokeStyle = 'black'; ctx.strokeRect(new_x, y, 30, 30); ctx.font = '15px Arial'; ctx.fillStyle = 'black'; ctx.fillText('X', new_x+10, y+20); ctx.restore(); return this; } function isInside (xx, yy) { return x <= xx && xx < x+width && y <= yy && yy < y+width; } function isInsideClose (xx, yy) { var new_x = x + (width - 30); return new_x <= xx && xx < x+width && y <= yy && yy < y+width; } } function mouse_position (event) { const rect = canvas.getBoundingClientRect(); const x = Math.floor(event.clientX - rect.left); const y = Math.floor(event.clientY - rect.top); if ( A === 1 ) create_win(x,y); if ( A === 2 ) close(x,y); } function create_win (x, y) { var width = $('#txt_width').val(); var height = $('#txt_height').val(); if (! width.match(/^\\d+$/) || !height.match(/^\\d+$/)) alert("Natural numbers only!"); if (height < 30) alert("Must be greater than 30!"); var w = win(width, height).open(x, y); win_list.push(w); $("#details_1").text(win_list.length); $("#details_2").append(' '+w.name); } function close (x, y) { //need help here var j; for (j = win_list.length - 1; j >= 0 ; --j) { let w = win_list[j]; if (w.isInside(x, y)) { if (w.isInsideClose(x, y)) { break; } return; } } if (j < 0) return; for (; j < win_list.length-1 ; ++j) { win_list[j] = win_list[j+1]; } win_list.pop(); redraw_all(); } function redraw_all () { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, 600, 600); win_list.forEach(function (w) {w.show();}); $("#details_1").text(win_list.length); $("#details_2").text("Those are: "); win_list.forEach(function (w) { $("#details_2").append(' '+w.name); }); }
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"></script> Height = <input type="text" id="txt_height" value="100"><br> Width = <input type="text" id="txt_width" value="200"><br> <input type="radio" name="choice" id="open"> Open new window <br> <input type="radio" name="choice" id="close"> Closing window <br> <br> <canvas id="canvas" width="600" height="600" style="border: 1px solid black"></canvas> <br> Currently, there are <span id="details_1"></span> windows open. <br> <span id="details_2"></span> <script> canvas = document.getElementById('canvas'); ctx = canvas.getContext("2d"); $("#details_1").text(0); $("#details_2").text("Those are: "); $('input[type=radio]').click(function(e) { if (this.id === 'open') A=1; if (this.id === 'close') A=2; }); $('#canvas').click(function(e){ mouse_position(e); }); </script>

Windows GUI、焦点和树

我想说这很容易。 无论您如何实现窗口系统,它都会很快变得复杂。

像 DOM 这样的 Windows GUI 被构建为一棵树,例如桌面包含窗口,窗口包含标题栏,标题栏包含关闭图标。

在下面的示例中,我实现了一个非常基本的树结构和各种类型的元素。 由于所有元素都有很多相同的行为,而且我个人不会使用类语法(直到它被正确修复),元素被构建添加 hock。

行为

  • click 事件在根元素树中搜索被点击的项目。

  • 项目通过顶部的聚焦窗口直观地排序。

  • 如果单击一个窗口元素并且它不在焦点上,则该窗口将被聚焦。

  • 如果一个窗口元素被点击并且它处于焦点并且被点击的项目是一个关闭按钮,该窗口将被关闭并且下一个视觉顺序将获得焦点。

  • 如果单击最顶部的项目(画布),则会创建一个新窗口。

  • 最顶层的元素(根)称为desktopCanvas 正是通过这个元素,您可以打开、关闭、聚焦和查询点击事件。

通常所有的动作都会驱动一个事件队列,但在这个例子中我没有实现。

所有项目都是从CanvasElement构建的,并且为CanvasElement分配了属性和方法来实现树结构、基本渲染。 您传递希望元素成为的对象类型。 例如const element = new CanvasElement(new Area(0, 0, this.w, 30), MenuBar, undefined, name)创建一个MenuBar类型的元素。 您可以通过cWindow.add(element)将其添加到窗口中

更多信息请参见示例。

例子

单击画布以创建窗口。 单击窗口以聚焦窗口。 单击焦点窗口上的关闭以关闭。 在获得焦点之前,您无法关闭窗口。

 const ctx = canvas.getContext("2d"); const canBounds = canvas.getBoundingClientRect(); canvas.addEventListener("click", mouseEvent); function mouseEvent(event) { const x = event.offsetX; const y = event.offsetY; const found = desktopCanvas.findClicked(x, y); if (found === desktopCanvas) { const wx = x < canvas.width - 200 ? x : canvas.width - 200; const wy = y < canvas.height - 200 ? y : canvas.height - 200; desktopCanvas.open(new CanvasElement(new Area(wx , wy, 200, 200), CanvasWindow, undefined, "Window")); } else if (found) { const top = found.topParent(); if (found.name === "close" && top.focused) { desktopCanvas.close(top) } else { desktopCanvas.focus(top) } } } const tree = { get children() { return [] }, add(child, top = false) { child.parent = this; if (top) { this.children.unshift(child) } else { this.children.push(child) } }, remove(child) { const idx = this.children.indexOf(child); if (idx > -1) { this.children.splice(idx, 1); return true; } for (const c of this.children) { if (c.remove(child)) { return true } } }, eachChild(cb, ...data) { if (typeof cb === "string") { for (const c of this.children) {c[cb](...data) } return; } for (const c of this.children) { cb(c) } }, topParent() { var top = this; while (top.top !== true) { top = top.parent } return top; }, } function Area(x, y, w, h) { this.x = x; this.y = y; this.w = w; this.h = h; } function CanvasElement(area, type = {}, name, ...data) { Object.assign(this, type); name !== undefined && (this.name = name); Object.assign(this, area); Object.assign(this, tree); this.init(...data); } CanvasElement.prototype = { init() {}, draw(ctx) { ctx.save(); this.transform(ctx); this.drawBorder(ctx); if (this.drawContent) { this.drawContent(ctx) } this.eachChild("draw", ctx); ctx.restore(); }, drawBorder(ctx) { ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.beginPath(); ctx.rect(0, 0, this.w, this.h); ctx.stroke(); }, transform(ctx) { ctx.transform(1, 0, 0, 1, this.x, this.y) }, isInside(x, y) { return x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h }, findClicked(x, y) { var idx = this.children.length; if (this.isInside(x, y)) { while (idx-- > 0) { // from top to bottom visually const child = this.children[idx]; const found = child.findClicked(x - this.x, y - this.y); if (found) { return found } } return this; } }, } const CloseIcon = { name : "close", drawContent(ctx) { const {w,h} = this; ctx.fillStyle = "#f00"; ctx.fill(); ctx.font = "16px arial"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = this.parent.parent.focused ? "#FFF" : "#000"; ctx.fillText("X", w / 2, h / 2); }, } const MenuBar = { name : "menuBar", init(text) { this.text = text; const bar = new Area(this.w - 18, 0, 18, 18); this.add(new CanvasElement(bar, CloseIcon)); }, drawContent() { ctx.fillStyle = this.parent.focused ? "#CCC" : "#999"; ctx.fill(); ctx.font = "16px arial"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = "#000"; ctx.fillText(this.text, this.w / 2, this.h / 2 + 2); }, } const CanvasWindow = { name: "window", top: true, init(name) { this.name = name; const bar = new Area(0, 0, this.w, 18); this.add(new CanvasElement(bar, MenuBar, undefined, name)) }, drawContent() { ctx.fillStyle = this.focused ? "#FFF" : "#EEE"; ctx.fill(); }, } const Desktop = { name: "desk", focuse: undefined, init(ctx) { this.w = ctx.canvas.width; this.h = ctx.canvas.height; this.ctx = ctx; }, draw() { this.ctx.setTransform(1,0,0,1, 0, 0) this.ctx.clearRect(0, 0, this.w, this.h); if (this.drawContent) { this.drawContent(this.ctx) } this.eachChild("draw", this.ctx); }, close(item) { this.remove(item); if(this.focuse && this.focuse === item) { if (this.children.length) { this.focus(this.children[this.children.length - 1]) } else { this.focuse = undefined } } desktopCanvas.draw(); }, open(item) { this.focus(item) }, focus(item) { this.remove(item); this.add(item); if(this.focuse) { this.focuse.focused = false; this.focuse = undefined; } item.focused = true; this.focuse = item; this.draw(); } } const desktopCanvas = new CanvasElement(new Area( 0, 0, canvas.width, canvas.width), Desktop, "desktop", ctx); desktopCanvas.open(new CanvasElement(new Area(10 , 10, 200, 200), CanvasWindow, undefined, "Example window"));
 <canvas id="canvas" width="600" height="600" style="border: 1px solid black"></canvas>

单击画布以创建窗口。 单击窗口以聚焦窗口。 单击焦点窗口上的关闭以关闭。 在获得焦点之前,您无法关闭窗口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM