基于HTML5 Canvas和jQuery 的畫圖工具的實現

domascot 8年前發布 | 18K 次閱讀 HTML5 jQuery 前端技術

來自: http://blog.csdn.net/luanlouis/article/details/38490589

簡介

HTML5 提供了強大的Canvas元素,使用Canvas并結合Javascript 可以實現一些非常強大的功能。本文就介紹一下基于HTML5 Canvas 的畫圖工具的實現。廢話少說,先看成品:

該應用是遵循所見即所得( WYSIWYG , What you see is what you get)原則設計的,它具有以下功能:

1. 可以繪制自由曲線、直線、矩形框和文字;

2. 可以根據需要定義線段和矩形框的顏色和寬度;

3. 你可以需要字體的大小、顏色、字體;

4. 支持undo、redo操作;

5. 支持橡皮擦功能;

6. 支持本地圖片保存功能。

源碼下載

       讀者可以去我的 GitHub jPainter 項目下下載,或者直接使用git 工具: https://github.com/LuanLouis/jPainter.git

</fieldset>

開發心得分享

上述功能的實現,難點倒不是很多,值得一提的有以下幾點:

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;

 // 事件處理

})</pre>

            當然,如果讀者有其他的實現方案,還請不吝賜教,共同學習!

怎樣實現所見即所得的設計

       使用Canvas繪圖時,其繪圖是通過javascript控制的,比如,我想繪制一個矩形,應該使用類似以下的代碼:

var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
cxt.fillStyle="#FF0000";
cxt.fillRect(0,0,150,75);
        但是對于對于可交互的用戶界面,如果想創建一個矩形,應該是通過鼠標在畫板上拖動,然后可以隨時看到我將要畫的矩形的大小、邊框、顏色等等。怎樣讓用戶可以看到動態的效果呢? 當然了,使用canvas 肯定是實現不了的,這里我想到了一個方法,就是 使用<DIV> 元素模擬我們需要繪制的矩形,當用戶在拖動鼠標的過程中,使用DIV 顯示矩形的信息,一旦用戶松開鼠標,那么,將此DIV隱藏,根據鼠標的軌跡以及矩形配置,使用javascript繪制在對應的形狀。

類似地,繪畫直線和添加文字也是通過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); };
                  }
              }</pre> 

畫板信息存為圖片

代碼如下:

/**

               * 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; 
              }</pre> 

源碼下載        讀者可以去我的 GitHub jPainter 項目下下載,或者直接使用git 工具: https://github.com/LuanLouis/jPainter.git

-----------------------------------------------------------------------------------------------------------------------------------------

 

</div>

 本文由用戶 domascot 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!