HTML5實例教程——創意畫板

在HTML5備受期待和矚目的今天,愈來愈多的人已經感覺到它帶來的無限魅力與震撼力,許多的技術人員、設計者、互聯網愛好者們紛紛加入了HTML5的研究與設計中。javascript

首先我先爲你們介紹一下一個功能很強大的HTML5在線繪畫應用,它還擁有多種筆刷和濾鏡,具備相似於photoshop的圖層功能,可調節透明度隱藏等,還有漸變、油漆桶、拾色器、選擇工具,你們必定會爲此感到驚訝吧。html

clipboard.png

但這樣複雜的應用並無使用flash實現,在canvas標記尚未出現以前,要想實現複雜的網頁應用,或者直接在網頁上進行繪圖,只能藉助於第三方的插件,好比Flash或Java,而如今,藉助於canvas標記,咱們能夠實現圖像顯示和處理了,那麼如今就讓我拋磚引玉,講解一下個人一些開發思路吧。html5

想要製做一個簡單的畫板並非太難,但我建議您掌握必定的canvas基礎和javascript基礎,這樣更便於理解和學習本教程。而若是你canvas技術比較好的話,你必定會以爲本教程又長又囉嗦,可是教程不可能顧及到全部的閱讀者,因此麻煩你跳過你瞭解的部分,只關注重要的部分就行了。java

首先,我講解一下個人開發思路。咱們須要在頁面中添加一個canvas標記做爲咱們的畫布,也就是咱們未來要繪畫的畫板。因爲須要用戶使用鼠標點擊、滑動、釋放鼠標等操做來實現繪畫,因此咱們也必需要使用鼠標的幾個基本的監聽事件mousemove、mouseup、mousedown。web

document.addEventListener('mousemove', mouseMove, false);
document.addEventListener('mousedown', mouseDown, false);
document.addEventListener('mouseup', mouseUp, false);

爲了使繪畫出來的線條更流暢,兼顧性能問題,咱們能夠採用setInterval來設置監聽事件的時間間隔。 setInterval(函數名,1000/60); 其中1000/60爲時間間隔。編程

setInterval(loop, 1000 / 60);
function loop() {
    $pos_display.innerHTML='你當前鼠標的位置爲('+pos.x+','+pos.y+')';
    if (isMouseDown) draw(context);}

loop爲循環執行的函數。canvas

固然,你也能夠採用requestAnimationFrame(若是不瞭解該屬性能夠自行百度^_^)。這取決於你的習慣。數組

那麼如今咱們須要獲取用戶鼠標點擊的位置,在這裏咱們須要區分pageX,clientX,offsetX,layerX等概念 ,這裏有篇文章講解,你能夠看看http://www.funnyhao.com/pagex-clientx-offsetx-layerx-of-those-things/服務器

因爲咱們如今畫布直接放在頁面上左上部,padding和margin都爲0,所以咱們直接用clientX和clientY便可.當用戶第一次點擊鼠標時,咱們設置isMouseDown爲true,開啓繪畫模式。websocket

function mouseDown(e) {
    isMouseDown = true;
}

獲取了用戶點擊的位置後,咱們在約定的時間間隔後(1/60秒)再次獲取用戶所在的位置,並進行更新

function loop() {
    if (isMouseDown) draw(context);//繪製鼠標點擊位置
}
function mouseMove(e) {
    pos.x=e.clientX;//設置x座標
    pos.y=e.clientY;//設置y座標
    $pos_display.innerHTML='你當前點擊鼠標的位置爲('+pos.x+','+pos.y+')';//更新當前鼠標點擊的位置
}

接下來咱們就能夠繪製了

function draw(ctx) {
    ctx.save();//保存當前繪圖狀態
    ctx.fillStyle = DEFAULT_BRUSH_COLOR;//設置填充的背景顏色
    ctx.lineWidth =DEFAULT_BRUSH_SIZE;  //設置畫筆的大小
    ctx.lineCap = "round"; //設置線條,讓線條邊緣更圓滑
    ctx.beginPath();
    ctx.arc(pos.x,pos.y,DEFAULT_BRUSH_SIZE,0,Math.PI * 2,true);
    /****
    *context.arc(x, y, radius, startAngle, endAngle, anticlockwise)
    *參數 x,y表示圓心
    *radius半徑
    *startAngle起始弧度
    *endAngle終止弧度
    *anticlockwise是否爲逆時針方向
    ***/
    ctx.fill();//填充繪畫路徑
    ctx.restore();//恢復繪畫狀態
}

彷佛這樣的大功告成了。看這裏的演示代碼:DEMO1(http://runjs.cn/detail/gxeeyocw)

clipboard.png

當咱們畫畫時,若是繪製筆移動的較快的時候,就會發現出現了斷斷續續的狀況,這是怎麼回事呢?原來咱們只設置了一個點每過1/60秒就更新一下位置,當咱們繪圖時若是畫筆移動的速度夠快時繪製的不夠密集,繪製的點久不能鏈接起來,從而引發斷續的現象。

可能會有些人說能夠設置時間間隔更小,好比設置爲1/1000秒,也就是將頁面中的代碼

setInterval(loop, 1000 / 60);

改成

setInterval(loop, 1000 / 1000);

甚至無窮小,這樣不就解決了嗎。可是相信不少人都不會推薦這樣的方法,由於這不只僅會影響到頁面的效率,並且也沒有從根本上解決問題,setinterval調用間隔的時間每每會有諸多限制,因此這樣的方法是行不通的。

要讓線連貫起來最簡單的方法:那就用線連起來吧。(旁白:廢話,⊙﹏⊙b汗)咱們知道兩點肯定一條直線,因此只要咱們肯定兩個點的座標便可。亦即每一個時間間隔單位,咱們獲取一次當前點的座標就行了。而後使用canvas的moveTo函數移動下一個點,記錄當前點座標和上一個點的座標,並使用canvas的lineTo函數將線連起來,而後不要忘了用stroke函數繪製出來,具體看這裏的代碼:DEMO2(http://runjs.cn/detail/r52qaltg)。

clipboard.png

咱們經過表格比較一下這兩種方案的區別:

clipboard.png

表格中明顯看出方案一都是孤立的點,而方案二每一個點都會有兩種狀態,將這兩種狀態下的點連起來就會造成銜接的較好的效果。

由於基礎的內容在上面已經講述了,因此在這裏我也不重複了,須要注意的是當前點與上一個點重複時須要作一下處理,不然頁面沒法繪製出來。

if(pos.x==next_pos.x&&pos.y==next_pos.y){
    ctx.arc(pos.x,pos.y,DEFAULT_BRUSH_SIZE/1.7,0,Math.PI * 2,true);
    ctx.fill();//填充繪畫路徑}
else{
    ctx.moveTo(pos.x,pos.y);
    ctx.lineTo(next_pos.x,next_pos.y);
    ctx.stroke();
}

爲了方便講解,我這裏採用的都是面向過程的方法,在比較大的應用中,咱們要儘量採用面向對象的方法,好處是不言而喻的,不只能讓代碼條理清晰,更有較好的擴展性,方便二次開發和模塊複用。使用面向對象方法的代碼請查看這裏(這裏會使用了point函數類,覆蓋了set和update等方法)請查看DEMO3_1(http://runjs.cn/detail/gvfyrswu)。

研究技術的時候,咱們須要觸類旁通,顯然如今的方法仍是不夠完善。能不能將全部的點都記錄下來,由於每一個時間間隔單位,都會損失掉不少的點,爲了讓畫出來的圖更加圓滑,咱們要將全部的點都記錄下來,而且效率又能獲得優化,我在這裏提出一個解決方案。

用數組記錄下全部的路徑,而後用堆棧的push方法將點添加到數組中,爲了達到更好的效率,咱們能夠採用一維數組,分別用兩個數組記錄橫座標和縱座標,具體的實現我就不貼代碼了,你們有餘力的話能夠看成一個小小的做業,參照個人這個頁面例子本身編寫代碼實現,頁面中會有代碼註釋的。

咱們實現了繪製功能,咱們還須要對繪製的圖片進行擦除。不要嘗試採用transparent或者rgba(x,x,x,0)這樣的顏色值繪製,由於這樣頁面便不會繪製出任何東西,最實用的方法就是繪製背景顏色,若是背景是圖片,那就重繪背景圖片,而後就原來的內容的其餘部分繪製到畫布中。具體查看demo3_2(http://runjs.cn/detail/jywf4qv1

clipboard.png

那若是咱們要實現蠟筆的效果。要怎麼處理呢,若是咱們將蠟筆畫放大後看就會知道那是一些很小的分散顆粒狀大小的粒子,這樣咱們就有了思路了。咱們仍是沿用DEMO3的例子,在其基礎上進行開發,須要注意的一點是粒子的分佈問題,如何才能將粒子均勻的分佈呢,不知道大夥們這麼久沒學數學是否是都將知識還給老師了。這裏咱們會用到一些基本的數學知識, 具體思路請看下圖,

clipboard.png

請參照源代碼

draw: function(ctx) {
        var v = this.subtract(this._latest);//當前點與下一個點的距離的橫縱座標
        var s = Math.ceil(this.size / 2);       //算出粒子的單位長度
        var stepNum = Math.floor(v.length() / s) + 1;   //算出步長  v.length()爲斜線長度
        v.normalize(s);//當前點與下一個點的

        var sep = 1.5; // 分割數  控制畫筆的濃密程度  關鍵所在
        //粒子的大小 根據畫筆描繪的速度(畫筆的停留時間)進行調整
        var dotSize = sep * Math.min(this.inkAmount / this._latestStrokeLength * 3, 1);
        var dotNum = Math.ceil(this.size * sep);
        var range = this.size / 2;
        var i, j, p, r, c, x, y;
        $whitemode_display.innerHTML="繪製的畫筆顏色是"+brush_color;
        ctx.save();
        ctx.fillStyle = currentColor;
        $pos_display.innerHTML='你上一點鼠標的位置爲('+this.x+','+this.y+').你當前鼠標的位置爲('+this._latest.x+','+this._latest.y+')';//更新當前鼠標點擊的位置
        ctx.beginPath();
        if(wmode=="擦除模式"){
            ctx.strokeStyle=brush_color;
            ctx.lineWidth =DEFAULT_BRUSH_SIZE;
            ctx.lineCap = "round";
            ctx.beginPath();
            p = this._latest;//獲取下一個點位置
            ctx.moveTo(this.x,this.y);
            ctx.lineTo(p.x,p.y);
            ctx.stroke();
        }
        else{
            for (i = 0; i < dotNum; i++) {
                for (j = 0; j < stepNum; j++) {
                    p = this._latest.add(v.scale(j));
                    r = random(range);
                    c = random(Math.PI * 2);
                    w = random(dotSize, dotSize / 2);
                    h = random(dotSize, dotSize / 2);
                    x = p.x + r * Math.sin(c) - w / 2;
                    y = p.y + r * Math.cos(c) - h / 2;
                    ctx.rect(x, y, w, h);//邊緣不要太平滑,不要使用arc
                }
            }
        }
        ctx.fill();
        ctx.restore();
    }

  });

進行分析比較。思路的重點是在必定間隔後對粒子進行隨機分散排布,並能處理在畫筆移動的比較快的時候的的繪製問題。爲了獲得更好的展現效果,咱們通常還會控制透明度進行調整。

處理完這些後,咱們若是喜歡這樣的圖片,還可使用圖片導出功能,方法也挺簡單。去掉監聽事件,使用canvas的toDataURL內置函數,而後展現到新打開的窗口中。

咱們就能夠運行一下源代碼看看帶擦出功能和圖片導出功能的實際效果如何:

clipboard.png

舒適提示:在鍵盤上輸入p鍵能夠導出圖片,圖片導出功能因爲在新窗口中打開,請使用全屏預覽模式並容許窗口彈出。

其實咱們還能夠有更多變化,只要你去構想,去思考。不少時候你都要去嘗試,每每屢次嘗試纔會有新的ideas。接下來咱們但是作出如下特殊的畫筆,好比說鋼筆效果,邊緣會比較筆觸比較重的。鋼筆效果須要將畫筆尺寸調小,減弱抽絲的效果,而且邊緣的鋸齒會比較明顯,因此須要作一下陰影模糊處理,讓其過渡更平滑。

具體源碼看這裏:demo5(http://runjs.cn/detail/df5u6cb5

clipboard.png

以前曾在這裏見過一個毛筆畫網站,因此也模擬了一下毛筆的效果,毛筆的特色是筆觸比較大,當收筆較快時邊緣要凹凸不平,筆尖寫字鋒棱易出。當收筆有停頓時則會圓潤而渾厚。有個相似的線上應用,你們能夠去研究研究:http://www.theshodo.com/Write

![clipboard.pn
clipboard.png
C)

![圖片上傳中...]

根據這些特色,我給你們提供一個DEMO源碼,是在鋼筆效果的基礎上作些小小的調整的。

若是你們還以爲這樣的效果是否還能夠添加點特效什麼的,對,能夠作雜點斑點的效果,還有墨水過多而流下的效果。

具體能夠參看DEMO6(http://runjs.cn/detail/ully3puv)和DEMO7。

clipboard.png

clipboard.png

源碼我就不進行分析了,留待你們本身去研究,將畫筆顏色調爲黑色就差很少能夠模擬出毛筆的效果了。在我看來,技術的專研不是一味的讓別人教你,而是讓你本身去領悟的。只有那樣你才真正學到技術,領略到不同的樂趣。

說些設計之外的東西,設計、編程都須要有本身的思想和靈魂,真正讓別人也能感覺到你的思路,而這一切都須要磨練,須要觸類旁通。不該該知足於現狀,我在這裏只列舉了其中一些效果,我相信還有不少效果能夠實現,好比說相似於這樣的噴霧效果,鉛筆字效果,藝術畫效果,等等。既然說HTML5創意畫板,那就要嘗試脫離這個畫板的束縛,學到更多的東西,好比說你能夠用這個畫板作什麼,能夠作一個記事本、塗鴉工具、處理和分享圖片,個性簽名,你還能夠作一些小遊戲,塗鴉類的遊戲,你畫我猜(須要採用websocket實現服務器端雙向通訊)等等,甚至能夠作一些canvas動畫,這些基礎上作些修改和調整,徹底是能夠實現的。有了目標和思路,那就沿着這個方向去學習,我相信你必定會有所收穫的。

此次的HTML5繪圖教程就到這裏,你們還能夠嘗試爲此添加更多的個性化的功能,同時歡迎你們留言提問或者提出批評建議。

源碼下載:

runJS: http://runjs.cn/detail/spxs2kxq

微盤:http://vdisk.weibo.com/s/otSnZ

百度網盤:http://pan.baidu.com/share/link?shareid=194573&uk=3744164386

擴展閱讀:

在線毛筆畫板:http://www.theshodo.com/Write

來自deviants的在線畫板:http://sta.sh/muro/

若是想查看以前的FLASH版本,能夠點擊這裏http://www.inzrb.com/blog/?page_id=211

via sina udc

相關文章
相關標籤/搜索