以下為本站文章分類清單

  1. AJAXspacer
  2. ASPspacer
  3. CSSspacer
  4. Javascriptspacer
  5. Windows 應用程式spacer
  6. 網頁設計
  7. 評論、短文、雜文spacer
  8. 版主spacer
  9. 最新回應spacer

以下為本頁本文內容

HTML5 Canvas 簡易手繪板

8/23 15' icon

Photo by www.williammalone.com/

HTML5 Canvas 提供了一塊畫布與眾多可用方法,使得在網頁應用程式中,設計繪圖板功能變得容易。這裡介紹一個簡易繪圖板製作。

HTML5 Canvas 所需事件

以桌上型瀏覽器來說,製作一個繪圖板程式,需要三個事件,首先,當滑鼠左鍵被按下時(onmousedown),這時要把目前滑鼠游標位置存下來,其次,若當滑鼠拖曳時(onmousemove),依照滑鼠游標軌跡由上一點繪製直線到下一點,造成手繪效果。最後放開滑鼠後(onmouseup),收捨善後。

這三個事件,在觸控裝置上則變成 onmouseover -> ontouchstart,onmousemove -> ontouchmove,onmouseup -> ontouchend 等對應事件名稱。

成品如下(功能多一點的成品在 http://audi.tw/pad)

開始製作

首先,定義一塊 canvas,使用 canvas 標籤


<canvas id="myCanvas" width="360" height="360" style="border:#000 1px solid;"></canvas>

接著為這塊畫布加上前述三個事件控制。如下,為畫布加上控制事件,其中 document.querySelector 是內建語法,用來選取物件,相當於 jQuery 的 $

使用 addEventListener 為物件加上事件控制,語法是:


obj.addEventListener(event,function)

傳入的參數一 event 請把前綴字 on 去掉,例如要設定 onmousedown,只要寫 mousedown 即可。

window.addEventListener('load',function(){
    var canvas=document.querySelector('#myCanvas');

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

    canvas.addEventListener('touchstart',touchStart);
    canvas.addEventListener('touchmove',touchMove);
    canvas.addEventListener('touchend',touchEnd);
});

mouseDown,touchStart


function mouseDown(e){
    this.draw=true;
    this.ctx = this.getContext("2d");
    this.ctx.strokeStyle='#000000';
    this.ctx.lineWidth=2;

    var o=this;
    this.offsetX=this.offsetLeft;
    this.offsetY=this.offsetTop;

    while(o.offsetParent){
    	o=o.offsetParent;
    	this.offsetX+=o.offsetLeft;
    	this.offsetY+=o.offsetTop;
    }

    this.ctx.beginPath();
    this.ctx.moveTo(e.pageX-this.offsetX,e.pageY-this.offsetY);
}

function touchStart(e){
    this.draw=true;
    this.ctx=this.getContext("2d");
    this.touch=e.targetTouches[0];
    this.ctx.strokeStyle='#000000';
    this.ctx.lineWidth=2;

    var o=this;
    this.offsetX=this.offsetLeft;
    this.offsetY=this.offsetTop;

    while(o.offsetParent){
    	o=o.offsetParent;
    	this.offsetX+=o.offsetLeft;
    	this.offsetY+=o.offsetTop;
    }

    this.ctx.beginPath();
    this.ctx.moveTo(this.touch.pageX-this.offsetX,this.touch.pageY-this.offsetY);
    e.preventDefault();
}

由於事件是附加在物件上,所以可以廣泛使用 this 關鍵字建置不同參數或變數

設定 this.draw 來控制開啟繪製或結束繪製。當按下鼠鍵時,設為 true,反之在 mouseup 中應設為 false

接著 this.ctx 用來取得2D繪製功能,簡單使用 getContext("2d") 方法即可,這個方法原始模樣如下:


var canvas=document.querySelector('#myCanvas');
var ctx=canvas.getContext("2d");

在我的範例中,把事件加在物件本身,所以上述 canvas 就變成 this,匠子明白了吧

2D繪製有許多屬性及方法可用,詳見 MDN 網站,此處僅說明重要部份。

屬性 strokeStyle 用來指定畫筆顏色(由名稱看來,未來能指定的,應該不指顏色),用十六進位寫法最可告,例如 #000000,也可用文字,例如 blue, red

屬性 lineWidth 則為畫筆粗細,只需數值,不用單位

接下來,使用 beginPath() 方法開始繪製。beginPath() 是指,現在起,點移動到何處,線就連到何處。一般畫線,要有起點和終點,而 beginPath() 一旦啟動,就是由目前置往下一點繪製一條直線。所以,在 mousedown 事件最後,就是把起始點移到使用者點選的位置。

beginPath() 有個對應的方法,叫 closePath()。任何時間,呼叫 closePath() 方法後,會自動把線條連回起始點。

物件和鼠標的螢幕位置

環境變數回傳的數位 pageX 和 pageY 分別代表滑鼠游標目前的水平和垂直位置,這個位置以螢幕左上角為 0,0 原點。不管畫面有沒有捲動,數字不變。

而物件在整份文件中的位置,有可能目前停在畫面之外,必需要捲動畫面才能看到。例如螢幕畫面寬 1024 高 600,但整份文件總高達 3000 像素。目前就有些內容必需捲動畫面才能查看,例如您現在正在閱讀的文字,也是經過捲動畫面才看到的。所以這時物件實際位置就很重要,否則,繪製線條時,找不到合適的點連線。

取得物件位置的基本語法是 obj.offsetLeft 和 obj.offsetTop,但這兩個值回傳的是第一個父層物件的 position:relative 時,相對於父層物件,如下圖:

A
B
C

區塊 A,B,C 的 position 屬性都是 relative,而 C 取回的 怎麼看都不是距離畫面左側的值吧?原因就是回傳值是找父層 position 屬性是不是 relative,是的話就回傳值,不是的話再往上層找物件。

所以最保險的方法,是不斷往父層找值,一直找到沒有父層為止。


    var o=this;
    this.offsetX=this.offsetLeft;
    this.offsetY=this.offsetTop;

    while(o.offsetParent){
    	o=o.offsetParent;
    	this.offsetX+=o.offsetLeft;
    	this.offsetY+=o.offsetTop;
    }
}

這段程式碼,就是在找到畫布在整個畫面真正的位置,那麼,當使用者按下鼠標時,不管畫面有沒有捲動,對應的位置就是

水平:e.pageX-this.offsetX,垂直:e.pageY-this.offsetY

觸控裝置呢?和一般使用滑鼠的差別在於環境變數回傳值,在鼠標世界,回傳 e.pageX 和 e.pageY,在觸控世界,以陣列回傳 e.touches 或 e.targetTouches,因為可能不只一支手指在觸控。

無論使用者放幾支手指,只取第個觸碰的那根⋯⋯this.touch=e.targetTouches[0] 這相當於是鼠標了,觸控位置 X 回傳值則為 this.touch.pageX,Y 則為 this.touch.pageY。

最後,觸控裝置,有部份行為和鼠標不同,通常按鼠標=觸控,拖曳鼠標=滑動手指。但,滑動手指通常有其他用途,所以在觸控裝置,記得把預設用途關掉,使用 e.preventDefault() 方法即可。

mousemove, touchmove,mouseup

花了一些篇幅說明 mousedown 的設計,接下來的 mousemove 設計應該不用再說明了


function mouseMove(e){
    if (this.draw){
        this.ctx.lineTo(e.pageX-this.offsetX,e.pageY-this.offsetY);
        this.ctx.stroke();
    }
}

function touchMove(e){
    this.touch=e.targetTouches[0];
    if (this.draw){
        this.ctx.lineTo(this.touch.pageX-this.offsetX,this.touch.pageY-this.offsetY);
        this.ctx.stroke();
    }
    e.preventDefault();
}

function mouseUp(e){
    this.draw=false;
}

最後,清空畫布的方法,就是 clear,清乾淨就可以。


function clearPad(){
    var canvas=document.querySelector('#myCanvas');
    var ctx=canvas.getContext("2d");
    ctx.clearRect(0,0,canvas.width,canvas.height);
}

要再多點彈性嗎?例如畫筆粗細?顏色?加油!

最後全部整理一次所有 javascritp code




重要說明

如果 canvas 本身 position 屬性是 fixed(或父層,父父層之類的),那麼,當頁面捲動時,畫布是不動,但事實上,畫布在頁面位置的回傳值,仍是在非 fixed 的情況。這種時候,請修正 moveTo 的位置,加入捲軸的因素


this.ctx.moveTo(e.pageX-this.offsetX-window.scrollX,e.pageY-this.offsetY-window.scrollY);

觸控
this.ctx.moveTo(this.touch.pageX-this.offsetX-window.scrollX,this.touch.pageY-this.offsetY-window.scrollY);

 

以下為文章回應區

同意轉載,不過麻煩看一下轉載需知

manicure   2017/5/4 上午 04:12:00

Greetings, I do think your website could possibly be having internet browser compatibility issues.
Whenever I take a look at your website in Safari, it looks fine however, when opening in Internet Explorer, it has some overlapping issues.
I merely wanted to give you a quick heads up! Besides that, fantastic website!

manicure   2017/4/11 下午 06:50:00

If you wish for to get a good deal from this piece of writing then you have to apply
such methods to your won webpage.

Eastpak   2016/11/11 下午 05:52:00

Thank you

給個回應
姓名:
佈落格網址:
  如果您是要發問問題, 最好有個問題測試的網址, 這樣比較容易找到您問題所在, 謝謝
內容: