以下為本站文章分類清單

  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);

 

以下為文章回應區

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

How you can increase your height?   2017/7/21 下午 10:48:00

Hey there! This is my first visit to your blog! We are a group of
volunteers and starting a new project in a community in the same niche.
Your blog provided us valuable information to work on. You
have done a outstanding job!

diabetes an foot pain   2017/7/4 上午 01:29:00

Hello very cool web site!! Man .. Excellent ..
Superb .. I'll bookmark your website and take the feeds additionally?
I'm happy to find numerous useful information here in the submit, we need work out more strategies on this
regard, thanks for sharing. . . . . .

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

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