简体   繁体   English

在 Canvas、JavaScript 上关闭窗口

[英]Closing windows on Canvas, JavaScript

I am trying to come up with a solution to this problem.我试图想出一个解决这个问题的方法。 I need to detect a mouse click on the little red rectangle for window closing.我需要检测鼠标点击小红色矩形以关闭窗口。 That clicked window should close only if there isn't some other window 'on top of it'.只有当没有其他窗口“在它上面”时,点击的窗口才应该关闭。

I thought of detecting a click on red color, but that doesn't work really well.我想检测红色的点击,但这并不能很好地工作。

PS.附注。 I can't use pop up windows, they need to be done like this.我不能使用弹出窗口,他们需要这样做。

Can someone help me with this?有人可以帮我弄这个吗? Thanks!谢谢!

 <!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>

Here is a simpler example :这是一个更简单的例子:

 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 GUIs, focus, and trees Windows GUI、焦点和树

I would like to say its easy.我想说这很容易。 No matter how you implement a windowing system it gets complicated very quickly.无论您如何实现窗口系统,它都会很快变得复杂。

Windows GUIs like the DOM are build as a tree, eg the desktop contains windows, windows contain title bars and the title bar contains the close icon.像 DOM 这样的 Windows GUI 被构建为一棵树,例如桌面包含窗口,窗口包含标题栏,标题栏包含关闭图标。

In the example below I implement a very basic tree structure and various types of elements.在下面的示例中,我实现了一个非常基本的树结构和各种类型的元素。 As all elements share a lot of the same behavior and as I personal will not use the class syntax (until it is fixed properly) the elements are build add hock.由于所有元素都有很多相同的行为,而且我个人不会使用类语法(直到它被正确修复),元素被构建添加 hock。

Behavior行为

  • The click event searches the root element tree for clicked items. click 事件在根元素树中搜索被点击的项目。

  • Items are sorted visually with the focused window at the top.项目通过顶部的聚焦窗口直观地排序。

  • If a window element is clicked and it is not in focus that window will be focused.如果单击一个窗口元素并且它不在焦点上,则该窗口将被聚焦。

  • If a window element is clicked and it is in focus and the clicked item is a close button that window will be closed and the next in visual order will get focus.如果一个窗口元素被点击并且它处于焦点并且被点击的项目是一个关闭按钮,该窗口将被关闭并且下一个视觉顺序将获得焦点。

  • If the top most item (canvas) is clicked a new window is created.如果单击最顶部的项目(画布),则会创建一个新窗口。

  • The top most element (root) is called desktopCanvas .最顶层的元素(根)称为desktopCanvas It is via this element that you open, close, focus, and query click events.正是通过这个元素,您可以打开、关闭、聚焦和查询点击事件。

Normally all the actions would drive an event queue, but in the example I have not implemented such.通常所有的动作都会驱动一个事件队列,但在这个例子中我没有实现。

All items are built from the CanvasElement and the CanvasElement is assigned the properties and methods to implement a tree structure, basic rendering.所有项目都是从CanvasElement构建的,并且为CanvasElement分配了属性和方法来实现树结构、基本渲染。 You pass the Object type you want the element to be.您传递希望元素成为的对象类型。 Eg const element = new CanvasElement(new Area(0, 0, this.w, 30), MenuBar, undefined, name) creates an element of type MenuBar .例如const element = new CanvasElement(new Area(0, 0, this.w, 30), MenuBar, undefined, name)创建一个MenuBar类型的元素。 You add it to a window via cWindow.add(element)您可以通过cWindow.add(element)将其添加到窗口中

See example for more.更多信息请参见示例。

Example例子

Click canvas to create windows.单击画布以创建窗口。 Click window to focus window.单击窗口以聚焦窗口。 Click close on focused window to close.单击焦点窗口上的关闭以关闭。 You can not close a window until it has focus.在获得焦点之前,您无法关闭窗口。

 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>

Click canvas to create windows.单击画布以创建窗口。 Click window to focus window.单击窗口以聚焦窗口。 Click close on focused window to close.单击焦点窗口上的关闭以关闭。 You can not close a window until it has focus.在获得焦点之前,您无法关闭窗口。

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

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