用Web socket和Node.js實(shí)現(xiàn)HTML 5畫(huà)布的實(shí)時(shí)繪圖
譯文Web套接字(Web socket)和畫(huà)布(Canvas)是目前實(shí)施到瀏覽器中的兩項(xiàng)超酷的特性。本教程將簡(jiǎn)要介紹這兩者是如何工作的,并且制作了基于Node.js和Web套接字的實(shí)時(shí)畫(huà)布。為了簡(jiǎn)潔起見(jiàn),我將使用CoffeeScript來(lái)編寫(xiě)所有代碼。如果你青睞用傳統(tǒng)的JavaScript來(lái)編寫(xiě),只要看一下相應(yīng)的.js文件。出于同樣的原因,我也棄用了CSS。
從GitHub下載代碼(https://github.com/wesbos/websocket-canvas-draw)
介紹詳細(xì)內(nèi)容的屏幕播放速成教程
http://www.youtube.com/watch?feature=player_embedded&v=n7wQsLu_k00
跨設(shè)備/瀏覽器的兼容性
http://www.youtube.com/watch?v=oI9AahO9vDY&feature=player_embedded
服務(wù)器端
我們要做的第一件事就是建立一臺(tái)Web套接字服務(wù)器。為此,我們要用到Node.js和模塊Socket.io。Socket.io使得搭建和運(yùn)行Web套接字服務(wù)器的任務(wù)超容易完成。它甚至為不支持原生Web套接字的瀏覽器提供了可退回到Flash的功能(Flash fallback)。在本教程中,我們將只使用支持元素的瀏覽器。
如果你之前還沒(méi)有安裝Socket.io,就要確保已安裝,為此需要往你的終端中輸入npm install socket.io。
現(xiàn)在,我們不妨建立Web套接字服務(wù)器。創(chuàng)建server.coffee文件,采用下列配置。
io = require('socket.io').listen(4000)
io.sockets.on 'connection', (socket) ->
編譯你的coffeescript,重新回到你的終端,輸入node server.js.?,F(xiàn)在你已有了一臺(tái)通過(guò)端口4000運(yùn)行的Web套接字服務(wù)器。
如果你進(jìn)入到本地主機(jī):4000,會(huì)看到下列結(jié)果:
客戶端
首先,我們不妨立馬讓index.html文件搭建和運(yùn)行起來(lái)。除了一些基本的標(biāo)記外,我還加入了jQuery、現(xiàn)在從我們這個(gè)服務(wù)器提供的Socket.io JS文件、面向拖動(dòng)事件的jQuery插件以及我們自己的scripts.js文件。
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta charset="UTF-8">
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
- <script type="text/javascript" src="js/jquery.event.drag-2.0.js"></script>
- <script src="http://localhost:4000/socket.io/socket.io.js"></script>
- <script type="text/javascript" src="scripts.js"></script>
- <link rel="stylesheet" href="style.css" />
- <title>HTML5 Canvas + Node.JS Socket.io</title>
- </head>
- <body>
- <article><!—我們的畫(huà)布將在此插入--></article>
- <!-- Scripts required -->
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
- <script type="text/javascript" src="js/jquery.event.drag-2.0.js"></script>
- <script src="http://localhost:4000/socket.io/socket.io.js"></script>
- <script type="text/javascript" src="scripts.js"></script>
- </body>
由于我們的服務(wù)器已搭建和運(yùn)行起來(lái),我們可以編寫(xiě)用來(lái)畫(huà)到畫(huà)布的一些代碼。創(chuàng)建一個(gè)新文件,名為scripts.coffee。下面的所有代碼都在App.init()方法里面執(zhí)行,一旦jQuery文檔準(zhǔn)備就緒,我們就觸發(fā)該方法。
創(chuàng)建我們的Canvas元素
#構(gòu)建我們的應(yīng)用程序,它有自己的命名空間
- App = {}
- ###
- 初始化
- ###
- App.init = ->
- App.canvas = document.createElement 'canvas' #創(chuàng)建<canvas>元素
- App.canvas.height = 400
- App.canvas.width = 800 #加大尺寸
- document.getElementsByTagName('article')[0].appendChild(App.canvas) #把它附加到DOM中
- AppApp.ctx = App.canvas.getContext("2d") # 存儲(chǔ)上下文
- # 為我們的線條畫(huà)設(shè)置一些參數(shù)
- App.ctx.fillStyle = "solid"
- App.ctx.strokeStyle = "#bada55"
- App.ctx.lineWidth = 5
- App.ctx.lineCap = "round"
- # 繪圖函數(shù)
- App.draw = (x,y,type) ->
- if type is "dragstart"
- App.ctx.beginPath()
- App.ctx.moveTo(x,y)
- else if type is "drag"
- App.ctx.lineTo(x,y)
- App.ctx.stroke()
- else
- App.ctx.closePath()
- return
#p#
畫(huà)到畫(huà)布函數(shù)
由于畫(huà)到畫(huà)布需要建立、移動(dòng)和關(guān)閉路徑,我創(chuàng)建了一個(gè)短小的函數(shù),用來(lái)連接到j(luò)Query拖動(dòng)開(kāi)始事件和拖動(dòng)事件。
- # 繪圖函數(shù)
- App.draw = (x,y,type) ->
- if type is "dragstart"
- App.ctx.beginPath()
- App.ctx.moveTo(x,y)
- else if type is "drag"
- App.ctx.lineTo(x,y)
- App.ctx.stroke()
- else
- App.ctx.closePath()
- return
設(shè)置客戶端Web套接字
由于我們已將文件添加到http://localhost:4000/socket.io/socket.io.js,我們就能創(chuàng)建一個(gè)對(duì)象,以便通過(guò)該對(duì)象發(fā)送數(shù)據(jù)。只編寫(xiě)了幾行代碼,我們就創(chuàng)建了App.socket對(duì)象,并且綁定到名為“draw”的任何入站W(wǎng)eb套接字事件。我們稍后對(duì)此會(huì)有介紹。
- # 套接字!
- App.socket = io.connect('http://localhost:4000')
- App.socket.on 'draw', (data) ->
- App.draw(data.x,data.y,data.type)
畫(huà)布繪圖事件
激動(dòng)人心的一刻就出現(xiàn)在這里。現(xiàn)在我們想把幾個(gè)事件綁定到我們的元素。其工作方式是,有人在畫(huà)布上繪圖時(shí),我們立即使用draw()函數(shù),畫(huà)到當(dāng)前畫(huà)布上,并且使用socket.io的emit,通過(guò)Web套接字發(fā)送x和y坐標(biāo)。稍后我們會(huì)看一下這個(gè)事件的服務(wù)器端部分,看看服務(wù)器如何將該數(shù)據(jù)發(fā)送到所有打開(kāi)的窗口。
- ###
- 繪圖事件
- ###
- $('canvas').live 'drag dragstart dragend', (e) ->
- type = e.handleObj.type
- offset = $(this).offset()
- ee.offsetX = e.layerX - offset.left
- ee.offsetY = e.layerY - offset.top
- x = e.offsetX
- y = e.offsetY
- App.draw(x,y,type)
- App.socket.emit('drawClick', { x : x, y : y, type : type})
- return
回到服務(wù)器端
由于我們知道通過(guò)Web套接字發(fā)送x、y和事件類型,我們需要在服務(wù)器端進(jìn)行一番處理。我們要做的就是,把該數(shù)據(jù)回過(guò)頭來(lái)發(fā)送到瀏覽器打開(kāi)的另外每個(gè)人。
我們更新后的server.coffee文件現(xiàn)在看來(lái)像這樣。我們先等待連接事件,然后等待由瀏覽器發(fā)送的“drawClick”事件。發(fā)送完畢后,我們把數(shù)據(jù)發(fā)送到瀏覽器打開(kāi)的另外每個(gè)人。我們之前編寫(xiě)的服務(wù)器端腳本然后就會(huì)繪制畫(huà)布。
- io = require('socket.io').listen(4000)
- io.sockets.on 'connection', (socket) ->
- socket.on 'drawClick', (data) ->
- socket.broadcast.emit 'draw',{ x : data.x, y : data.y, type: data.type}
- return
- return
你現(xiàn)在需要重啟Web套接字服務(wù)器,因?yàn)槲覀円褜?duì)它作了改動(dòng)。按Control-c命令即可終止它,輸入node server.js即可重啟它。
開(kāi)始繪圖!
一旦你完全了解了這一切的工作原理,就可以在支持Web套接字和畫(huà)布的任何Web瀏覽器(截至截稿時(shí)包括Chrome、Firefox、Safari、Opera和IE9)中打開(kāi)你的index.html文件。訪問(wèn)http://caniuse.com/#search=canvas,即可獲得更多的支持信息。
補(bǔ)充說(shuō)明
由于這是一個(gè)非?;镜难菔?,存在幾個(gè)局限性,不過(guò)再編寫(xiě)一點(diǎn)代碼,就很容易克服。目前,畫(huà)布只支持每次一個(gè)人繪圖;如果兩個(gè)或多個(gè)人同時(shí)繪圖,畫(huà)布就會(huì)被零星地繪制。另外,添加工具方面肯定也有很大的改進(jìn)余地,比如缺少畫(huà)筆、顏色、橡皮檫和PNG導(dǎo)出。
原文:http://wesbos.com/html5-canvas-websockets-nodejs/
【編輯推薦】