使用Chrome瀏覽器的同學(xué)應(yīng)該會(huì)經(jīng)常看到一些網(wǎng)站的搜索框右邊有一個(gè)麥克風(fēng)的標(biāo)志,點(diǎn)擊圖標(biāo)后即可連接麥克風(fēng)進(jìn)行語(yǔ)音識(shí)別搜索,那么這是如何實(shí)現(xiàn)的呢?
其實(shí)非常簡(jiǎn)單,這是html5下一個(gè)新的屬性,通過(guò)調(diào)用google的語(yǔ)音服務(wù)api,用于支持網(wǎng)頁(yè)內(nèi)的語(yǔ)音識(shí)別輸入功能。
只需要在輸入框的input上添加一個(gè)x-webkit-speech 屬性就可以了。
如本博客的搜索框:
<input type="text" name="edtSearch" x-webkit-speech id="edtSearch" value="Search...." onfocus="if (this.value == 'Search....') {this.value = '';}" onblur="if (this.value == '') {this.value = 'Search....';}" />
另外它還可以添加以下屬性:
語(yǔ)言種類: lang="zh-CN"
發(fā)聲改變時(shí)觸發(fā)相關(guān)語(yǔ)音識(shí)別:onwebkitspeechchange="foo()"
讓語(yǔ)音輸入的內(nèi)容更加精確盡量接近搜索內(nèi)容:x-webkit-grammar="bUIltin:search"
通過(guò)平時(shí)的使用感覺(jué)如果發(fā)音清晰的話,識(shí)別率還算不錯(cuò)。這是google應(yīng)用于Chrome瀏覽器上的一項(xiàng)實(shí)驗(yàn)性功能,以上屬性同樣也對(duì)webkit內(nèi)核瀏覽器生效。
相關(guān)W3C標(biāo)準(zhǔn)參考文檔:http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/api-draft.html
早在公元2011年6月3日傍晚,人人網(wǎng)推出了一個(gè)很裝B且完全無(wú)視IE瀏覽器的功能——拖拽上床。哦,Sorry, 是拖拽上傳。本文將重點(diǎn)介紹實(shí)現(xiàn)拖拽上傳的幾個(gè)HTML5技術(shù):Drag&Drop、FileReader API和FormData。
關(guān)于這個(gè)拖拽上傳,其實(shí)國(guó)外有很多網(wǎng)站已經(jīng)有這樣的應(yīng)用,最早推出拖拽上傳應(yīng)用的是Gmail,它支持標(biāo)準(zhǔn)瀏覽器下拖拽本地文件到瀏覽器中作為郵件的附件發(fā)送。人人網(wǎng)的這個(gè)拖拽上傳也是同理,可以讓使用標(biāo)準(zhǔn)瀏覽器的用戶通過(guò)簡(jiǎn)單的拖拽行為,將本地文件夾中的照片直接上傳到人人網(wǎng),用戶體驗(yàn)?zāi)艿玫教嵘耐瑫r(shí),也希望借此機(jī)會(huì)推廣一下標(biāo)準(zhǔn)瀏覽器,淘汰IE。人人網(wǎng)當(dāng)時(shí)也向廣大用戶推出升級(jí)瀏覽器活動(dòng),并喊出口號(hào):”工欲善其計(jì)算機(jī),必先利其瀏覽器”。本次拖拽上傳的宣傳口號(hào):你敢”脫”,我就敢上傳…
言歸正題,首先看看效果,大家如果有人人網(wǎng)帳號(hào)的話可以在首頁(yè)試一試拖拽上傳功能,下面是演示視頻:
拖拽上傳應(yīng)用主要使用了以下HTML5技術(shù):
關(guān)于Drag&Drop拖拽事件,之前我寫過(guò)一篇專門介紹的文章《給力的 Google HTML5 訓(xùn)練營(yíng)(HTML5 Drag&Drop 拖拽、FileReader實(shí)例教程)》,那篇文章詳細(xì)講解了Drag & Drap事件的原理和代碼實(shí)例,這里的拖拽上傳實(shí)現(xiàn)原理基本上是一樣的,大家有興趣或不太了解的話可以先看看那篇文章,我在這里就不再過(guò)多啰嗦了~下面直接出拖拽上傳簡(jiǎn)要代碼實(shí)例:
//拖進(jìn)
oDragWrap.addEventListener(’dragenter’, function(e) {
e.preventDefault();
}, false);
//拖離
oDragWrap.addEventListener(’dragleave’, function(e) {
dragleaveHandler(e);
}, false);
//拖來(lái)拖去 , 一定要注意dragover事件一定要清除默認(rèn)事件
//不然會(huì)無(wú)法觸發(fā)后面的drop事件
oDragWrap.addEventListener(’dragover’, function(e) {
e.preventDefault();
}, false);
//扔
oDragWrap.addEventListener(’drop’, function(e) {
dropHandler(e);
}, false);
var dropHandler = function(e) {
//將本地圖片拖拽到頁(yè)面中后要進(jìn)行的處理都在這
}
在之前那篇文章中我也有介紹過(guò)關(guān)于File API中的FileReader接口,作為 File API 的一部分,FileReader 專門用于讀取文件,根據(jù) W3C 的定義,F(xiàn)ileReader 接口 “提供一些讀取文件的方法與一個(gè)包含讀取結(jié)果的事件模型”。關(guān)于FileReader的詳細(xì)介紹和代碼實(shí)例大家可以先去看看那篇文章。
今天我著重介紹一下File API中的FileList接口,它主要通過(guò)兩個(gè)途徑獲取本地文件列表,一是<input type=”file”>的表單形式,另一種則是e.dataTransfer.files拖拽事件傳遞的文件信息。很顯然,我們這里會(huì)用到后者。
使用files方法將會(huì)獲取到拖拽文件的數(shù)組形勢(shì)的數(shù)據(jù),每個(gè)文件占用一個(gè)數(shù)組的索引,如果該索引不存在文件數(shù)據(jù),將返回null值。可以通過(guò)length屬性獲取文件數(shù)量.
拖拽上傳需要注意的是需要判斷兩個(gè)條件,1:拖拽的是文件不是頁(yè)面中的元素; 2:拖拽的是圖片而不是其它文件,可以通過(guò)file.type屬性獲取文件的類型
下面讓我們來(lái)看看如何結(jié)合之前的拖拽事件來(lái)實(shí)現(xiàn)拖拽圖片并在頁(yè)面中進(jìn)行預(yù)覽:
//獲取文件列表
var fileList = e.dataTransfer.files;
//檢測(cè)是否是拖拽文件到頁(yè)面的操作
if (fileList.length == 0) {return;};
//檢測(cè)文件是不是圖片
if (fileList[0].type.indexOf(’image’) === -1) {return;}
//實(shí)例化file reader對(duì)象
var reader = new FileReader();
var img = document.createElement(’img’);
reader.onload = function(e) {
img.src = this.result;
oDragWrap.appendChild(img);
}
reader.readAsDataURL(fileList[0]);
}
這里有一個(gè)簡(jiǎn)單的拖拽圖片預(yù)覽的Demo
這時(shí)你如果用FireBug等類似調(diào)試工具查看DOM的話,會(huì)看到<img>標(biāo)簽的src屬性是一個(gè)超長(zhǎng)的文件二進(jìn)制數(shù)據(jù),所以如果DOM有很多這類圖片,那就要當(dāng)心瀏覽器性能了,因?yàn)檫@些數(shù)據(jù)極大地?cái)U(kuò)充的頁(yè)面的代碼量,而每次頁(yè)面的reflow都會(huì)對(duì)瀏覽器形成很大的負(fù)擔(dān),So,如果這些圖片還在DOM中,那就盡量不要做動(dòng)畫或任何重繪操作,如果真的要做就盡量讓圖片脫離文檔流,讓其絕對(duì)定位比較靠譜。
補(bǔ)充:可以使用window.URL.createObjectURL(file)來(lái)獲取文件的URL(Chrome下用window.webkitURL.createObjectURL(file)),這種方式獲取的URL要比上面說(shuō)的readAsDataURL簡(jiǎn)短很多。而且可以省去使用FileReader。這里感謝BinBinLiao的留言建議:) 下面是使用readAsDataURL與createObjectURL生成的代碼對(duì)比:
優(yōu)化后的代碼:(紅色為優(yōu)化的代碼)
var fileList = e.dataTransfer.files; //獲取文件列表
var img = document.createElement(’img’);
//檢測(cè)是否是拖拽文件到頁(yè)面的操作
if (fileList.length == 0) {return;};
//檢測(cè)文件是不是圖片
if (fileList[0].type.indexOf(’image’) === -1) {return;}
reader.onload = function(e) {
img.src = this.result;
oDragWrap.appendChild(img);
}
reader.readAsDataURL(fileList[0]);
}
}
需要注意的是,window.URL.createObjectURL是有生命周期的,也就意味著你每用此方法獲取URL,其生命周期都會(huì)和DOM一樣,它會(huì)單獨(dú)占用內(nèi)存,所以當(dāng)刪除圖片或不再需要它是,記得用window.URL.revokeObjectURL(file)來(lái)釋放其內(nèi)存。當(dāng)然,如果你沒(méi)有釋放,刷新頁(yè)面也是可以釋放的。
既然已經(jīng)獲取到了拖拽到web頁(yè)面中圖片的數(shù)據(jù),下一步就是將其發(fā)送到服務(wù)器端了。
話說(shuō)HTML5時(shí)代之前,AJAX傳輸文件二進(jìn)制流數(shù)據(jù)是不可能完成的事情,而現(xiàn)在我們完全可以通過(guò)file.getAsBinary獲取文件的二進(jìn)制數(shù)據(jù)流,進(jìn)而將其當(dāng)做XHR的data數(shù)據(jù)傳送到后端,8過(guò)由于Chrome不支持file的getAsBinary方法,F(xiàn)F3.6+支持此方法。所以Chrome就要另尋它法了,這時(shí)我們發(fā)現(xiàn)XMLHttpRequest Level 2中的FormData接口完美解決了這個(gè)問(wèn)題,它可以很快捷的模擬Form表單數(shù)據(jù)并通過(guò)AJAX發(fā)送至后端,F(xiàn)ormData的支持情況是FF5及以上支持,Chrome12及以上支持。
file.getAsBinary獲取文件流很簡(jiǎn)單,但是要想上傳數(shù)據(jù),就要模擬一下表單的數(shù)據(jù)格式了,首先看看模擬表單的js代碼, FormData模擬表單數(shù)據(jù)時(shí)更是簡(jiǎn)潔,不用麻煩的去拼字符串,而是直接將數(shù)據(jù)append到formdata對(duì)象中即可:
xhr.open(”post”, url, true);
xhr.setRequestHeader(’Content-Type’, ‘multipart/form-data; boundary=’ + boundary);
if (window.FormData) {
//Chrome12+
var formData = new FormData();
formData.append(’file’, file);
formData.append(’hostid’, userId);
formData.append(’requestToken’, t);
data = formData;
} else if (file.getAsBinary) {
//FireFox 3.6+
data = “–” +
boundary +
crlf +
”Content-Disposition: form-data; ” +
”name=\”" +
’file’ +
”\”; ” +
”filename=\”" +
unescape(encodeURIComponent(file.name)) +
”\”" +
crlf +
”Content-Type: image/jpeg” +
crlf +
crlf +
file.getAsBinary() +
crlf +
”–” +
boundary +
crlf +
”Content-Disposition: form-data; ” +
”name=\”hostid\”" +
crlf +
crlf +
userId +
crlf +
”–” +
boundary +
crlf +
”Content-Disposition: form-data; ” +
”name=\”requestToken\”" +
crlf +
crlf +
t +
crlf +
”–” +
boundary +
’–’;
}
xhr.send(data);
首先表單數(shù)據(jù)headers頭信息需要以下兩項(xiàng):
發(fā)送時(shí)的post數(shù)據(jù)類似這樣:
ÿØÿà?JFIF?…這里是文件二進(jìn)制流…~iúoî5P%-vãîHü 4QHgÿÙ
————————-1323611763556
Content-Disposition: form-data; name=”hostid”
229421603
—————————–1323612996486
Content-Disposition: form-data; name=”requestToken”
369009193
————————-1323611763556–
好了,現(xiàn)在文件上傳成功后你就可以按照平常AJAX的操作來(lái)進(jìn)行后續(xù)處理了。
最后,再來(lái)總結(jié)一下拖拽上傳的技術(shù)要點(diǎn):
OK,拖拽上傳就講到這里,歡迎大家一起探討。
HTML5提供了一組API用來(lái)獲取用戶的地理位置,如果瀏覽器支持且設(shè)備具有定位功能,就能夠直接使用這組API來(lái)獲取當(dāng)前位置信息。
該API是navigator對(duì)象的一個(gè)屬性 – Geolocation。目前除了ie內(nèi)核瀏覽器外,其他瀏覽器的最新版本基本都支持Geolocation。同時(shí),移動(dòng)設(shè)備IOS 3.0+ 和 Android 2.0+ 系統(tǒng)也支持它,現(xiàn)在很多移動(dòng)設(shè)備的應(yīng)用加入了地理定位的元素。
那么我們接下來(lái)看如何使用Geolocation API:
一、檢查瀏覽器是否支持Geolocation
var hasGeolocation = !!(navigator.geolocation);
if(hasGeolocation){
alert(“瀏覽器支持hasGeolocation”);
}
二、navigator.geolocation 的方法:
* navigator.geolocation有三個(gè)方法,分別是getCurrentPosition()、watchPosition()和clearWatch()
getCurrentPosition()方法
* getCurrentPosition()方法檢索用戶的當(dāng)前位置,但只檢索一次。當(dāng)該方法被腳本調(diào)用時(shí),方法以異步的方式來(lái)嘗試獲取宿主設(shè)備的當(dāng)前位置。
* 該方法最多可以有三個(gè)參數(shù):
geolocationSuccess:帶回當(dāng)前位置的回調(diào)(callback)(必需的)
geolocationError:有錯(cuò)誤發(fā)生時(shí)使用的回調(diào)(可選的)
geolocationOptions:地理位置選項(xiàng)(可選的)
調(diào)用如下所示:
navigator.geolocation.getCurrentPosition(geolocationSuccess, geolocationError, geolocationOptions);
watchPosition()方法
* watchPosition()方法定期輪詢用戶的位置,查看用戶的位置是否發(fā)生改變。其最多可帶三個(gè)參數(shù)。
調(diào)用如下所示:
var watchPositionHandler = navigator.geolocation.watchPosition(geolocationSuccess, geolocationError, geolocationOptions);
clearWatch()方法
* clearWatch()方法終止正在進(jìn)行的watchPosition(),該方法只能帶一個(gè)參數(shù)。在調(diào)用時(shí),其找到之前已經(jīng)開(kāi)始了的watchID參數(shù)并立即停止它。
調(diào)用如下所示:
navigator.geolocation.clearWatch(watchID);
三、navigator.geolocation返回一個(gè)Position對(duì)象:
* Position對(duì)象有兩個(gè)屬性:timestamp和coords。timestamp屬性表示地理位置數(shù)據(jù)的創(chuàng)建時(shí)間,coords屬性又包含七個(gè)屬性:
coords.latitude:估計(jì)緯度
coords.longitude:估計(jì)經(jīng)度
coords.altitude:估計(jì)高度
coords.accuracy:所提供的以米為單位的經(jīng)度和緯度估計(jì)的精確度
coords.altitudeAccuracy:所提供的以米為單位的高度估計(jì)的精確度
coords.heading: 宿主設(shè)備當(dāng)前移動(dòng)的角度方向,相對(duì)于正北方向順時(shí)針計(jì)算
coords.speed:以米每秒為單位的設(shè)備的當(dāng)前對(duì)地速度
* 注意altitude, altitudeAccuracy, heading, speed不一定被瀏覽器支持,所以大家最好看一下官方規(guī)范的描述,多一些了解。
四、注意事項(xiàng)
* Geolocation App是不能直接訪問(wèn)設(shè)備的,只能通過(guò)請(qǐng)求瀏覽器來(lái)訪問(wèn)設(shè)備;
* Geolocation涉及到用戶隱私,在獲取 Geolocation 的時(shí)候,需要先征求用戶的意思。
* Geolocation目前沒(méi)有比較好的前端兼容解決方案,但是在移動(dòng)設(shè)備 iOS 和 Android上,我們可以大膽嘗試使用。
不得不佩服下谷歌Chrome團(tuán)隊(duì),利用HTML5和CSS3實(shí)現(xiàn)了一本相當(dāng)漂亮的在線電子書:《關(guān)于瀏覽器和互聯(lián)網(wǎng)20件事》。
訪問(wèn)地址:http://www.20thingsilearned.com
話說(shuō)這本電子書已經(jīng)出來(lái)很久了,不過(guò)今天來(lái)看依然覺(jué)得相當(dāng)?shù)馁潯N覀儫o(wú)需刷新頁(yè)面,就可以來(lái)回切換電子書頁(yè)面,這正是OPOA(One Page One Application)的完美體現(xiàn)。
現(xiàn)在正在學(xué)習(xí)關(guān)于history API這方面的東西,所以特別感興趣的是他們?nèi)绾问褂脀indow.history.pushState()和window.history.replaceState()來(lái)做頁(yè)面之間的不刷新切換。
今天查閱了一些資料,基本上對(duì)history API有了一個(gè)基本了解。
首先要說(shuō)的就是history是個(gè)全局,即window.history。看到這個(gè)變量名你一定很熟悉,因?yàn)榻?jīng)常可以看到用window.history.back()或者window.history.go(-1)來(lái)返回上一頁(yè)的JavaScript代碼。
所以history并不是什么新東西,在HTML4的時(shí)代,我們可以使用它的這幾個(gè)屬性和方法:
length:歷史堆棧中的記錄數(shù)。
back():返回上一頁(yè)。
forward():前進(jìn)到下一頁(yè)。
go([delta]):delta是個(gè)數(shù)字,如果不寫或?yàn)?,則刷新本頁(yè);如果為正數(shù),則前進(jìn)到相應(yīng)數(shù)目的頁(yè)面;若為負(fù)數(shù),則后退到相應(yīng)數(shù)目的頁(yè)面。
現(xiàn)在,HTML5為其又添加了以下2個(gè)方法:
pushState(data, title [, url]):往歷史堆棧的頂部添加一條記錄。data為一個(gè)對(duì)象或null,它會(huì)在觸發(fā)window的popstate事件(window.onpopstate)時(shí),作為參數(shù)的state屬性傳遞過(guò)去;title為頁(yè)面的標(biāo)題,但當(dāng)前所有瀏覽器都忽略這個(gè)參數(shù);url為頁(yè)面的URL,不寫則為當(dāng)前頁(yè)。
replaceState(data, title [, url]):更改當(dāng)前頁(yè)面的歷史記錄。參數(shù)同上。這種更改并不會(huì)去訪問(wèn)該URL。不過(guò)目前只有Safari 5.0+、Chrome 8.0+、Firefox 4.0+和iOS 4.2.1+支持。如果想兼容老瀏覽器的話,可以試試History.js,而且它還修正了一些bug。
當(dāng)然,在移動(dòng)平臺(tái)上,我們可以大膽嘗試html5的history API。ios3.0+ 和Android2.0+ 平臺(tái)的內(nèi)置瀏覽器對(duì)history都比較完美了,利用它我們可以web app更趨向與native app。
下面,推薦幾篇文章:
.Manipulating the browser history
.Session history and navigation
.Manipulating History for Fun & Profit
今天看到 twitter 上有人在分享 Min.us 這個(gè)網(wǎng)站。說(shuō)是只要把圖片進(jìn)去,就可以分享。是的,它確實(shí)是這樣。體驗(yàn)了一翻,無(wú)論從界面,還操作的方便性上說(shuō),這體驗(yàn)還真是很贊的。
不過(guò),我更在意的是,他用了什么樣的技術(shù)。想著今晚回來(lái)瞄下代碼。這下,汗的事來(lái)了。這頁(yè)面看起來(lái)不像前端寫的啊。亂七八糟的。真是看不下去了。不過(guò),為了更好的用戶體驗(yàn),看吧。從頁(yè)面的 HTML5 標(biāo)簽猜測(cè)到可能用到 HTML5 的某些 API,再查一下 JS 發(fā)現(xiàn)了這兩行:
function isValidBrowser() { var browser = BrowserDetect.browser; var version = BrowserDetect.version; var OS = BrowserDetect.OS; //alert(browser+','+version+','+OS); return ( (browser == 'Chrome' && version >= 6) || (browser == 'Firefox' && version >= 3.6) || (browser == 'Safari' && version >= 5.0 && OS == 'Mac') || ("FileReader" in self && "ondrag" in document) ); }
先來(lái)個(gè)預(yù)覽吧,你可以先玩玩,再接著往下面看:HTML5 文件拖放上傳
好吧,確定是使用HTML5。我們來(lái)看一下這種技術(shù)是怎么實(shí)現(xiàn)的吧。嗯,再回看上面的代碼,還有我提了這么多次,其實(shí),它就用到兩個(gè)技術(shù):
這里有兩篇比較詳細(xì)的文章,看(等看完我講的,你再去看吧):
現(xiàn)在 1:10 am 了,同學(xué)。明天還要上班。而具體講來(lái),還是需要很多時(shí)間滴。我們就簡(jiǎn)單一點(diǎn)。說(shuō)些應(yīng)該注意的吧。具體的實(shí)現(xiàn),你看我 DEMO 的代碼吧,和上面的文章吧。
一、 瀏覽器檢測(cè) & 支持
支持的瀏覽器并不多,我測(cè)試的是 Filefox 和 Chrome, winSafari 5.0.2 沒(méi)通過(guò)。IE8 以下就更不用說(shuō)了,當(dāng)然,Opera 也沒(méi)有支持。
if (window.FileReader) { // 做事的 } else { alert('你的瀏覽器不支持啊,同學(xué)') }
從 Minus 的代碼中可以看出,支持的有:
其他的,就是浮云了。不過(guò),這已經(jīng)足夠了,因?yàn)槲覀兛梢杂?hack 來(lái)實(shí)現(xiàn)。我們這里,就講可以用的吧。不可以的,就用原來(lái)項(xiàng)目中使用的方法吧。
二、注意事項(xiàng)
默認(rèn)情況下,你向 Firefox 拖進(jìn)一張圖片,他可能會(huì)重定向于新頁(yè)面。或者拖過(guò)一個(gè)鏈接,可能會(huì)讓頁(yè)面重定向。我想,你一定不會(huì)讓你的用戶一邊上傳,一邊又打開(kāi)一個(gè)本地文件頁(yè)面,然后讓他再跑回來(lái)上傳的頁(yè)面。那就,阻止瀏覽器默認(rèn)行為是非常有必要的。
你可能發(fā)現(xiàn)了,在 DEMO 當(dāng)我們把東西拖進(jìn)容器的時(shí)候,容器的邊框樣式變成虛線。因?yàn)槲疫@里綁定了一個(gè)事件,當(dāng)文件進(jìn)入目標(biāo)是,添加一個(gè) dragenter 的 CLASSNAME。這里需要注意的事,減少事件的解法,你可以在事件 ondragenter 的時(shí)候,添加 CLASSNAME ,而 ondragleave 的時(shí)候,刪除 CLASSNAME。但綁定在 dragover 上,他就像 mouseover ,在拖動(dòng)的過(guò)程中不斷解發(fā),這對(duì)于瀏覽器的負(fù)擔(dān)就很大了。可能會(huì)導(dǎo)致一些亂七八糟的,類似于崩潰之類的事。
你可能需要用 e.dataTransfer.setData 和 e.dataTransfer.getData 來(lái)決定是否上傳用戶拖放的文件。
檢測(cè)狀態(tài),以便下一步操作,而 result 則可以實(shí)現(xiàn)本地預(yù)覽的功能,讓用戶知道自己要上傳的東西是什么。
FileReader.readAsBinaryString(fileBlob)
- result
屬性會(huì)包含一個(gè)文件的二進(jìn)制的格式FileReader.readAsText(fileBlob, opt_encoding)
- result
屬性將會(huì)包含一個(gè)文件的文本格式,默認(rèn)解碼參數(shù)是 “utf-8”。FileReader.readAsDataURL(file)
- result
將會(huì)包含一個(gè)文件的 DataURL 格式(圖片通常用這種方式)其實(shí)我還沒(méi)有見(jiàn)到國(guó)內(nèi)用上這個(gè)技術(shù)的網(wǎng)站。剛才瞄了一下 QQ 微博。也沒(méi)有。對(duì)于這些新技術(shù)。于我們前端開(kāi)來(lái),其實(shí)挑戰(zhàn)并不是會(huì)不會(huì)使用,而是,如何去使用。
比如,在支付寶,下次需要改版用戶上傳身份證認(rèn)證的時(shí)候,或許我會(huì)去推動(dòng)這樣的改進(jìn)。又比如,如果是我在做 QQ 微博,或者任何一個(gè)微博,我將會(huì)提供一個(gè)讓用戶拖讓一張圖片就能上傳的功能。
HTML5 能用到的地方太多了。就慢你沒(méi)想法。因?yàn)閯e人其實(shí)并不清楚,連最清楚的你,前端,都不去推動(dòng),還能有誰(shuí)去推動(dòng)呢?
好吧。在接下來(lái)的項(xiàng)目中,你,或許也可以試試。
1.Let's Crate
Let's Crate,采用了較新的手藝,拖曳就能快速上傳文件(但需要瀏覽器的撐持,建議使用谷歌瀏覽器)的一款國(guó)外網(wǎng)盤,簡(jiǎn)單易用,它不需要注冊(cè),打開(kāi)頁(yè)面后,直接把要上傳的文件拖曳到頁(yè)面中心的木箱里,完成后,會(huì)生成文件分享鏈接。還有它會(huì)提醒你注冊(cè)一個(gè)帳戶。若是不想注冊(cè),那么復(fù)制分享鏈接,但只能使用30 分鐘,相當(dāng)于的用于姑且分享的,建議注冊(cè),注冊(cè)后可以看到你今朝成立的文件夾數(shù)、上傳文件總數(shù)以及可使用的空間,今朝免費(fèi)帳戶可以使用的容量為 200MB。
網(wǎng)址:
http://www.letscrate.com/
2.Ge.tt
Ge.tt,一個(gè)設(shè)計(jì)精練清雅文件存儲(chǔ)分享網(wǎng)盤,上傳速度很快。它最年夜的特點(diǎn)在于上傳存儲(chǔ)分享極其精練便捷,就跟它精練的域名一樣,Ge.tt頁(yè)面只有一個(gè)精明的上傳文件的按鈕。
它為你供給無(wú)限量的收集空間,免費(fèi)用戶撐持保留文件30天,撐持批量上傳,可是是經(jīng)由過(guò)程一路選擇當(dāng)?shù)囟鄠€(gè)文件上傳,不需要你一個(gè)一個(gè)添加文件,一次最多可以上傳30個(gè),上傳完成后系統(tǒng)會(huì)供給超短分享外鏈地址,也供給一鍵分享到Facebook和Twitter,當(dāng)你的鼠標(biāo)滑過(guò)此鏈接時(shí)可復(fù)制該鏈接與你的伴侶分享。
此外,您還可以經(jīng)由過(guò)程注冊(cè)獲得額外功能,如對(duì)下載次數(shù)的實(shí)時(shí)統(tǒng)計(jì)、更長(zhǎng)久的文件保留時(shí)刻。
網(wǎng)址:http://www.ge.tt/
3..Min.us
Min.us,帶給你全新上傳體驗(yàn)在線圖片存儲(chǔ),瀏覽,分享和打點(diǎn)工具。頁(yè)面很是精練,您只需把圖片拖拽到Min.us的頁(yè)面里,圖片就能自動(dòng)上傳。若是想成批上傳圖片,只需用鼠標(biāo)拔取多張一塊拖拽到Min.us即可。要考試考試此項(xiàng)功能,必需你的瀏覽器是Firefox 3.6+, Chrome, Safari, IE9瀏覽器。否則,會(huì)呈現(xiàn)指定文件上傳的上傳頁(yè)面。圖片上傳后Min.us會(huì)自動(dòng)完成而且生成相冊(cè)鏈接,便利您分享給伴侶,有作為編纂用(Edit)鏈接,有用來(lái)分享的(Viewer)鏈接。在每個(gè)圖片正下方有一個(gè)鏈接(Link to image)便利你將上傳后的圖片貼到任何網(wǎng)站和博客中。
今朝Min.us撐持上傳的圖片名目有:JPEG, JPG, GIF, PNG, APNG,BMP。撐持單張7MB以下的圖片上傳,每個(gè)相冊(cè)圖片不跨越50張,上傳的圖片是永遠(yuǎn)保留的。
網(wǎng)址:http://min.us/
4.PicsEngine
PicsEngine,一款在線圖片打點(diǎn)存儲(chǔ)軟件,近似與桌面版的picasa,你也可以將它算作專注于圖片存儲(chǔ)的網(wǎng)盤。PicsEngine打點(diǎn)照片很是便利輕易,供給使用標(biāo)簽來(lái)智能打點(diǎn)相冊(cè)。它的整體界面設(shè)計(jì)很是酷的,供給了無(wú)限的存儲(chǔ)空間,而且可以從任何處所來(lái)進(jìn)行訪謁。
PicsEngine有一個(gè)強(qiáng)年夜的很新的功能,就是你可以直接從桌面拖動(dòng)照片到瀏覽器進(jìn)行上傳,當(dāng)然體驗(yàn)這一功能你需要安裝較高版本的網(wǎng)頁(yè)瀏覽器
導(dǎo)語(yǔ):前年圣誕節(jié)上,西班牙程序員Roman Cortes帶來(lái)了用純javascript腳本編寫的神奇3D圣誕樹(shù),令人印象深刻。2月14日情人節(jié)就要來(lái)臨了,還是Roman Cortes,這次他又帶來(lái)了用javascript腳本編寫的紅色玫瑰花。用代碼做出的玫瑰花,這才是牛逼程序員送給女友的最好情人節(jié)禮物呢!(提示:在不同瀏覽器下觀看效果、速度會(huì)有很大的不同)
圖片是由代碼生成,用戶可以刷新該頁(yè)面,重復(fù)觀看這朵玫瑰的呈現(xiàn)過(guò)程。
3D玫瑰花的實(shí)現(xiàn)代碼如下:
with(m=Math)C=cos,S=sin,P=pow,R=random;c.width=c.height=f=500;h=-250;function p(a,b,c){if(c>60)return[S(a*7)*(13+5/(.2+P(b*4,4)))-S(b)*50,b*f+50,625+C(a*7)*(13+5/(.2+P(b*4,4)))+b*400,a*1-b/2,a];A=a*2-1;B=b*2-1;if(A*A+B*B<1){if(c>37){n=(j=c&1)?6:4;o=.5/(a+.01)+C(b*125)*3-a*300;w=b*h;return[o*C(n)+w*S(n)+j*610-390,o*S(n)-w*C(n)+550-j*350,1180+C(B+A)*99-j*300,.4-a*.1+P(1-B*B,-h*6)*.15-a*b*.4+C(a+b)/5+P(C((o*(a+1)+(B>0?w:-w))/25),30)*.1*(1-B*B),o/1e3+.7-o*w*3e-6]}if(c>32){c=c*1.16-.15;o=a*45-20;w=b*b*h;z=o*S(c)+w*C(c)+620;return[o*C(c)-w*S(c),28+C(B*.5)*99-b*b*b*60-z/2-h,z,(b*b*.3+P((1-(A*A)),7)*.15+.3)*b,b*.7]}o=A*(2-b)*(80-c*2);w=99-C(A)*120-C(b)*(-h-c*4.9)+C(P(1-b,7))*50+c*2;z=o*S(c)+w*C(c)+700;return[o*C(c)-w*S(c),B*99-C(P(b, 7))*50-c/3-z/1.35+450,z,(1-b/1.2)*.9+a*.1, P((1-b),20)/4+.05]}}setInterval('for(i=0;i<1e4;i++)if(s=p(R(),R(),i%46/.74)){z=s[2];x=~~(s[0]*f/z-h);y=~~(s[1]*f/z-h);if(!m[q=y*f+x]|m[q]>z)m[q]=z,a.fillStyle="rgb("+~(s[3]*h)+","+~(s[4]*h)+","+~(s[3]*s[3]*-80)+")",a.fillRect(x,y,1,1)}',0) |
當(dāng)然,感興趣的人可以了解下面的實(shí)現(xiàn)過(guò)程與相關(guān)理論:
這朵三維代碼玫瑰的呈現(xiàn)效果采用了蒙特卡羅方法,創(chuàng)造者對(duì)蒙特卡羅方法非常推崇,他表示在功能優(yōu)化和采樣方面,蒙特卡羅方法是“令人難以置信的強(qiáng)大工具”。關(guān)于蒙特卡羅方法可以參考:Monte Carlo method 。
具體操作:
外觀采樣呈現(xiàn)效果繪制
我用了多個(gè)不同的形狀圖來(lái)組成這朵代碼玫瑰。共使用了31個(gè)形狀:24個(gè)花瓣,4個(gè)萼片,2個(gè)葉子和1根花莖,其中每一個(gè)形狀圖都用代碼進(jìn)行描繪。
首先,來(lái)定義一個(gè)采樣范圍:
function surface(a, b) { // I'm using a and b as parameters ranging from 0 to 1. return { x: a*50, y: b*50 }; // this surface will be a square of 50x50 units of size } |
然后,編寫形狀描繪代碼:
var canvas = document.body.appendChild(document.createElement("canvas")), context = canvas.getContext("2d"), a, b, position; // Now I'm going to sample the surface at .1 intervals for a and b parameters: for (a = 0; a < 1; a += .1) { for (b = 0; b < 1; b += .1) { position = surface(a, b); context.fillRect(position.x, position.y, 1, 1); } } |
這時(shí),看到的效果是這樣的:
現(xiàn)在,嘗試一下更密集的采樣間隔:
正如現(xiàn)在所看到的,因?yàn)椴蓸娱g隔越來(lái)越密集,點(diǎn)越來(lái)越接近,到最高密度時(shí),相鄰點(diǎn)之間的距離小于一個(gè)像素,肉眼就看不到間隔(見(jiàn)0.01)。為了不造成太大的視覺(jué)差,再進(jìn)一步縮小采樣間隔,此時(shí),繪制區(qū)已經(jīng)填滿(比較結(jié)果為0.01和0.001)。
接下來(lái),我用這個(gè)公式來(lái)繪制一個(gè)圓形:(X-X0)^ 2 +(Y-Y0)^ 2 <半徑^ 2,其中(X0,Y0)為圓心:
function surface(a, b) { var x = a * 100, y = b * 100, radius = 50, x0 = 50, y0 = 50; if ((x - x0) * (x - x0) + (y - y0) * (y - y0) < radius * radius) { // inside the circle return { x: x, y: y }; } else { // outside the circle return null; } } |
為了防止溢出,還要加上一個(gè)采樣條件:
if (position = surface(a, b)) { context.fillRect(position.x, position.y, 1, 1); } |
結(jié)果如下:
有不同的方法來(lái)定義一個(gè)圓,其中一些并不需要拒絕采樣。我并無(wú)一定要使用哪一種來(lái)定義圓圈的意思,所以下面用另一種方法來(lái)定義一個(gè)圓:
function surface(a, b) { // Circle using polar coordinates var angle = a * Math.PI * 2, radius = 50, x0 = 50, y0 = 50; return { x: Math.cos(angle) * radius * b + x0, y: Math.sin(angle) * radius * b + y0 }; } |
如圖:
(此方法相比前一個(gè)方法需要密集采樣以進(jìn)行填充。)
好了,現(xiàn)在讓圓變形,以使它看起來(lái)更像是一個(gè)花瓣:
function surface(a, b) { var x = a * 100, y = b * 100, radius = 50, x0 = 50, y0 = 50; if ((x - x0) * (x - x0) + (y - y0) * (y - y0) < radius * radius) { return { x: x, y: y * (1 + b) / 2 // deformation }; } else { return null; } } |
結(jié)果:
這看起來(lái)已經(jīng)很像一個(gè)玫瑰花瓣的形狀了。在這里也可以試試通過(guò)修改一些函數(shù)數(shù)值,將會(huì)出現(xiàn)很多有趣的形狀。
接下來(lái)應(yīng)該給它添加色彩了:
function surface(a, b) { var x = a * 100, y = b * 100, radius = 50, x0 = 50, y0 = 50; if ((x - x0) * (x - x0) + (y - y0) * (y - y0) < radius * radius) { return { x: x, y: y * (1 + b) / 2, r: 100 + Math.floor((1 - b) * 155), // this will add a gradient g: 50, b: 50 }; } else { return null; } } for (a = 0; a < 1; a += .01) { for (b = 0; b < 1; b += .001) { if (point = surface(a, b)) { context.fillStyle = "rgb(" + point.r + "," + point.g + "," + point.b + ")"; context.fillRect(point.x, point.y, 1, 1); } } } |
結(jié)果:
一片帶色的花瓣就出現(xiàn)了。
3D曲面和透視投影
定義三維表面很簡(jiǎn)單,比如,來(lái)定義一個(gè)管狀物體:
function surface(a, b) { var angle = a * Math.PI * 2, radius = 100, length = 400; return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius, z: b * length - length / 2, // by subtracting length/2 I have centered the tube at (0, 0, 0) r: 0, g: Math.floor(b * 255), b: 0 }; } |
接著添加投影透視圖,首先需要我們定義一個(gè)攝像頭:
如上圖,將攝像頭放置在(0,0,Z)位置,畫布在X / Y平面。投影到畫布上的采樣點(diǎn)為:
var pX, pY, // projected on canvas x and y coordinates perspective = 350, halfHeight = canvas.height / 2, halfWidth = canvas.width / 2, cameraZ = -700; for (a = 0; a < 1; a += .001) { for (b = 0; b < 1; b += .01) { if (point = surface(a, b)) { pX = (point.x * perspective) / (point.z - cameraZ) + halfWidth; pY = (point.y * perspective) / (point.z - cameraZ) + halfHeight; context.fillStyle = "rgb(" + point.r + "," + point.g + "," + point.b + ")"; context.fillRect(pX, pY, 1, 1); } } } |
效果為:
z-buffer
z-buffer在計(jì)算機(jī)圖形學(xué)中是一個(gè)相當(dāng)普遍的技術(shù),在為物件進(jìn)行著色時(shí),執(zhí)行“隱藏面消除”工作,使隱藏物件背后的部分就不會(huì)被顯示出來(lái)。
上圖是用z-buffer技術(shù)處理后的玫瑰。(可以看到已經(jīng)具有立體感了)
代碼如下:
var zBuffer = [], zBufferIndex; for (a = 0; a < 1; a += .001) { for (b = 0; b < 1; b += .01) { if (point = surface(a, b)) { pX = Math.floor((point.x * perspective) / (point.z - cameraZ) + halfWidth); pY = Math.floor((point.y * perspective) / (point.z - cameraZ) + halfHeight); zBufferIndex = pY * canvas.width + pX; if ((typeof zBuffer[zBufferIndex] === "undefined") || (point.z < zBuffer[zBufferIndex])) { zBuffer[zBufferIndex] = point.z; context.fillStyle = "rgb(" + point.r + "," + point.g + "," + point.b + ")"; context.fillRect(pX, pY, 1, 1); } } } } |
旋轉(zhuǎn)
你可以使用任何矢量旋轉(zhuǎn)的方法。在代碼玫瑰的創(chuàng)建中,我使用的是歐拉旋轉(zhuǎn)。現(xiàn)在將之前編寫的管狀物進(jìn)行旋轉(zhuǎn),實(shí)現(xiàn)繞Y軸旋轉(zhuǎn):
function surface(a, b) { var angle = a * Math.PI * 2, radius = 100, length = 400, x = Math.cos(angle) * radius, y = Math.sin(angle) * radius, z = b * length - length / 2, yAxisRotationAngle = -.4, // in radians! rotatedX = x * Math.cos(yAxisRotationAngle) + z * Math.sin(yAxisRotationAngle), rotatedZ = x * -Math.sin(yAxisRotationAngle) + z * Math.cos(yAxisRotationAngle); return { x: rotatedX, y: y, z: rotatedZ, r: 0, g: Math.floor(b * 255), b: 0 }; } |
效果:
蒙特卡羅方法
關(guān)于采樣時(shí)間,間隔過(guò)大過(guò)小都會(huì)引起極差的視覺(jué)感受,所以,需要設(shè)置合理的采樣間隔,這里使用蒙特卡羅方法。
var i; window.setInterval(function () { for (i = 0; i < 10000; i++) { if (point = surface(Math.random(), Math.random())) { pX = Math.floor((point.x * perspective) / (point.z - cameraZ) + halfWidth); pY = Math.floor((point.y * perspective) / (point.z - cameraZ) + halfHeight); zBufferIndex = pY * canvas.width + pX; if ((typeof zBuffer[zBufferIndex] === "undefined") || (point.z < zBuffer[zBufferIndex])) { zBuffer[zBufferIndex] = point.z; context.fillStyle = "rgb(" + point.r + "," + point.g + "," + point.b + ")"; context.fillRect(pX, pY, 1, 1); } } } }, 0); |
設(shè)置a和b為隨機(jī)參數(shù),用足夠的采樣完成表面填充。我每次繪制10000點(diǎn),然后靜待屏幕完成更新。
另外需要注意的是,如果隨機(jī)數(shù)發(fā)生錯(cuò)誤時(shí),表面填充效果會(huì)出錯(cuò)。有些瀏覽器中,Math.random的執(zhí)行是線性的,這就有可能導(dǎo)致表面填充效果出錯(cuò)。這時(shí),就得使用類似Mersenne Twister(一種隨機(jī)數(shù)算法)這樣的東西去進(jìn)行高質(zhì)量的PRNG采樣,從而避免錯(cuò)誤的發(fā)生。
完成
為了使玫瑰的每個(gè)部分在同一時(shí)間完成并呈現(xiàn),還需要添加一個(gè)功能,為每部分設(shè)置一個(gè)參數(shù)以返回值來(lái)進(jìn)行同步。并用一個(gè)分段函數(shù)代表玫瑰的各個(gè)部分。比如在花瓣部分,我用旋轉(zhuǎn)和變形來(lái)創(chuàng)建它們。
雖然表面采樣方法是創(chuàng)建三維圖形非常著名的、最古老的方法之一,但這種把蒙特卡羅、z-buffer加入到表面采樣中的方法并不常見(jiàn)。對(duì)于現(xiàn)實(shí)生活場(chǎng)景的制作,這也許算不上很有創(chuàng)意,但它簡(jiǎn)易的代碼實(shí)現(xiàn)和很小的體積仍令人滿意。
希望這篇文章能激發(fā)計(jì)算機(jī)圖形學(xué)愛(ài)好者來(lái)嘗試不同的呈現(xiàn)方法,并從中獲得樂(lè)趣。(Roman Cortes)
英文原址:romancortes.com