天天看點

基于HTML5建構Web作業系統

HTML5 是下一代 web 語言的标準,具有相容性好,安全性高,功能豐富,開發便捷等優點,特别适合如 Web 作業系統一類的富用戶端網際網路應用的前端開發。本文将展示如何利用 HTML5 提供的多種新技術如:本地資料庫、多線程開發、視訊支援、離線程式設計等建構一個基本的 Web 作業系統。

簡介

傳統的作業系統有着一些難以克服的缺點,如僅能在本地終端通路,或僅支援有限的遠端通路,限于本地終端的資源,計算能力薄弱,存儲空間有 限,缺乏強大的防火牆等一系列安全機制,安全性較差。鑒于以上缺點,Web 作業系統應運而生 - Web 作業系統是一種基于浏覽器的虛拟的作業系統,使用者通過浏覽器可以在其中進行應用程式的操作,以及相關資料的存儲。Web 作業系統提供的基本服務有文本文檔的建立與存儲,音頻視訊檔案的播放與存儲,提供對時間資訊的支援等,更進階的服務則包含即時通信,郵件甚至遊戲等服務。 Web 作業系統克服了傳統作業系統的缺點,在網絡的支援下,它可以在任何時間,任何地點經由任何支援 Web 的終端進行通路,可以利用伺服器端無限的計算及存儲資源,使用者資料儲存在伺服器端,安全性較高。

相關技術

目前建構 Web 作業系統的前端技術主要有 Flex、Silverlight、ActiveX 插件等等,它們各有一些優缺點。

Flex

Flex 是一個優秀的富用戶端應用架構,專注于頁面顯示,Adobe 專業維護,統一穩定,而且其腳本語言 ActionScript3 是面向對象的,非常适合程式員使用。缺點則是耗能高,占用帶寬多,對移動應用的支援性差。

Silverlight

Silverlight 是由微軟推出的用以跟 Flash 抗衡的 RIA(富網際網路應用)解決方案,優點是具備硬體級的加速功能,但它目前仍不成熟,對非 Windows 系統的支援性并不夠好,且學習難度較大。

ActiveX 插件

ActiveX 插件同樣是微軟推出的 RIA 解決方案,它是一個開放的解決方案,可以相容多種語言,不過它的缺點也是顯而易見的,使用者需要調整浏覽器的安全等級并下載下傳插件才能運作 RIA 應用,極大地降低了安全性。

HTML5

為推動 web 标準化運動的發展,W3C 推出了下一代 HTML 的标準 - HTML5,為衆多的公司所支援,是以具有良好的前景。它有以下特點:首先,為增強使用者體驗,強化了 web 網頁的表現性能;其次,為适應 RIA 應用的發展,追加了本地資料庫等 web 應用的功能;再次,由于高度标準化以及諸多浏覽器廠商的大力支援,它的相容性和安全性非常高;最後它是一種簡潔的語言,容易為廣大開發者掌握。更為難得的 是,由于節能和功耗低,在移動裝置上 HTML5 将具有更大的優勢。是以更适合如 Web 作業系統一類的 RIA 應用的前端開發。

系統簡介

本系統基于 HTML5 開發,利用 HTML5 引入的多種新技術如拖拽 API、視訊标簽、本地資料庫、draw API、多線程開發、離線程式設計等提供了一個基本的 Web 作業系統環境,包含了對桌面的支援、應用程式的支援,提供了一個簡單的視訊播放器和記事本以及一個時鐘,并對系統日志進行了記錄,此外還提供了對離線狀态 的支援。

回頁首

桌面實作

系統對桌面的支援主要包括應用程式圖示的打開與拖拽,以及桌面的上下文菜單等。

桌面拖拽

桌面的布局由一定數量的 div 組成,它們按照次序依次排列在矩形的桌面上,為應用程式圖示的打開與拖拽提供了基本的支援。

清單 1. 建立 div

var iconHolder = document.createElement("div");   iconHolder.id = 'iconHolder' + i;   iconHolder.className = "iconHolder";   mainDiv.appendChild(iconHolder);        

HTML5 提供了對 drag 事件的支援,大大簡化了實作拖拽的難度。通過對 dragstart 事件的監聽,将被拖拽的應用程式圖示所在的 div 記錄下來,作為拖拽的源。

清單 2. 拖拽支援

iconHolder.addEventListener("dragstart", function(ev) {   var dt = ev.dataTransfer;   dt.setData("text/plain", ev.currentTarget.id);// 記錄被拖拽圖示的 id   }, false);    iconHolder.addEventListener("drop", function(ev) {   var dt = ev.dataTransfer;   var srcIconHolderId = dt.getData("text/plain");   var srcIconHolder = document.getElementById(srcIconHolderId);    // 如果拖拽至資源回收筒,則删掉被拖拽圖示,否則互換兩圖示位置  if(ev.currentTarget.firstChild && ev.currentTarget.firstChild.id == "recycleBin" &&   srcIconHolder.firstChild.id != "recycleBin"){                  srcIconHolder.innerHTML = "";   }else if(ev.currentTarget.firstChild){          var temp =  ev.currentTarget.firstChild;          ev.currentTarget.appendChild(srcIconHolder.firstChild);          srcIconHolder.appendChild(temp);   }else{         ev.currentTarget.appendChild(srcIconHolder.firstChild);   }   }, false);        

通過對 drop 事件的監聽,可以擷取拖拽的源,以及拖拽的目标 div。若目标 div 為空,則将源 div 中的應用程式圖示轉移至目的 div 中。若目标 div 中已包含應用程式圖示,則将兩個圖示的位置互換。若資源回收筒圖示處于目标 div 中,資源回收筒将發揮作用并将源 div 中的應用程式圖示删除。圖 1 顯示了桌面拖拽的效果。

圖 1. 桌面拖拽效果

基于HTML5建構Web作業系統

程式打開

程式可以以兩種方式打開,左鍵點選或通過上下文菜單打開。

通過監聽 div 的 onclick 事件,擷取要打開的應用程式 id,并利用 openApp 方法打開相應的應用程式可實作對左鍵點選的支援。

清單 3. 左鍵點選

iconHolder.onclick =  function(ev){   if(ev.currentTarget.firstChild){          openApp(ev.currentTarget.firstChild.id);          ev.stopPropagation();   }   };        

通過監聽 div 的 oncontextmenu 事件,擷取要打開的應用程式 id,并利用 openAppContextMenu 方法顯示相應應用程式的上下文菜單,可實作對右鍵上下文菜單的支援。

清單 4. 上下文菜單

iconHolder.oncontextmenu =  function(ev){   if(ev.currentTarget.firstChild){          openAppContextMenu(ev.currentTarget.firstChild.id, ev);          ev.stopPropagation();   }   return false;   };        

利用相應應用程式的 id,可以擷取對應應用程式的腳本,并執行,同時在系統日志中記錄下相應的操作。

清單 5. 程式打開

function openApp(appId){      var time = new Date().getTime();      var action = "open app";      var details = "open: " + appId;      addHistory(time, action, details);// 記錄系統日志      var appScript = getAppScript(appId);// 擷取應用程式腳本      eval(appScript);// 執行應用程式  }        

清單 6. 打開程式上下文菜單

function openAppContextMenu(appId, ev){   var appContextMenu = document.getElementById("appContextMenu");   appContextMenu.style.display="block";// 令上下文菜單可見  appContextMenu.style.pixelTop=ev.clientY;// 設定上下文菜機關置   appContextMenu.style.pixelLeft=ev.clientX;   appContextMenu.style.background = "#eee";   appContextMenu.style.color = "black";   appContextMenu.style.fontSize = "30";   appContextMenu.style.width = "200px";   appContextMenu.style.height = "220px";   appContextMenu.style.opacity = 0.5;// 令上下文菜單透明度為 50%   appContextMenu.innerHTML = "";    // 擷取應用程式相應上下文菜單的内容  var apps = getApps();   for(var i=0; i<apps.length; i++){                  if(apps[i].appId == appId){                          for(var j=0; j<apps[i].contextMenu.length; j++){                          appContextMenu.innerHTML += "<div class='appContextMenuItem'                          οnclick=\"appContextMenu.style.display='none';" +                          apps[i].contextMenu[j].action + "\"                          οnmοuseοver='this.style.background=\"darkblue\"'                          οnmοuseοut='this.style.background=\"#eee\"'>"                         +apps[i].contextMenu[j].name+"</div>";                          }                          break;                   }     }   }        

應用程式的上下文菜單由名為 appContextMenu 的 div 實作,将 oncontextmenu 事件中的 clientX 及 clientY 作為上下文菜單出現的位置,并将其透明度設定為 0.5。利用相應應用程式的 id 擷取上下文菜單對應的内容,并将其填充至上下文菜單。

圖 2 顯示了應用程式上下文菜單打開時的效果。

圖 2. 應用程式上下文菜單

基于HTML5建構Web作業系統

上下文菜單

桌面上下文菜單的實作方式與應用程式上下文菜單的實作方式基本類似,圖 3 和圖 4 分别是桌面以及工作列的上下文菜單。

圖 3. 桌面上下文菜單

基于HTML5建構Web作業系統

圖 4. 工作列上下文菜單

基于HTML5建構Web作業系統

視訊播放器

系統提供了一個簡單的視訊播放器,它支援從系統外部拖拽視訊檔案進行播放。

順應網絡媒體的發展,HTML5 提供了視訊标簽 video 以便于加強對視訊的支援,大大簡化了 web 播放器開發的難度,開發人員僅憑幾行代碼,就可以開發出一個基本功能完善的視訊播放器。

清單 7. 視訊标簽的建立

var video = document.createElement('video');   video.id ='video';   video.src ='';   video.width  = 370;   video.height = 260;   video.controls = 'controls';   video.className = 'video';   appHolder.appendChild(video);   addDragSupport(appHolder);        

清單 7 中構造了一個 video 标簽并将其添加到一個名為 appHolder 的 div 中。代碼的最後一行為其添加了拖拽的支援。

HTML5 不但支援浏覽器内的拖拽,也支援浏覽器與本地系統之間的拖拽。清單 8 顯示了為一個 div 添加拖拽支援的過程。

清單 8. 添加拖拽支援

function addDragSupport(dropbox){   document.addEventListener("dragenter", function(e){   }, false);   document.addEventListener("dragleave", function(e){   }, false);   dropbox.addEventListener("dragenter", function(e){   }, false);   dropbox.addEventListener("dragleave", function(e){   }, false);   dropbox.addEventListener("dragenter", function(e){   e.stopPropagation();   e.preventDefault();   }, false);   dropbox.addEventListener("dragover", function(e){   e.stopPropagation();   e.preventDefault();   }, false);   dropbox.addEventListener("drop", function(e){   handleFiles(e.dataTransfer.files, e.currentTarget, e);   e.stopPropagation();   e.preventDefault();                }, false);    }        

其中,handleFiles 函數說明了如何對拖拽的檔案進行處理。

清單 9. 拖拽處理

function handleFiles(files, dropbox, e) {      if(files.length == 0){// 若檔案不存在,則用相應文本代替          var dt = e.dataTransfer;           var text = dt.getData("text/plain");           var p = document.createElement("p");           p.innerHTML += text;           dropbox.appendChild(p);           return;   }    for (var i = 0; i < files.length; i++) {           var file = files[i];           var fileProcessor = dropbox.firstChild;           fileProcessor.classList.add("obj");           fileProcessor.file = file; // 添加檔案                  var reader = new FileReader();           reader.onload = (// 讀取檔案内容          function(aFileProcessor) {                   return function(e) {                   aFileProcessor.src = e.target.result;   };   }   )(fileProcessor);    reader.readAsDataURL(file);   }   }        

handleFiles 函數首先判斷檔案是否存在,若不存在,則以相應文字取代,若存在,則對

所有檔案一一進行處理。向 fileprocessor( 這裡是視訊标簽 ) 添加檔案,然後利用 FileReader 讀取檔案内容至 fileprocessor 進行處理。

圖 5 顯示了拖拽一個視訊檔案 movie.ogg 到播放器的效果。

圖 5. 視訊播放

基于HTML5建構Web作業系統

回頁首

本地存儲

Web 作業系統通常将大部分資料存儲于伺服器端,這樣做的好處顯而易見,資料存儲空間更大,安全性更好。然而這樣做也有不足之處,由于網絡的穩定性依然較本地磁 盤差,是以在脫離網絡的狀況下,Web 作業系統無法擷取相應的資料資源,是以 Web 作業系統需要一定的通路本地存儲空間的能力,當然本地存儲空間僅是作為伺服器端存儲的一個補充,它的空間有限,通路也受到一定的限制。

一直以來,HTML 以 Cookie 作為通路本地空間的方式,然而,這種方式有着很多缺點和不足,如存儲的資料格式過于簡單,通常僅為鍵值對;存儲的空間大小有限。為此,HTML5 提供了本地資料庫以增強本地存儲空間的通路能力,它是一個簡化版的資料庫,能夠支援模拟的 SQL 以及簡單的事務處理等功能。

系統為支援本地存儲,建立了一個名為 MyData 的資料庫。清單 10 顯示了資料庫建立的過程。

清單 10. 建立資料庫

var db;   var openDatabase;   if(openDatabase != undefined)       db = openDatabase('MyData', '', 'My Database', 102400);        

其中 MyData 為資料庫的名稱,省略的參數為資料庫的版本,My Database 為顯示的名稱,最後的數字為資料庫預估長度(以位元組為機關)。

系統日志将系統在某一時間的行為操作記錄下來,本地資料庫為其提供存儲支援。日志在資料庫中存儲為表 History,包含 3 個字段,分别為時間,操作,及操作的詳細資訊。清單 11 顯示了系統是如何記錄日志的。

清單 11. 日志記錄

var time = new Date().getTime();    var action = "open app";   var details = "open: " + appId;   addHistory(time, action, details);// 向系統日志中添加一條記錄    function addHistory(time, action, details){   if(openDatabase != undefined)   db.transaction(   function(tx) {   tx.executeSql('CREATE TABLE IF NOT EXISTS History(time INTEGER,   action TEXT, details TEXT)',[]);// 建立日志記錄表    tx.executeSql('INSERT INTO History VALUES(?, ?, ?)', [time,   action, details], // 插入一條日志  function(tx, rs) {    //alert("store: "+time+"-"+action+"-"+details);                  },    function(tx, error) {      //alert(error.source + "::" + error.message);    });    });    }        

清單的第一部分顯示了如何調用日志記錄,第二部分顯示了日志記錄的詳細過程。在一個 transaction 中,首先判斷表 History 是否存在,若不存在,則建立它。第二部分執行一條 SQL 語句,向資料庫中插入目前的日志。

通過檢索表 History,我們可以檢視系統日志,清單 12 顯示了如何從資料庫中查詢系統日志,并将其顯示出來。

清單 12. 日志顯示

var historyTable = document.getElementById("historyTable");    // 定義表頭  historyTable.innerHTML = "";   var th = document.createElement('thead');   th.style = "color:#CC3300";   var th1 = document.createElement('td');   th1.align = "center";   th1.width=300;   th1.innerHTML = "Time";   var th2 = document.createElement('td');   th2.align = "center";   th2.width=100;   th2.innerHTML = "Action";   var th3 = document.createElement('td');   th3.align = "center";   th3.width=150;   th3.innerHTML = "Details";   th.appendChild(th1);    th.appendChild(th2);    th.appendChild(th3);   historyTable.appendChild(th);                   if(openDatabase != undefined)   db.transaction(function(tx) {      tx.executeSql('SELECT * FROM History', [], function(tx, rs)   {         // 将日志逐條顯示到表的各行中  for(var i = 0; i < rs.rows.length && i<15; i++) {                      var tr = document.createElement('tr');   var td1 = document.createElement('td');   td1.style.paddingLeft = "3px";   td1.style.paddingRight = "3px";                        var t = new Date();    t.setTime(rs.rows.item(i).time);    td1.innerHTML = t.toLocaleDateString()+  " "+t.toLocaleTimeString();     var td2 = document.createElement('td');    td2.style.paddingLeft = "3px";   td2.style.paddingRight = "3px";   td2.innerHTML = rs.rows.item(i).action;                          var td3 = document.createElement('td');   td3.style.paddingLeft = "3px";   td3.style.paddingRight = "3px";   td3.innerHTML = rs.rows.item(i).details;                           tr.appendChild(td1);    tr.appendChild(td2);    tr.appendChild(td3);                          historyTable.appendChild(tr);                     }    });    });         

清單 12 中,首先擷取用于顯示的日志的 HTML 表格 historyTable,并設定其樣式及表頭。

然後在一個 transaction( 事務 ) 中,執行一條 SQL 語句,查詢系統日志,并将每條日志添加為 historyTable 中的一行以便顯示。圖 6 顯示了系統日志的效果。

圖 6. 系統日志

基于HTML5建構Web作業系統

記事本

系統提供了一個簡單的記事本,實作了文本文檔的基本操作。文本文檔包含标題和内容兩個顯式屬性,以及一個名為 id 的隐式屬性。與系統日志類似,本地資料庫為文本資料的存儲提供了底層的支援。圖 7 顯示了記事本程式的界面。

圖 7. 記事本

基于HTML5建構Web作業系統

當編輯完文檔的标題與内容後,點選左上角的儲存按鈕,将執行 createFile 函數。清單 13 顯示了 createFile 函數的詳細過程。

清單 13. 建立檔案

function createFile(fileId, fileTitle, fileContent){       var idx = 1;       var update = false;//false 表示建立,true 表示修改       if(openDatabase != undefined)           db.transaction(function(tx) {           tx.executeSql('CREATE TABLE IF NOT EXISTS TextFiles(idx INTEGER,           title TEXT, content TEXT)',[]);// 建立文本文檔表          tx.executeSql('SELECT * FROM TextFiles', [], function(tx, rs){               for(var i = 0; i < rs.rows.length; i++) {                  // 若文檔存在,則修改它                  if(rs.rows.item(i).idx == fileId){                       db.transaction(function(tx) {                          tx.executeSql('UPDATE TextFiles                       SET title=?, content=?                       WHERE idx='+fileId,                       [fileTitle, fileContent],                       function(tx, rs) {                                 alert("update successfully");                       });                    });                   return;               }           }      // 若文檔不存在,則建立一個文檔           if(rs.rows.length>0)   idx = rs.rows.item(rs.rows.length-1).idx + 1;   db.transaction(function(tx) {                           tx.executeSql('INSERT INTO TextFiles VALUES(?, ?, ?)', [idx, fileTitle, fileContent],                function(tx, rs){                  alert("save successfully: "+idx+"-"+fileTitle+ "-"+fileContent);                  createFileIcon(idx);    },    function(tx, error) {                  alert(error.source + "::" + error.message);                    });                });           });       });   }        

清單 13 首先在一個 transaction 中,首先判斷用于存儲文本文檔的表 TextFiles 是否存在,若不存在,則建立它。然後通過查詢表 TextFiles 判斷文本文檔是否存在,若存在,則目前操作為更新操作,程式将執行一條 SQL 語句,對目前文本文檔進行更新。若不存在,則取目前最大文檔 id 并加 1 作為新文檔的 id,并執行一條 SQL 語句,将文檔資訊,包括文檔 id,以及标題和内容插入到資料庫中,并于插入操作結束後的回調方法中,利用 createFileIcon 方法在桌面上為新文檔建立一個文檔圖示。清單 14 顯示了 createFileIcon 方法的具體過程。

清單 14. 建立文檔圖示

function createFileIcon(fileId){       var iconHolder;       for(var i=1;i<=120;i++){// 查詢第一個為空的位置          iconHolder = document.getElementById('iconHolder' + if(!iconHolder.firstChild ){               var text = document.createElement('img');               text.src = "images/text.gif";               text.id = fileId;               iconHolder.appendChild(text);               text.onclick =  function(ev){                    if(ev.currentTarget){                   openApp('notebook');// 打開記事本應用程式                  var saveHolder = document.getElementById('saveHolder');                   saveHolder.onclick  = function(){                       var title = document.getElementById('title');                       var content = document.getElementById('content');                       createFile(fileId, title.value, content.value);// 建立文本文檔                  };                var openedFileId = ev.currentTarget.id;               if(openDatabase != undefined)               db.transaction(function(tx) {// 查詢資料庫,顯示文檔内容              tx.executeSql('SELECT * FROM TextFiles', [], function(tx, rs){                   for(var i = 0; i < rs.rows.length; i++) {                    if((rs.rows.item(i).idx+"") == (openedFileId+"")){                       var title = document.getElementById('title');                       var content = document.getElementById('content');                                 title.value = rs.rows.item(i).title;                                         content.value = rs.rows.item(i).content;}                                    }                  });   });     ev.stopPropagation();   }   };   break;   }       }//for   }        

清單 14 首先在桌面中尋找一個空的 div,然後建立一個文檔圖示,并将其填充至 div。文檔圖示有一個 id 屬性對應文檔 id。最後為文檔圖示添加點選事件處理函數,當點選文檔圖示時,會首先打開記事本,然後根據文檔圖示的 id 查詢資料庫,提取文檔的标題和内容進行顯示。

圖 8 顯示了建立後的文本文檔,點選後的效果如圖 7 所示。

圖 8. 文本文檔

基于HTML5建構Web作業系統

時鐘

系統提供了一個簡單的時鐘用以顯示目前時間,它由一個表盤以及分針和時針組成,能夠随着時間的變化動态地變換。以往的 web 應用利用 JavaScript 或 Flash 完成此類功能,其複雜性可想而知。借助 HTML5 的 draw API,可以輕松地畫出所需的圖形,極大的友善了此類應用的建構,此外,HTML5 還提供了以往 JavaScript 無法支援的多線程程式設計,大大加強了 web 應用的互動性和豐富性。

時鐘有一個基本的表盤,它僅是一副簡單的圖檔,如圖 9 所示。

圖 9. 表盤

基于HTML5建構Web作業系統

在表盤之上,建有一個 canvas( 畫布 ),如清單 15 所示。

清單 15. 畫布

<canvas id="canvas" width="128px" height="128px" class="canvas"></canvas>        

接下來,清單 17 将在畫布上模拟出時鐘以及分針,在這之前,額外需要一個背景線程用以計算時間,它被定義在名為 time.js 的獨立腳本檔案中,如清單 16 所示。

清單 16. 背景線程

onmessage = function(event)   {   //var i = 1;      setInterval(function() {      //i++;      postMessage("");      }, 60000);   };        

每過 60 秒鐘,背景線程将會向前台線程發送一個空消息,以告訴前台線程有 60 秒鐘已經過去了。

清單 17. 前台線程的初始化

var canvas = document.getElementById("canvas");   if (canvas == null)    return false;    var context = canvas.getContext('2d');// 這是一個二維的圖像  context.lineWidth = 2;   context.translate(64, 64);// 定義原點   // 初始化分針  context.beginPath();   context.moveTo(0,0);// 從原點開始   var date = new Date();   var mhx = 37*Math.cos((date.getMinutes()-15)*Math.PI/30);   var mhy = 37*Math.sin((date.getMinutes()-15)*Math.PI/30);   context.lineTo(mhx, mhy);// 至分針末端所在位置  context.closePath();   context.stroke();        // 初始化時針  context.beginPath();   context.moveTo(0,0);// 從原點開始  var date = new Date();   var hour = date.getHours();   if(hour>=12)   hour = hour - 12;   var minute = date.getMinutes();   var hhx = 27*Math.cos((hour-3)*Math.PI/6 + minute*Math.PI/360);   var hhy = 27*Math.sin((hour-3)*Math.PI/6 + minute*Math.PI/360);   context.lineTo(hhx, hhy);// 至時針末端所在位置  context.closePath();   context.stroke();        

前台線程首先會擷取 canvas,并設定表盤中心為坐标原點。然後,擷取目前時間,計算分針目前所應指向的坐标,然後從原點出發,畫出分針。對于時針,若系統為 24 小時制,需要首先轉化為 12 小時制,此後的處理類似于分針。

接下來,需要将前台與背景線程聯系起來,利用 HTML5 提供的多線程程式設計方法,聲明 Worker 對象作為背景線程的代理,并利用 onmessage 事件,對背景線程發出的消息進行處理。

清單 18. 前台線程的 onmessage 事件

var worker = new Worker("js/timer.js");    worker.onmessage = function(event){           context.clearRect(-64, -64, 128, 128);// 清空分針和時針          // 重畫分針     context.beginPath();      context.moveTo(0,0);// 從原點開始       var date = new Date();      var mhx = 37*Math.cos((date.getMinutes()-15)*Math.PI/30);      var mhy = 37*Math.sin((date.getMinutes()-15)*Math.PI/30);      context.lineTo(mhx, mhy);// 至分針末端所在位置     context.closePath();      context.stroke();               // 重畫時針     context.beginPath();      context.moveTo(0,0);// 從原點開始       var date = new Date();      var hour = date.getHours();      if(hour>=12)      hour = hour - 12;      var minute = date.getMinutes();      var hhx = 27*Math.cos((hour-3)*Math.PI/6 + minute*Math.PI/360);      var hhy = 27*Math.sin((hour-3)*Math.PI/6 + minute*Math.PI/360);      context.lineTo(hhx, hhy);// 至時針末端所在位置     context.closePath();      context.stroke();      };      worker.postMessage("");        

每過 60 秒鐘,背景線程将會向前台線程發送一個空消息,前台線程接收到消息後,首先,清空 canvas,然後重新擷取目前時間,計算分針以及時針對應的坐标,并重新畫出時針和分針,進而完成對分針以及時針的更新,最終,每過 1 分鐘,表盤更新一次,進而模拟出動态時針的效果,如圖 10 所示。

圖 10. 時鐘

基于HTML5建構Web作業系統

離線支援

雖然 Web 作業系統的優點是可以利用網絡随時随地進行通路。然而在無法通路網絡的情況下,Web 作業系統便無法發揮作用。是以 Web 作業系統有必要在離線狀态下,仍能對部分應用及其功能進行支援。事實上,各種浏覽器已提供了各式各樣的緩存機制以提供對離線應用的支援,然後這些緩存機制 往往是臨時性的,不可控的。HTML5 為開發人員提供了解決此問題的另一種途徑,它提供了一種永久性的,自定義的緩存方法,使得 Web 作業系統可以在離線的狀況下,依然支援部分應用的功能。

HTML5 離線支援的核心是一個緩存清單,其中列出了需要緩存的檔案,本系統中的緩存檔案 index.manifest,如清單 19 所示。

清單 19. 緩存清單

CACHE MANIFEST   #version 1.10   CACHE:   index.html   js/app.js   js/clock.js   js/data.js   js/database.js   js/desktop.js   js/history.js   js/taskbar.js   js/timer.js   js/file.js   js/utils.js   css/index.css   images/appHolder1.png   images/background.jpg   images/clock.png   images/close.gif   images/computer.gif   images/history.png   images/network.gif   images/recycleBin.gif   images/startIcon.png   images/taskBar.png   images/vidioplayer.gif   images/notebook.gif   images/text.gif   images/save.gif   movs/movie.ogg   sounds/WindowsLogonSound.wav        

其中,CACHE MANIFEST 标示本檔案為緩存檔案,#version 1.10 标示了本檔案的版本。

CACHE 之後所羅列的則是開發人員自定義的内容,其中包含了所有在離線狀态下使用者通路應用程式所必不可少的檔案。

緩存清單定義結束後,在 index.html 中插入這個清單檔案名,這樣,當浏覽器加載這個頁面的時候,會自動緩存清單檔案中所羅列的檔案。

清單 20. 應用緩存清單

<html manifest="index.manifest">        

值得一提的是,若要支援離線緩存,除用戶端浏覽器的支援以外,服務端的支援也是必不可少的,就本系統所使用的 tomcat 而言,需要在其配置檔案 web.xml 中添加清單 21 所示的條目。

清單 21. 伺服器端緩存配置

<mime-mapping>   <extension>manifest</extension>   <mime-type>text/cache-manifest</mime-type>   </mime-mapping>        

最後,禁用本地機器的網絡,重新打開浏覽器并通路 Web 作業系統所在的網址,系統中的大部分應用程式依然可以正常工作,如圖 11 所示。

圖 11. 離線系統

基于HTML5建構Web作業系統

結束語

本文介紹了 Web 作業系統的基本知識,并與傳統的作業系統進行了比較,進而介紹了 HTML5 這種新技術為 Web 作業系統開發帶來的益處,并與傳統的 web 前端開發技術進行了比較,最後通過建構一個基本的 Web 作業系統詳細的展現了 Web 作業系統的基本模式和功能以及支撐其運作的 web 前端開發技術是如何實作其具體功能的。從本文的讨論中可以看出,基于 HTML5 的 Web 作業系統是未來的一大趨勢,必将逐漸走入人們的日常生活工作中去。