Webデスクトップの制作 #03 ウィンドウ表示

2024/10/10

アプリケーション

t f B! P L
eyecatch デスクトップで重要な要素として、ウィンドウの表示があります。 フォルダを開いて中に入っているファイルなどを表示したり、 テキストファイルを開いたり、 アプリケーションを表示する領域になるので、インターフェイスの根幹と言っても過言じゃないでしょう。 GUIの真骨頂ですね。 アイコンをクリックしたら、ウィンドウが表示する処理を作ってみます。

デモ

アイコンをクリックすると、ウィンドウが開きます。 ※ダブルクリックじゃないですよ。

ソースコード

index.html

<meta charset="utf-8"> <link rel="stylesheet" href="demo.css"> <script type="module" src="main.js"></script> <div id="desktop"> <header> <a class='logo' href='./'> Logo </a> <ul class="menu"> <li> <a href='#news'> 最新情報 </a> </li> <li> <a href='#contact' data-logined> お問い合わせ </a> </li> <li> <label for="menu"> メニュー </label> <ul> <li> <a class='sort-icon'> アイコン整列 </a> </li> <li> <a href='#link'> Links </a> </li> </ul> </li> </ul> <div class='time'><span class='ymd'></span><span class='his'></span></div> <label for='menu_toggle'> <div class='hamburger'> <span></span> </div> </label> </header> <main class='desktop'> <div class="icon"><img src="img/set-up-svgrepo-com.png"><p class="name">Setting</p></div> <div class="icon"><img src="img/gift-svgrepo-com.png"><p class="name">Gift</p></div> <div class="icon"><img src="img/like-svgrepo-com.png"><p class="name">Heart</p></div> <div class="icon"><img src="img/the-internet-svgrepo-com.png"><p class="name">World</p></div> </main> </div>

style.css

#desktop{ --header-size : 50px; --main-size : calc(100% - var(--header-size)); --color-bg1 : #6bd8e5; --color-bg2 : #98cead; width : 100%; height:50vh; min-height: 300px; box-shadow:4px 4px 20px rgba(0,0,0,0.5); display:flex; flex-direction:column; gap:0; } #desktop, #desktop *{ white-space:normal; } #desktop, #desktop *, #desktop *::before, #desktop *::after{ -webkit-box-sizing : border-box; -moz-box-sizing : border-box; -o-box-sizing : border-box; -ms-box-sizing : border-box; box-sizing : border-box; } #desktop main{ height : var(--main-size); position:relative; z-index:1; background: linear-gradient(-45deg, var(--color-bg1), var(--color-bg2)); } #desktop header{ width:100%; height : var(--header-size); display : flex; gap:20px; align-items : center; padding:0 10px; background-color:white; position:relative; z-index:100; } #desktop header .time{ padding-right:10px; } #desktop header .time *{ font-size:0.8em; display:block; height:30%; text-align:right; } #desktop header .time > *{ display:block; margin:0; } #desktop .menu{ --hover-color: #FDD; height:100%; margin-left:auto; display:flex; align-items:center; gap:10px; } #desktop .menu li{ height:100%; padding:5px; display:flex; gap:2px; align-items:center; justify-content:center; cursor:pointer; position:relative; } #desktop .menu *{ color : black; text-decoration:none; } #menu .icon{ width:20px; height: 20px; fill:black; vertical-align:middle; } #desktop label[for='menu_toggle']{ display:none; } /** * サブメニュー */ #desktop .menu > li > ul{ display:flex; flex-direction:column; background-color:white; min-width:100px; position:absolute; top:100%; left:50%; transform:translateX(-50%); margin:0; padding:0; } #desktop .menu > li:has(ul)::after{ content:""; display:inline-block; width:0.5em; height:0.5em; border-color:black; border-style:solid; border-width:0 1px 1px 0; transform:rotate(45deg); margin-left:4px; } #desktop .menu > li:hover{ background-color: var(--hover-color); } #desktop .menu > li:not(:hover) > ul{ display:none; } #desktop .menu > li > ul li{ height:var(--header-size); padding:5px 10px; cursor:pointer; justify-content:start; } #desktop .menu > li > ul li *{ white-space:nowrap; } #desktop .menu > li > ul li:hover{ background-color: var(--hover-color); } @media (max-width:500px){ #desktop label[for='menu_toggle']{ display:block; } #desktop header nav{ height:100%; margin:0; width:100%; } #desktop header .menu{ justify-content:flex-start; width:100%; height:100%; background-color:var(--color-body-bg); } #desktop header .menu > *{ padding:0; flex-grow:1; } } #desktop .logo{ height:100%; display:flex; align-items:center; text-decoration:none; background-color:var(--color-bg); } #desktop .logo img, #desktop .logo svg{ height:100%; fill:var(--color-01); } /** * Icon */ #desktop{ --icon-size : 50px; --icon-margin : 10px; --icon-font-size : 10px; } #desktop .icon{ --x : 0px; --y : 0px; --z : 1; --z-add : 2000; width : calc(var(--icon-size) + var(--icon-margin) * 2 ); display : flex; flex-direction: column; gap : 4px; /* cursor : move; */ /* position : absolute; left : var(--x); top : var(--y); */ border : 2px solid transparent; border-radius:5px; overflow : hidden; z-index : var(--z); } #desktop .icon:not([data-status="move"]){ transition-property: left,top; transition-duration: 0.3s; } #desktop .icon[data-status="move"]{ z-index : calc(var(--z) + var(--z-add)); } #desktop .icon[data-select]{ border-color:rgba(255,255,255,0.5); background-color:rgba(0,0,0,0.3); } #desktop .icon img{ display : block; margin : 0 var(--icon-margin); width : var(--icon-size); height : var(--icon-size); object-fit: contain; pointer-events:none; } #desktop .icon .name{ display : block; margin : 0; padding : 3px; width : 100%; font-size : var(--icon-font-size); text-align: center; line-height: 1.4em; word-break: break-all; overflow: hidden; display: -webkit-box; text-overflow: ellipsis; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } #desktop .icon[data-select] .name{ background-color:rgb(66, 86, 188); color:white; } /** * Window */ #desktop .window{ position:absolute; top : 50%; left : 50%; transform:translate(-50%,-50%); width : 50%; height : 50%; min-width: 300px; min-height:300px; display:flex; flex-direction:column; box-shadow:4px 4px 20px rgba(0,0,0,0.5); border-radius:10px; overflow:hidden; z-index:100; } #desktop .window .header{ height:30px; background-color:#DDD; /* cursor:move; */ display:flex; gap:8px; align-items:center; padding:10px; } #desktop .window .header .name{ white-space:nowrap; overflow: hidden; text-overflow: ellipsis; } #desktop .window .header .close{ margin-left:auto; width: 20px; height: 20px; cursor:pointer; background-color:white; border:1px solid black; position:relative; } #desktop .window .header .close::before, #desktop .window .header .close::after{ content:""; display:block; width:100%; height:1px; background-color:black; position:absolute; top:50%; left:50%; } #desktop .window .header .close::before{ transform:translate(-50%,-50%) rotate(45deg); } #desktop .window .header .close::after{ transform:translate(-50%,-50%) rotate(-45deg); } #desktop .window .body{ flex:1; background-color:white; overflow:auto; position:relative; z-index:1; } @media (max-width:500px){ #desktop header .time{ padding:0; } }

main.css

class Main{ constructor(){ this.set_event() } get elm_main(){ return document.querySelector("main.desktop") } set_event(){ window.addEventListener("click" , this.click.bind(this)) } click(e){ // アイコンをクリック const icon = e.target.closest(".icon") if(icon){ const name = icon.querySelector(".name").textContent this.view_window({ name : name }) } // windowのクローズボタンをクリック const close = e.target.closest(".window .header .close") if(close){ const elm_window = e.target.closest(".window") elm_window.parentNode.removeChild(elm_window) } } view_window(options){ // same window don't view if(this.elm_main.querySelector(`.window[name="${options.name}"]`)){return} const elm_window = document.createElement("div") elm_window.className = "window" elm_window.name = options.name elm_window.innerHTML = `<div class="header"> <span class="name">${options.name}</span> <div class="close"></div> </div> <div class="body"></div> ` this.elm_main.appendChild(elm_window) } } switch(document.readyState){ case "complete": case "interactive": new Main() break default: window.addEventListener("DOMContentLoaded", (()=>new Main())) }

解説

HTMLは前回(#02)とほぼ同じです。(Javascriptファイルの追加のみ冒頭しています。) CSSは、windowの部分(今回追加分)の箇所を色を変えて登録しておきました。 そして、Javascriptが今回新設した内容です。 ソースは比較的内容が分かる人はシンプルだと気がついてくれると思いますが、 画面クリックイベントを軸に、アイコンか、ウィンドウの閉じるボタンかを判定して、それぞれの処理につなげています。 見た目は全てCSSで制御するようにして、Javascriptでは、DOM構造をちゃんと整えるという役割に徹しましょう。

あとがき

デスクトップとしての要素を必要最低限詰め込んだ状態になりましたが、まだまだ処理が足りていません。 アイコンなどをウィンドウに出したり入れたり、ダブルクリックで開くようにする仕様にして、より今どきのGUIデスクトップに近づける必要がありますね。 なかなか長い道のりですが、コツコツ作り続けていくとゴールにたどり着けることでしょう。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ