使用HTML 5和Javascript設(shè)計繪圖程序
在本文中,將會介紹如何使用HTML5和Javascript去設(shè)計一個簡單的繪圖程序。HTML5的一個新的特性是canvas畫布功能,通過canvas畫布的強(qiáng)大功能可以實(shí)現(xiàn)繪畫不少圖形和其他絢麗的功能。在本文中,讀者將學(xué)習(xí)到如下幾個知識點(diǎn):
1) 如何動態(tài)在canvas畫布上繪畫圖形
2) HTML 5 canvas的前景特性探討
3) 目前瀏覽器對HTML5的兼容情況
本文的讀者對象為,對HTML 5 Canvas有初步認(rèn)識及熟悉Javascript的讀者。
設(shè)計目標(biāo)
首先,我們來設(shè)計下這個繪圖程序?qū)碛惺裁垂δ?。在這個簡單的繪圖程序中,首先要有的是一塊能給用戶涂鴉的畫布區(qū)域,上面有一只可愛的小鴨,然后我們準(zhǔn)備了4種不同顏色的蠟筆,可以給用戶給這只小鴨上色,同時也要提供橡皮擦的功能,以方便隨時擦除這個小鴨。而除了蠟筆外,也提供了普通的油畫筆的效果,當(dāng)然也指定了每次繪畫時筆觸范圍的大小,這里設(shè)定了4個選擇。設(shè)計好后的繪圖應(yīng)用,效果如下圖:
在這個應(yīng)用中,用戶點(diǎn)左邊的四種顏色筆,就可以在指定的矩形框中隨便涂鴉,也可而已點(diǎn)右面兩種不同的筆觸效果(crayon蠟筆)和普通筆,也可以使用橡皮擦,也可以使用右下角四種不同的筆觸大小。
開始使用Canvas畫布
首先,要聲明一個canvas畫布,使用如下代碼聲明:
目前,對canvas支持的最好的瀏覽器依然是FireFox,Chrome等非IE的瀏覽器,在本文的這個例子中,也兼顧了對IE瀏覽器的支持,使用的是一個開源的JS文件,其中提供了一些對canvas的基本支持腳本(在附件下載中包含了該腳本,名稱為excanvas.js)。因此,我們可以同時也為了兼顧IE,所以這里改用了
的方式,如下代碼:
接下來,為了要使用canvas畫布的功能,必須如下調(diào)用:
- context = document.getElementById('canvasInAPerfectWorld').getContext("2d");
然而,同樣為了兼顧在IE下的使用,我們改用以下的代碼段實(shí)現(xiàn):
- var canvasDiv = document.getElementById('canvasDiv');
- canvas = document.createElement('canvas');
- canvas.setAttribute('width', canvasWidth);
- canvas.setAttribute('height', canvasHeight);
- canvas.setAttribute('id', 'canvas');
- canvasDiv.appendChild(canvas);
- if(typeof G_vmlCanvasManager != 'undefined') {
- canvas = G_vmlCanvasManager.initElement(canvas);
- }
- context = canvas.getContext("2d");
可以看到,在上面的代碼中,通過document.createElement創(chuàng)建了一個標(biāo)簽元素canvas,然后再用setAttribute方法設(shè)置了畫布的高度和寬度等屬性(這些都可以通過設(shè)置常量屬性值進(jìn)行設(shè)置)。然后通過
- canvasDiv.appendChild(canvas);
為canvasDiv增加了一個子元素canvas。然后利用excanvas.js這個專門為IE擴(kuò)展的canvas元素包中提供的處理方法initElement進(jìn)行相應(yīng)的判斷處理,即:
- if(typeof G_vmlCanvasManager != 'undefined') {
- canvas = G_vmlCanvasManager.initElement(canvas);
- }
最后,要使用canvas的繪圖功能的話,必須調(diào)用canvas的上下文,這里使用的語句是:
- context = canvas.getContext("2d");
在畫布上繪畫圖形
接下來,我們開始在canvas上繪制圖形。這里我們要對4個鼠標(biāo)的相關(guān)事件進(jìn)行編碼,并且要編寫兩個相關(guān)的方法addClick和redraw。addClick方法記錄鼠標(biāo)移動的點(diǎn),而redraw方法則將已記錄的數(shù)據(jù)點(diǎn)在canvas畫布中繪畫出來。
先來看下鼠標(biāo)按下時的mouse down事件,代碼如下:
- $('#canvas').mousedown(function(e){
- var mouseX = e.pageX - this.offsetLeft;
- var mouseY = e.pageY - this.offsetTop;
- paint = true;
- addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
- redraw();
- });
其中設(shè)置的變量paint為true時,表明當(dāng)前正在繪制圖形,patint為false時,表示鼠標(biāo)已經(jīng)松開。
再看下鼠標(biāo)移動時的事件,代碼如下:
- $('#canvas').mousemove(function(e){
- if(paint){//是不是按下了鼠標(biāo)
- addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
- redraw();
- }
- });
鼠標(biāo)松開時的事件代碼為:
- $('#canvas').mouseup(function(e){
- paint = false;
- });
鼠標(biāo)移開的事件代碼為:
- $('#canvas').mouseleave(function(e){
- paint = false;
- });
下面是addClick方法的代碼如下:
- var clickX = new Array();
- var clickY = new Array();
- var clickDrag = new Array();
- var paint;
- function addClick(x, y, dragging)
- {
- clickX.push(x);
- clickY.push(y);
- clickDrag.push(dragging);
- }
可以看到,這里分別用三個數(shù)組clickX,clickY及clickDrag記錄了鼠標(biāo)移動的點(diǎn)的X,Y坐標(biāo),以及判斷是否鼠標(biāo)松開的標(biāo)志。
再來看下redraw這個方法,其作用為每次都清空畫板,然后重新把所有的點(diǎn)都畫過,效率不高,但作為本例子來說還是可以接受,代碼如下:
- function redraw(){
- canvas.width = canvas.width; // Clears the canvas
- context.strokeStyle = "#df4b26";
- context.lineJoin = "round";
- context.lineWidth = 5;
- for(var i=0; i < clickX.length; i++)
- {
- context.beginPath();
- if(clickDrag[i] && i){//當(dāng)是拖動而且i!=0時,從上一個點(diǎn)開始畫線。
- context.moveTo(clickX[i-1], clickY[i-1]);
- }else{
- context.moveTo(clickX[i]-1, clickY[i]);
- }
- context.lineTo(clickX[i], clickY[i]);
- context.closePath();
- context.stroke();
- }
- }
接下來,再定義四種不同的顏色:紫色,綠色,棕色和黃色,分別用四個不同的變量表示,并且用變量curColor保存當(dāng)前正在使用的顏色,并且也用一個數(shù)組clickColor來記錄用戶每次選擇的顏色。代碼如下:
- var colorPurple = "#cb3594";
- var colorGreen = "#659b41";
- var colorYellow = "#ffcf33";
- var colorBrown = "#986928";
- var curColor = colorPurple;
- var clickColor = new Array();
同樣,在addClick方法中,也必須加入對用戶每次選擇顏色的記錄,所以更新后的addclick代碼如下:
- function addClick(x, y, dragging)
- {
- clickX.push(x);
- clickY.push(y);
- clickDrag.push(dragging);
- clickColor.push(curColor);
- }
而在redraw的方法中,我們?nèi)サ鬰ontext.strokeStyle一句,將繪畫筆的顏色設(shè)置到for循環(huán)中去設(shè)置,更新后的redraw代碼如下:
- function redraw(){
- /* context.strokeStyle = "#df4b26"; */
- context.lineJoin = "round";
- context.lineWidth = 5;
- for(var i=0; i < clickX.length; i++)
- {
- context.beginPath();
- if(clickDrag[i] && i){
- contex.moveTo(clickX[i-1], clickY[i-1]);
- }else{
- context.moveTo(clickX[i]-1, clickY[i]);
- }
- context.lineTo(clickX[i], clickY[i]);
- context.closePath();
- context.strokeStyle = clickColor[i];
- context.stroke();
- }
- }
我們再設(shè)置畫筆每次繪畫筆觸范圍的大小,同樣,有四種選擇,分別為小,中,大和很大,并用clickSize數(shù)組記錄用戶的選擇,默認(rèn)的筆觸范圍大小用curSize進(jìn)行記錄。并且也要更新redraw方法,更新后的addClick,redraw代碼如下:
- function addClick(x, y, dragging)
- {
- clickX.push(x);
- clickY.push(y);
- clickDrag.push(dragging);
- clickColor.push(curColor);
- clickSize.push(curSize);
- }
- var radius;
- var i = 0;
- for(; i < clickX.length; i++)
- {
- if(clickSize[i] == "small"){
- radius = 2;
- }else if(clickSize[i] == "normal"){
- radius = 5;
- }else if(clickSize[i] == "large"){
- radius = 10;
- }else if(clickSize[i] == "huge"){
- radius = 20;
- }else{
- alert("Error: Radius is zero for click " + i);
- radius = 0;
- }
- function redraw(){
- ........
- context.strokeStyle = clickColor[i];
- context.lineWidth = radius;
- context.stroke();
- }
- }
最后,我們設(shè)置不同筆的繪畫效果,分別是蠟筆和普通筆以及橡皮擦功能。用clickTool記錄用戶選擇的工具種類,curTool則為當(dāng)前用戶選擇的工具,addClick的方法如下:
- function addClick(x, y, dragging)
- {
- clickX.push(x);
- clickY.push(y);
- clickDrag.push(dragging);
- if(curTool == "eraser"){
- clickColor.push("white");
- }else{
- clickColor.push(curColor);
- }
- clickColor.push(curColor);
- clickSize.push(curSize);
- }
注意,這里判斷如果用戶選擇的工具是橡皮擦,則將白色加入到clickColor數(shù)組中。同樣要在redraw的方法中對新的兩個繪圖工具進(jìn)行處理,代碼如下:
- function redraw(){
- context.lineJoin = "round";
- for(var i=0; i < clickX.length; i++)
- {
- context.beginPath();
- if(clickDrag[i] && i){
- context.moveTo(clickX[i-1], clickY[i-1]);
- }else{
- context.moveTo(clickX[i]-1, clickY[i]);
- }
- context.lineTo(clickX[i], clickY[i]);
- context.closePath();
- context.strokeStyle = clickColor[i];
- context.lineWidth = radius;
- context.stroke();
- }
- if(curTool == "crayon") {
- context.globalAlpha = 0.4;
- context.drawImage(crayonTextureImage, 0, 0, canvasWidth, canvasHeight);
- }
- context.globalAlpha = 1;
- }
這里針對當(dāng)用戶選擇“crazyon”蠟筆效果時,對繪畫的效果進(jìn)行了透明度的處理。
最后,我們要把小鴨子的圖在畫布中畫上,首先要聲明一個圖片對象如下:
- var outlineImage = new Image();
然后在prepareCanvas()方法中加載事先準(zhǔn)備好的圖片:
- function prepareCanvas(){
- ...
- outlineImage.src = "images/watermelon-duck-outline.png";
- }
最后在redraw的繪畫方法中,要使用canvas畫布的drawImage方法進(jìn)行繪畫,代碼為:
- function redraw(){
- ...
- context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
- }
其中drawingAreaX, drawingAreaY為要在哪個具體位置繪畫圖形,drawingAreaWidth和
drawingAreaHeight則為具體圖片的寬度和高度。
我們還要把繪圖的區(qū)域限制在一個矩形框里,這要用到畫布的save和clip方法。其中save用來保存Canvas的狀態(tài),而clip方法則是指定一個區(qū)域進(jìn)行剪裁,規(guī)定了繪畫的區(qū)域,代碼如下:
- function redraw()
- {
- ...
- context.save();
- context.beginPath();
- context.rect(drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
- context.clip(); //剪裁出指定的繪畫區(qū)域
- var radius;
- var i = 0;
- for(; i < clickX.length; i++)
- {
- ...
- }
- context.restore(); //使用restore方法,恢復(fù)每次保存的canvas狀態(tài)
- ...
- }
總結(jié)
本文中只是對如何使用HTML5和javascript繪制小繪圖應(yīng)用進(jìn)行了思路和基本技術(shù)點(diǎn)的分析,其中著重介紹了畫布canvas的各種使用方法,完整的代碼請到這里下載
(http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/downloads/html5-canvas-drawing-app.zip ),代碼中完成實(shí)現(xiàn)了相關(guān)的各種功能,并加入了一些邏輯判斷等操作,由于篇幅關(guān)系,不再在文中詳細(xì)描述。
原文:http://tech.it168.com/a2011/1104/1269/000001269250_all.shtml
【編輯推薦】