时间流互联网之未来(下)

2013/04/15 · HTML5 · 1
评论 ·
HTML5

来源:pingwest

2014年5个最流行前端框架对比

2015/01/08 · CSS,
HTML5 ·
bootstrap,
Foundation,
Pure, Semantic
UI,
UIkit,
前端框架

本文由 伯乐在线 –
kinolee
翻译,JustinWu
校稿。未经许可,禁止转载!
英文出处:www.sitepoint.com。欢迎加入翻译组。

当今时代众多CSS的前端框架纷涌而至,但真正的优秀的却屈指可数。

在这篇文章中我们将对我认为最好的五个框架进行比较,每个框架都有自己优缺点和特定的应用领域,这允许你根据特定项目的需求选择合适的框架。比如,如果你的项目比较简单,你就不需要复杂的框架,此外,许多选项是模块化的,这允许你仅使用你需要的组件,或者混合使用不同框架的组件。

我们要讨论的框架都是基于其在github上的人气展示的,首先说最流行的,当然是:Bootstrap。

(注意:下面的一些信息在未来的几周和几月后就过时了,如:GitHub
上的评分(Stars)和版本数,因此如果你是在这篇文章发布很久以后阅读的话,你需要留意这一点。此外,还要注意框架大小是否将必要的CSS和JS文件最小化。)

基于 HTML5 构建 Web 操作系统

2012/09/29 · HTML5,
JavaScript · 1
评论 ·
HTML5,
Javascript

来源:IBM
Developerworks

简介: Web
操作系统有着传统操作系统无法比拟的优势,如可以随时随地利用任何终端进行访问,数据保存在服务器端,空间更大,数据安全性更好,可以利用服务器端的
CPU、内存等资源进行更为复杂的运算。然而目前的 Web 操作系统前端大多基于
Flex、Silverlight、ActiveX
插件等技术开发,存在着对移动设备的支持性差,终端安全性差,开发难度大等缺点。

HTML5 是下一代 web
语言的标准,具有兼容性好,安全性高,功能丰富,开发便捷等优点,特别适合如
Web 操作系统一类的富客户端互联网应用的前端开发。本文将展示如何利用 HTML5
提供的多种新技术如:本地数据库、多线程开发、视频支持、离线编程等构建一个基本的
Web 操作系统。

简介

传统的操作系统有着一些难以克服的缺点,如仅能在本地终端访问,或仅支持有限的远程访问,限于本地终端的资源,计算能力薄弱,存储空间有限,缺乏强大的防火墙等一系列安全机制,安全性较差。鉴于以上缺点,Web
操作系统应运而生 – Web
操作系统是一种基于浏览器的虚拟的操作系统,用户通过浏览器可以在其中进行应用程序的操作,以及相关数据的存储。Web
操作系统提供的基本服务有文本文档的创建与存储,音频视频文件的播放与存储,提供对时间信息的支持等,更高级的服务则包含即时通信,邮件甚至游戏等服务。Web
操作系统克服了传统操作系统的缺点,在网络的支持下,它可以在任何时间,任何地点经由任何支持
Web
的终端进行访问,可以利用服务器端无限的计算及存储资源,用户数据保存在服务器端,安全性较高。

图片 1

相关技术

目前构建 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

XHTML

var iconHolder = document.createElement(“div”); iconHolder.id =
‘iconHolder’ + i; iconHolder.className = “iconHolder”;
mainDiv.appendChild(iconHolder);

1
2
3
4
var iconHolder = document.createElement("div");
iconHolder.id = ‘iconHolder’ + i;
iconHolder.className = "iconHolder";
mainDiv.appendChild(iconHolder);

HTML5 提供了对 drag 事件的支持,大大简化了实现拖拽的难度。通过对
dragstart 事件的监听,将被拖拽的应用程序图标所在的 div
记录下来,作为拖拽的源。

清单 2. 拖拽支持

XHTML

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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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. 桌面拖拽效果

图片 2程序打开

程序可以以两种方式打开,左键点击或通过上下文菜单打开。

通过监听 div 的 onclick 事件,获取要打开的应用程序 id,并利用 openApp
方法打开相应的应用程序可实现对左键点击的支持。

清单 3. 左键点击

XHTML

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

1
2
3
4
5
6
iconHolder.onclick =  function(ev){
if(ev.currentTarget.firstChild){
        openApp(ev.currentTarget.firstChild.id);
        ev.stopPropagation();
}
};

通过监听 div 的 oncontextmenu 事件,获取要打开的应用程序 id,并利用
openAppContextMenu
方法显示相应应用程序的上下文菜单,可实现对右键上下文菜单的支持。

清单 4. 上下文菜单

XHTML

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

1
2
3
4
5
6
7
iconHolder.oncontextmenu =  function(ev){
if(ev.currentTarget.firstChild){
        openAppContextMenu(ev.currentTarget.firstChild.id, ev);
        ev.stopPropagation();
}
return false;
};

利用相应应用程序的
id,可以获取对应应用程序的脚本,并执行,同时在系统日志中记录下相应的操作。

清单 5. 程序打开

XHTML

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);// 执行应用程序 }

1
2
3
4
5
6
7
8
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. 打开程序上下文菜单

XHTML

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’
onclick=\”appContextMenu.style.display=’none’;” +
apps[i].contextMenu[j].action + “\”
onmouseover=’this.style.background=\”darkblue\”‘
onmouseout=’this.style.background=\”#eee\”‘>”
+apps[i].contextMenu[j].name+”</div>”; } break; } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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’
                        onclick=\"appContextMenu.style.display=’none’;" +
                        apps[i].contextMenu[j].action + "\"
                        onmouseover=’this.style.background=\"darkblue\"’
                        onmouseout=’this.style.background=\"#eee\"’>"
                        +apps[i].contextMenu[j].name+"</div>";
                        }
                        break;
                 }  
}
}

应用程序的上下文菜单由名为 appContextMenu 的 div 实现,将 oncontextmenu
事件中的 clientX 及 clientY 作为上下文菜单出现的位置,并将其透明度设置为
0.5。利用相应应用程序的 id
获取上下文菜单对应的内容,并将其填充至上下文菜单。

图 2 显示了应用程序上下文菜单打开时的效果。

图 2. 应用程序上下文菜单

图片 3上下文菜单

桌面上下文菜单的实现方式与应用程序上下文菜单的实现方式基本类似,图 3
和图 4 分别是桌面以及任务栏的上下文菜单。

图 3. 桌面上下文菜单

图片 4

 图 4. 任务栏上下文菜单

图片 5视频播放器

系统提供了一个简单的视频播放器,它支持从系统外部拖拽视频文件进行播放。

顺应网络媒体的发展,HTML5 提供了视频标签 video
以便于加强对视频的支持,大大简化了 web
播放器开发的难度,开发人员仅凭几行代码,就可以开发出一个基本功能完善的视频播放器。

清单 7. 视频标签的创建

XHTML

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);

1
2
3
4
5
6
7
8
9
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. 添加拖拽支持

JavaScript

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); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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. 拖拽处理

JavaScript

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); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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. 视频播放

图片 6

本地存储

Web
操作系统通常将大部分数据存储于服务器端,这样做的好处显而易见,数据存储空间更大,安全性更好。然而这样做也有不足之处,由于网络的稳定性依然较本地磁盘差,所以在脱离网络的状况下,Web
操作系统无法获取相应的数据资源,因此 Web
操作系统需要一定的访问本地存储空间的能力,当然本地存储空间仅是作为服务器端存储的一个补充,它的空间有限,访问也受到一定的限制。

一直以来,HTML 以 Cookie
作为访问本地空间的方式,然而,这种方式有着很多缺点和不足,如存储的数据格式过于简单,通常仅为键值对;存储的空间大小有限。为此,HTML5
提供了本地数据库以增强本地存储空间的访问能力,它是一个简化版的数据库,能够支持模拟的
SQL 以及简单的事务处理等功能。

系统为支持本地存储,创建了一个名为 MyData 的数据库。清单 10
显示了数据库创建的过程。

清单 10. 创建数据库

XHTML

var db; var openDatabase; if(openDatabase != undefined) db =
openDatabase(‘MyData’, ”, ‘My Database’, 102400);

1
2
3
4
var db;
var openDatabase;
if(openDatabase != undefined)
     db = openDatabase(‘MyData’, ”, ‘My Database’, 102400);

其中 MyData 为数据库的名称,省略的参数为数据库的版本,My Database
为显示的名称,最后的数字为数据库预估长度(以字节为单位)。

系统日志将系统在某一时间的行为操作记录下来,本地数据库为其提供存储支持。日志在数据库中存储为表
History,包含 3 个字段,分别为时间,操作,及操作的详细信息。清单 11
显示了系统是如何记录日志的。

清单 11. 日志记录

XHTML

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); });
}); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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. 日志显示

XHTML

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); } }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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. 系统日志

图片 7

记事本

系统提供了一个简单的记事本,实现了文本文档的基本操作。文本文档包含标题和内容两个显式属性,以及一个名为
id
的隐式属性。与系统日志类似,本地数据库为文本数据的存储提供了底层的支持。图
7 显示了记事本程序的界面。

图 7. 记事本

图片 8

当编辑完文档的标题与内容后,点击左上角的保存按钮,将执行 createFile
函数。清单 13 显示了 createFile 函数的详细过程。

清单 13. 创建文件

XHTML

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); });
}); }); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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. 创建文档图标

XHTML

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 }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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. 文本文档

图片 9时钟

系统提供了一个简单的时钟用以显示当前时间,它由一个表盘以及分针和时针组成,能够随着时间的变化动态地变换。以往的
web 应用利用 JavaScript 或 Flash 完成此类功能,其复杂性可想而知。借助
HTML5 的 draw
API,可以轻松地画出所需的图形,极大的方便了此类应用的构建,此外,HTML5
还提供了以往 JavaScript 无法支持的多线程编程,大大加强了 web
应用的交互性和丰富性。

时钟有一个基本的表盘,它仅是一副简单的图片,如图 9 所示。

图 9. 表盘

图片 10

在表盘之上,建有一个 canvas( 画布 ),如清单 15 所示。

清单 15. 画布

JavaScript

<canvas id=”canvas” width=”128px” height=”128px”></canvas>

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

接下来,清单 17
将在画布上模拟出时钟以及分针,在这之前,额外需要一个后台线程用以计算时间,它被定义在名为
time.js 的独立脚本文件中,如清单 16 所示。

清单 16. 后台线程

XHTML

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

1
2
3
4
5
6
7
8
onmessage = function(event)
{
//var i = 1;
    setInterval(function() {
    //i++;
    postMessage("");
    }, 60000);
};

每过 60 秒钟,后台线程将会向前台线程发送一个空消息,以告诉前台线程有 60
秒钟已经过去了。

清单 17. 前台线程的初始化

XHTML

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();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 事件

XHTML

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(“”);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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. 时钟

图片 11离线支持

虽然 Web
操作系统的优点是可以利用网络随时随地进行访问。然而在无法访问网络的情况下,Web
操作系统便无法发挥作用。因此 Web
操作系统有必要在离线状态下,仍能对部分应用及其功能进行支持。事实上,各种浏览器已提供了各式各样的缓存机制以提供对离线应用的支持,然后这些缓存机制往往是临时性的,不可控的。HTML5
为开发人员提供了解决此问题的另一种途径,它提供了一种永久性的,自定义的缓存方法,使得
Web 操作系统可以在离线的状况下,依然支持部分应用的功能。

HTML5
离线支持的核心是一个缓存清单,其中列出了需要缓存的文件,本系统中的缓存文件
index.manifest,如清单 19 所示。

清单 19. 缓存清单

XHTML

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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. 应用缓存清单

XHTML

<html manifest=”index.manifest”>

1
<html manifest="index.manifest">

值得一提的是,若要支持离线缓存,除客户端浏览器的支持以外,服务端的支持也是必不可少的,就本系统所使用的
tomcat 而言,需要在其配置文件 web.xml 中添加清单 21 所示的条目。

清单 21. 服务器端缓存配置

XHTML

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

1
2
3
4
<mime-mapping>
<extension>manifest</extension>
<mime-type>text/cache-manifest</mime-type>
</mime-mapping>

最后,禁用本地机器的网络,重新打开浏览器并访问 Web
操作系统所在的网址,系统中的大部分应用程序依然可以正常工作,如图 11
所示。

图 11. 离线系统

图片 12结束语

本文介绍了 Web
操作系统的基本知识,并与传统的操作系统进行了比较,进而介绍了 HTML5
这种新技术为 Web 操作系统开发带来的益处,并与传统的 web
前端开发技术进行了比较,最后通过构建一个基本的 Web 操作系统详细的展现了
Web 操作系统的基本模式和功能以及支撑其运行的 web
前端开发技术是如何实现其具体功能的。从本文的讨论中可以看出,基于 HTML5
的 Web 操作系统是未来的一大趋势,必将逐步走入人们的日常生活工作中去。

赞 2 收藏 1
评论

图片 13

《时间流互联网之未来(上)》

1. Bootstrap

Bootstrap 在当今流行的各种框架中是无可争议的老大。鉴于其每天仍在增长的巨大人气,可以肯定,这个美妙的工具绝对不会让你失望,它也不会在你成功建立网站前就离你而去。

图片 14

  • 创建者: Mark Otto and Jacob
    Thornton.
  • 发布: 2011
  • 当前版本: 3.3.1
  • 人气: 在Github上有75,000+ stars
  • 描述: “Bootstrap是最流行的的 HTML, CSS和 JavaScript
    响应式开发框架 ,web上开发的第一个移动项目.”
  • 核心概念/原则: RWD 和移动优先
  • 框架大小: 145 KB
  • 预处理器: Less 和 Sass
  • 响应式: Yes
  • 模块化: Yes
  • 开始模板/布局: Yes
  • 图标设置: Glyphicons Halflings set
  • 附加/插件: 没有捆绑插件,但许多第三方插件可用.
  • 独特的组件: Jumbotron
  • 文档: 良好
  • 定制: 基本的GUI定制器。不幸的是,你需要手动输入的颜色值,因为没有可用的颜色选择器。
  • 浏览器支持: Firefox, Chrome, Safari, IE8+
    (你需要 Respond.js for IE8)
  • 许可证: MIT

Notes
on Bootstrap

  •  Bootstrap 的主要优点是它非常流行。从技术上讲,它并不一定比这次列出来的其他框架好,但它提供的资源(文章和教程、第三方插件和扩展、主题开发者等等)比其他四个框架的总和还要多。简而言之,Bootstrap无处不在。这是人们继续选择它的主要原因。
  • (注意:“独特的组件”的意思是,相比这里提到的其他框架是独一无二的。)

手机、Google Glass、智能手表、平板、电视……

移动互联网的发展,不仅让网络时间流特性更加明显、信息更加扁平化,同时对于普通大众来说,最大的变化便是多屏化趋势、以及每个屏幕信息扁平化展现形式的变化。而这最重要的一个原因,就是互联网从PC
走向手机、电视、手表等物联网时代的各种设备。本章我们就介绍一下各种大大小小的屏幕之间的差别、互联网内容在这些设备上的展现方式。

图片 15

在互联网还仅局限于PC
上之前,业内就有第一屏幕(电视)、第二屏幕(手机)、第三屏幕(PC)等等的说法。后来随着科技的发展,手机渐渐成为了第一屏幕,平板兴起并开始和电视争夺客厅的掌控权,而手表、眼镜也成了新兴屏幕,更别提Nest、冰箱、微波炉这些小屏幕了。

智能手机

移动互联网的发展,已经让智能手机成为了PC之外兼容互联网内容最优的设备,未来的网络内容展现形式和现在差异也不会太大。但手机的演化,也会使得未来我们使用手机的方式有些不同。

首先,手机屏幕越来越大,从3.5英寸到4英寸,从4英寸到5甚至5.5英寸,人们开始慢慢接受大屏幕。屏幕一大,自然呈现的信息就多,很多隐藏的很深的信息便可以直接放到桌面上,比如天气、信息、各种小插件等。这符合互联网时间流的变化,信息层级变浅,更多内容聚集到桌面上。

其次,触屏手机的流行,使得未来语音、智能传感器(如眼动仪、距离感应器等)得到发展。未来只需对着手机说句话,它就会告诉你任何信息,你不用去打开某款App,设备就可以自动从网络上找到你想要的东西。信息进一步扁平,HTML5网站内容可直接呈现在手机屏幕上。

平板电脑

平板电脑的流行也就这两三年的事(2010年iPad第一代发布),虽然之前也有各种Pad
概念和设备。平板延续了智能手机的操作系统,导致其易用性也比PC更高。相比PC
这种生产力工具,平板更像是一个生活设备,而大部分家庭需要的,也只是用来休闲娱乐的生活设备。

所以整体上,平板和智能手机差不多,在互联网内容兼容、内容展示方式、人机交互等方面都已经非常成熟了。随着互联网时间流的扁平化,未来的平板更像是一个信息流自动展示平台。

智能电视

电视以前是客厅的霸主,现在平板的出现让人们在客厅也有了其他的事情可做,但智能电视很有可能挽回这种局面。和平板一样,智能电视也是一个互联网的时间流展示平台:所有内容在人们不看电视的时候自动滚动一些信息,比如天气、新闻、社交好友照片、新信息、分享内容等。而且智能电视的视讯系统和手势操控,能分别控制客厅视频通话和游戏这两大项目,在电视前和家人视频聊天、玩游戏是再自然不过的事了。

在网络信息流展示形式上,电视相比平板其呈现内容更少(虽然屏幕大但距离远),所以电视上的内容更偏向于后面的要讲的眼镜,即卡片式展示。除了语音和手势控制,电视上都是以大块内容展示的,比如一个电影海报可能就占据了半个屏幕,一场球赛直播可能就占据了3/4甚至更多的屏幕。但从目前智能电视的界面设计看,还没有做的很好的展现方式。

智能眼镜(Google Glass)

虽然本段我们以Google
Glass为例,但这里要声明的是:智能眼镜并不单独指Google
Glass这种形式。Google
Glass在智能眼镜研发上做的很好,但不排除未来会有其他的形式(比如Vuzix
的Smart Glasses M100),而且微软、索尼、Bluester
等竞争对手的加入,可能也会让智能眼镜领域拥有更多样性的生态。

在互联网时间流概念上,Google Glass
的信息展现方式可以说是最典型的了:Timeline Card 。Glass
的每一个信息都是一个小卡片,并以时间为排序方式。卡片上可以显示天气、时间、照片、短消息、视频通话等等,Google
把互联网内容进行了分解,社交好友的一条状态、Instagram上的一张图片都只放在一张卡片上,这样信息更加集中,也不会给用户造成干扰。

Google
Glass的这种“时间流卡片”的展示方式可以用在很多设备上,比如智能电视、智能手表、冰箱、洗衣机、Nest智能家居检测仪等。由于可是屏幕足够小,所以这种方式比较适合。而且HTML5的跨平台特性,也能让网络资源在这些小屏幕设备上很好展现。

智能手表

智能手表有很多,它可能也会像智能眼镜一样成为各大科技厂商的战略性产品,而且由于智能手表门槛要比眼镜低,可能更会有一批小厂商也涌进来。如果我们拿Pebble 为例,就可以知道手表屏幕很小,只能显示很少的内容,所以Timeline
Card的形式也适用,在此我们就不多说了。

但智能手表和智能眼镜都面临着同样的一个问题:由于体积太小,导致硬件受限,很难开发一个独立的操作系统,只能通过连接到手机成为一个显示屏。(虽然Google
Glass是独立运行的,但未来不保证其他眼镜也能解决这一问题。)

PC

互联网就是在这上面诞生的,PC
的价值毋庸置疑。但科技的发展,已经让PC慢慢退居二线了,我们现在绝大部分操作都可以在移动终端上完成,未来也不保证不会完全抛弃PC
。有人说,现在的PC 不应该叫PC 了,应该叫WC (Work Computer)。确实,PC
现在基本上成为了一个生产力工具,人们下班之后基本都不会摸电脑了,更何况对于老人孩子还非常难用。

 

2. Foundation by ZURB

Foundation是这几个框架中第二大的,在像ZURB一样实力雄厚的公司支持下,这个框架确实很强大,是的,就是foundation。毕竟, Foundation已经在很多大型网站上投入使用,包括 Facebook,
Mozilla, Ebay, Yahoo!和国家地理等等。

图片 16

  • 创建者: ZURB
  • 发布: 2011
  • 当前版本:5.4.7
  • 人气: 在Github上有18,000+ stars
  • 描述: “世界上最优秀的响应式前端框架”
  • 核心概念/原则: RWD 、手机优先、语义的
  • 框架大小: 326KB
  • 预处理器: Sass
  • 响应式: Yes
  • 模块化: Yes
  • 开始模板/布局: Yes
  • 图标设置: Foundation Icon
    Fonts
  • 附加/插件: yes
  • 独特的组件: Icon Bar, Clearing Lightbox, Flex Video, Keystrokes,
    Joyride, Pricing Tables
  • 文档: 良好,还有许多额外的资源是可用的。
  • 定制: 没有GUI编辑器,只能手工定制。
  • 浏览器支持: Chrome, Firefox, Safari, IE9+; iOS, Android,
    Windows Phone 7+
  • 许可证: MIT

Facebook Home、HTC BlinkFeed功能、SO.HO……

Facebook 发布了一款基于Android 的深度应用 Facebook
Home,到现在国内外仍不乏许多媒体称赞Mark
Zuckerberg的明智之举。虽然我不认为Facebook
Home称得起如此大的赞誉,但它却实实在在地可以说是互联网时间流形态很好的例子。本文除了介绍Facebook
Home ,还会介绍HTC
Sence UI的BlinkFeed、第三方应用SO.HO 以及和互联网时间流理论类似的简单演示 lifestreams.com等。

图片 17

Facebook Home

关于Facebook Home
,PingWest之前的几篇文章有过介绍。它是一款Android手机深度定制App,相当于在应用层和系统层中间加了一层。用户的直接使用感受就是:你的Facebook
好友图片信息流都会在主屏幕显示,你可以滑动桌面浏览Facebook好友照片;当有短信、消息或Facebook
Message时,可以直接在主屏幕上进行回复、删除、存档等操作;与Facebook好友聊天也可以指直接在桌面上进行;另外Facebook
Home的 Launcher允许用户选取几个最常用的进行快速打开或使用。

可以看出,Facebook已经把互联网信息流(只有Facebook内容)直接推到用户的主屏幕上了,用户每时每刻都在浏览Facebook上的信息,而Facebook
的Newsfeed恰恰又是一时间流的形式排序的,所以这是一个非常典型的互联网时间流应用形式。

但互联网时间流并不只是newsfeed这一种类型,它还包括消息流、任务流等更细分的内容。Facebook
Home也把消息流很好的给用户展现出来了,但做的并不完美,而且Facebook
Home毕竟只是Facebook
一家的产品,未来用户桌面的互联网时间流会集成Facebook、Twitter、纽约时报、邮件等各种各样的内容。具体有哪些,完全依靠用户添加。

HTC Sence UI的BlinkFeed 功能

二月中下旬,当看到HTC Sence 5.0上的BlinkFeed
功能之后,我的心情相当激动,这就是互联网时间流的真正展现形式。BlinkFeed将你的社交网络、新闻资讯、生活娱乐、照片视频等各种信息一网打尽,直接在你的主屏幕上显示出来。BlinkFeed的信息源来自全球1400多家媒体和网站,同时还可以显示日历等用户信息,基本满足了所以用户的要求。

可能很多人并不习惯这样的浏览方式,而且直接把互联网信息推送到桌面上,对网速、手机续航、内存等也是一个压力。而且如果订阅的信息流很多的话,便会出现信息爆炸的情况,导致用户根本看不过来。不过这些问题都有各种办法来解决或优化,最后一章我们会详细介绍。

HTC的 Sence
UI本身就不错(现在手机也越来越漂亮了),而在手机的交互设计上,这家公司也越来越大胆。BlinkFeed
这种形式虽然现在还有点超前,但随着系统的不断迭代,以后会越来越符合人们的使用习惯。

lifestreams.com

lifestreams.com是一个互联网时间流概念的一个简单Demo,它本身也是根据David
Gelernter的理论设计的。其采用了Google Glass 的 Timeline
Card形式,并根据时间流排序。我们在前一章也介绍过,Timeline Card
这种形式非常适合智能手表、眼镜或其他的小屏幕设备,但在手机或平板上这种展示方式并不合适。

lifestreams.com 虽然严格依据David
Gelernter的时间流互联网概念,但还是太偏理论。不过这种形式基本上给出了未来网络信息展示形态的基本样式,以后的网络信息流会以此为基本形式进行各种更高级的演化。

第三方应用SO.HO

SO.HO是一款Android
App,准确地说,是一款Android启动器(Launcher)。在Google
Play上线也只有一个月左右的时间,和Facebook
Home差不多,SO.HO也是让你在屏幕上直接显示Facebook上的内容,同时还支持Twitter。产品做得很糙,但已经有点未来的意思了。

SO.HO上线两天,Google
Play下就有人评论说:“可以加上Google+、多账户twitter、Foursquare、Instagram、tumblr、RSS”。显然,大家想得都一样,我们可以把手机桌面上塞满各种我们想要的信息……

正如前面在BlinkFeed
那段我们提到的一样,把所有信息都放在一个屏幕上显然很不靠谱。我们应该对其进行合理的分类,比如社交信息、新闻资讯一个屏幕,短信、消息、邮件等一个屏幕,专业内容(工作需要)的东西再放一个屏幕(如BlackBerry
10的Balance生活工作切换功能),这样就简单多了。

 

Notes on Foundation

Foundation 是一个提供业务支持、培训和咨询真正专业的框架。它还提供了很多资源帮助你更快和更容易学习和使用框架。

发表评论

电子邮件地址不会被公开。 必填项已用*标注