簡介
HTML5 提供了強大的Canvas元素,使用Canvas並結合Javascript 可以實現一些非常強大的功能。本文就介紹一下基於HTML5 Canvas 的畫圖工具的實現。廢話少說,先看成品:
該應用是遵循所見即所得(WYSIWYG, What you see is what you get)原則設計的,它具有以下功能:
1. 可以繪製自由曲線、直線、矩形框和文字;
2. 可以根據需要定義線段和矩形框的顏色和寬度;
3. 你可以需要字體的大小、顏色、字體;
4. 支持undo、redo操作;
5. 支持橡皮擦功能;
6. 支持本地圖片保存功能。
開發心得分享
上述功能的實現,難點倒不是很多,值得一提的有以下幾點:
1. 鼠標按下並移動 事件應該怎樣實現
2. 怎樣實現所見即所得 的設計
3. undo redo 的實現原理
4. 畫板信息另存爲圖片
鼠標按下並移動 事件應該怎樣實現
如果我們在畫板想畫自由曲線,我們需要捕獲鼠標按下並拖動的過程中 拖動的軌跡。那麼怎樣捕獲這樣的事件呢? 熟悉javascript 事件的讀者應該知道,鼠標移動事件的句柄是 onmousemove,有的讀者可能認爲,可以直接爲onmousemove 綁定事件處理函數,從event事件對象的button屬性來判斷是鼠標的哪一個鍵點擊不就行了嗎?代碼如下:
而實際上,上述的代碼運行時,當我們在頁面上無論是點擊鼠標的哪個鍵,都是輸出如下的信息:$(function() { $(document).mousemove(function(e){ console.log(e.button+" "+e.which); }) });
從輸出的結果可以看出,結果和我們預期的並不一樣。這是爲什麼呢?
原因是: javascript的事件機制是這樣的,當用戶觸發了事件之後,javascript宿主-瀏覽器會將事件封裝成event對象,然後根據事件的類型對event屬性進行賦值。然後根據event的類型,根據什麼類型的事件來調用相應的事件處理函數。舉例來說,如果我們在界面上按下了鼠標的右鍵,那麼,瀏覽器會首先創建一個event對象,然後對event屬性賦值,而相應的button會被置爲2、which爲3表示右鍵被按下;然後javascript 會將此event對象作爲參數傳遞給相應的事件處理函數,執行事件處理函數。也就是說,event的button屬性(以及jquery封裝後的which屬性)只有當 click、mousedown,mousup 對應的事件處理函數纔有意義。
那麼,我們怎樣才能判斷當鼠標移動時,鼠標鍵是否被按下呢?
解決方法:鼠標按下和鬆開是個過程,我們可以設置一個 flag,在鼠標按下的時候置爲true,鼠標鬆開的時候置爲false,然後在鼠標移動的事件處理函數中判斷這個flag,進而可以區分鼠標是否被按下。
假設我們需要在<body> 元素上捕獲 相應的鼠標事件,以下是使用jquery 進行事件處理函數的綁定:
//onmousemove 事件 $("body").mousemove(function(e){ if(flag) { // 鼠標被按下 } }) //onmousedown事件 $("body").onmousedown(function(e){ flag = true; // 事件處理 }) //onmouseup事件 $("body").mouseup(function(e){ flag = false; // 事件處理 })
當然,如果讀者有其他的實現方案,還請不吝賜教,共同學習!
怎樣實現所見即所得的設計
使用Canvas繪圖時,其繪圖是通過javascript控制的,比如,我想繪製一個矩形,應該使用類似以下的代碼:
但是對於對於可交互的用戶界面,如果想創建一個矩形,應該是通過鼠標在畫板上拖動,然後可以隨時看到我將要畫的矩形的大小、邊框、顏色等等。怎樣讓用戶可以看到動態的效果呢? 當然了,使用canvas 肯定是實現不了的,這裏我想到了一個方法,就是使用<DIV> 元素模擬我們需要繪製的矩形,當用戶在拖動鼠標的過程中,使用DIV 顯示矩形的信息,一旦用戶鬆開鼠標,那麼,將此DIV隱藏,根據鼠標的軌跡以及矩形配置,使用javascript繪製在對應的形狀。var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); cxt.fillStyle="#FF0000"; cxt.fillRect(0,0,150,75);
類似地,繪畫直線和添加文字也是通過HTML僞裝的邏輯:
繪畫直線時,用戶在畫板上拖動並按下鼠標時,動態地顯示出一條使用HTML僞裝的直線,可以隨着用戶鼠標的移動而變化,當用戶鬆開鼠標時,對應模擬直線的HTML元素隱藏,調用javavscript繪製真正的直線;
添加文字時,這裏使用的<textarea>元素 進行模擬文本輸入框,當用戶在畫板上添加文字時,可以拖動鼠標設置輸入框的大小,然後輸入文字,一旦輸入框失去焦點,則隱藏此<textarea> 元素,然後使用javascript繪製相應的文字
undo redo 的實現原理
在介紹 undo redo 的實現之前,要先講一下canvas的toDataURL()方法。toDataURL()方法將canvas上所繪製的內容轉換成格式png格式圖片,並將圖片通過base64編碼,轉換成形如如:data:image/png;base64,iVBORw0KGg....... 的字符串,用來表示圖片數據。(PS:對此比較困惑的讀者可以自行查找關於HTML 圖片 BASE64 存儲的相關問題,這個知識點還是很重要的)
undo redo 的原理實際上很簡單,就是當每執行一次繪畫,則將畫板的內容轉換成base64編碼的字符串,存到緩存數組中去,然後在需要undo 的時候,將畫板清空,再將緩存數組中的最後一次編輯的圖片繪製到畫板上即可。相關的實現細節如下代碼所示:
//undo redo var history =new Array(); var cStep = -1; /** * put current canvas to cache */ function historyPush() { cStep++; if (cStep < history.length) { history.length = cStep; } history.push($("#myCanvas").get(0).toDataURL()); } /** * function: undo */ function undo() { if (cStep >= 0) { cStep--; var tempImage = new Image(); tempImage.src = history[cStep]; tempImage.onload = function () { ctx.drawImage(tempImage, 0, 0);}; } } /** * function: redo */ function redo() { if (cStep <history.length-1) { clearCanvas(); cStep++; var tempImage = new Image(); tempImage.src = history[cStep]; tempImage.onload = function () { ctx.drawImage(tempImage, 0, 0); }; } }
畫板信息存爲圖片
代碼如下:
/** * save canvas content as image */ function saveItAsImage() { var image = $("#myCanvas").get(0).toDataURL("image/png").replace("image/png", "image/octet-stream"); //locally save window.location.href=image; }
-----------------------------------------------------------------------------------------------------------------------------------------
本文源自 http://blog.csdn.net/luanlouis/,如需轉載,請註明出處,謝謝!