canvas 基礎系列(一)之實現抽獎刮刮卡(橡皮擦)

筆主最近一個多月以來 「深刻「 研究了 canvas 的實現原理,一口氣讀完了 《HTML5 Canvas 核心技術》這本書;而這一切以及這篇文章的誕生,都源於筆主公司的一位實習產品經理~
這位實習生擁有剛畢業時的血氣方剛,以及天馬行空的想象力;他從未考慮過項目的實際需求,以及上線時間成本,在咱們公司以注重產品功能爲導向的氛圍中,自成一派,成爲一股清流 😂
一個推廣的小頁面,須要有山有水,有天空有流星,手指放上去還得泛起漣漪,拿到需求文檔時嚇得我趕忙上 GG 搜 demo,這時我發現單個 demo 都能很好的運行在頁面中,但是當他們合併之後,在瀏覽器調試面板中一行行鮮紅的文字不停的拍打着個人臉頰。我忽然發現本身在遇到一些奇葩需求時,是多麼的無助。由於我不會 canvas 啊!
本系列文章旨在與你們分享筆主這段時間學習 canvas 的經驗,由淺入深,將本身實際工做過程當中抄過的 demo (什麼?作個抽獎?評估時間?你等等,我搜下 demo…… 哦,不用評估時間了,已經改好了)都一一實現一遍,讓媽媽不再用擔憂咱們遇到奇葩需求啦!javascript

原文地址 喜歡就給我個大大的 ✨ 吧!css

先上預覽 demo html

scratchCard

咱們假設咱們的讀者都很是熟悉 javascript 原生語法。java

本章涉及到的知識點

  • context 繪圖環境 咱們能夠經過 canvas 標籤來新建一個 canvas 畫布,這個 canvas 畫布使得咱們能夠在瀏覽器中操做畫布內的像素,來實現繪製圖形,動畫等功能; 想要操做 canvas 畫布裏的內容,只能使用 javascript,千萬別嘗試用 css3 的動畫屬性去操做它,沒卵用~

咱們想要操做 canvas,首先須要獲取這個 canvas 對象css3

<canvas id="canvas" width="250" height="50"></canvas>
複製代碼
// 獲取 canvas 對象
let canvas = document.getElementById('canvas'),  
    // 獲取 cavans 繪圖環境對象,參數爲 2d
	context = canvas.getContext('2d');
複製代碼

context 是繪圖環境對象,若是你在瀏覽器中輸出 context,你會發現裏面包含了許多屬性,如:fillStyle strokeStyle等,以及一些函數方法,arc() rect() save() restore() clip()等等。git


  • 繪製基本圖形

矩形

context.rect($x, $y, $width, $height)github

矩形是最簡單的圖像繪製了;canvas

🔑 參數: xy:分別對應矩形左上角在 canvas 畫布中的座標; widthheight:就是矩形的寬高啦;數組

⚠️ 不過值得注意的是,調用這個方法,~只是繪製的矩形的路徑~,該路徑是不可見的,除非你在後面調用 context.fill()context.stroke() 方法,進行填充或者描邊。瀏覽器

填充描邊的表現形式取決於 context 繪圖環境的 fillStylestrokeStyle屬性的值。

圓形

context.arc($x, $y, $radius, $start_radian, $end_radian [, $clockwise])

圓形稍微複雜一丟丟,不過也很簡單啦。

🔑 參數: xy:表明圓的圓心在 canvas 畫布中的座標點; radius:圓的半徑,半徑,是半徑,不是直徑!start_radian $end_radian:起始角和終止角,他們接收的值只能是弧度。

記得圓周率麼?記得3.14是啥不,在 javascript 中 Math.PI 就表明的 π ;
簡單的說,圓有360°,而一個 π 是180°,你想畫個整圓,就是從 0° 到 360° 走一圈,Math.PI * 2;你想畫個半圓,就是 0° 到 180°,Math.PI

$clockwise:傳入布爾值,false 圓由順時針繪製;true 圓由逆時針繪製。

和矩形相同,這個方法也只是繪製了一個路徑,想要在頁面顯示,任然是須要調用 context.fill()context.stroke() 兩個方法。

當前路徑

context.beginPath() 咱們在建立一個集合圖形以前,都應當先調用該方法,該方法會清除上一次繪製時留下的路徑,並將本次繪製的路徑做爲 ~當前路徑~。

繪製矩形和圓

let canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d');

/** * 繪製一個矩形 * @param {Num} x 矩形的左上角 x 軸的位置 * @param {Num} y 矩形的左上角 y 軸的位置 * @param {Num} width 矩形的寬度 * @param {Num} height 矩形的高度 */
function drawRect(x, y, width, height) {
	context.beginPath();               // 清空上一次的路徑
	context.rect(x, y, width, height); // 建立一個矩形路徑
	context.fill();                    // 將該矩形路徑填充爲繪圖環境指定的填充顏色 context.fillStyle
}

/** * 繪製一個圓形 * @param {Num} centerX 圓心 x 軸座標 * @param {Num} centerY 圓心 y 軸座標 * @param {Num} radius 圓的半徑 */
function drawCircle(centerX, centerY, radius) {
    context.beginPath();
    context.arc(centerX, centerY, radius, 0, Math.PI * 2, false);
    context.fill();
}

drawRect(0, 0, 100, 100);
drawCircle(160, 50, 50);
複製代碼

  • 清除畫布內容 context.clearRect($x, $y, $width, $height) 該方法,擦除規定矩形中的全部內容;參數同繪製矩形是同樣的,只不過這是清除!

  • canvas 的狀態

canvas 的狀態包括了:

  1. 當前座標變換信息,transform 的東西,不用擔憂,本章並不涉及;
  2. 剪輯區域,這是本章的核心,context.clip() 後面會詳細講到;
  3. 全部繪圖環境(context)屬性,也就剛剛提到的,fillStyle[規定填充的顏色] strokeStyle[規定描邊的顏色],這些狀態決定的 canvas 中繪製的圖像的展現效果,他們是全局的,也就是說一旦規定了fillStyle爲紅色,那麼除非在 javascript 後面重置這個屬性,不然你未來繪製出來的幾何圖形永遠都是紅色的。
context.fillStyle = 'red';
drawRect(0, 0, 100, 100);   // -> red
drawRect(110, 0, 100, 100); // -> red

context.fillStyle = 'blue';
drawRect(220, 0, 100, 100); // -> blue
複製代碼

  • canvas 的狀態保存與恢復 前面已經提到過,canvas 的狀態是什麼。 而這裏引出的是繪圖環境中的兩個很重要的方法,save() 和 restore()。

若是有個需求,我想要先繪製一個紅色的矩形,接着繪製一個藍色的矩形,最後再繪製一個紅色的矩形

咱們知道 javascript 是逐條同步執行的,那麼要實現上面的需求,咱們須要先定義繪圖環境的填充顏色爲紅色,繪製了第一個紅色矩形後,再定義繪圖環境的填充顏色爲藍色,繪製,再定義繪圖環境的填充顏色爲紅色,就像這樣:

context.fillStyle = 'red';
drawRect(0, 0, 100, 100);   // -> red

context.fillStyle = 'blue';
drawRect(110, 0, 100, 100); // -> blue

context.fillStyle = 'red';
drawRect(220, 0, 100, 100); // -> red
複製代碼

是否是以爲這樣作很是的傻~

此時 save() 和 restore() 就站出來講不了!

context.save():能將執行該方法以前的全部 ~canvas 狀態~ 保存下來; context.restore() :將 save 方法保存的 ~canvas 狀態~ 釋放出來;

因此上面弱智的寫法,咱們能夠寫成這樣:

context.fillStyle = 'red';
drawRect(0, 0, 100, 100);    // - red 

context.save();

context.fillStyle = 'blue';
drawRect(110, 0, 100, 100);  // -> blue

context.restore();
drawRect(220, 0, 100, 100);  // -> red
複製代碼

save 方法保存的是 context.fillStyle = 'red' 這個狀態,當設置這個狀態爲藍色時,context 繪圖環境的 fillStyle 屬性變成了藍色,當執行 restore 方法時,又被恢復成了紅色。


  • 剪輯區域 clip()

context 繪圖環境對象提供了一個 context.clip() 剪輯區域方法,這也是本章實現橡皮擦功能的核心方法。

該方法會將 ~當前路徑~ 做爲一個剪輯區域,使該區域之外的圖像不可見。

而且在剪輯區域中,全部填充,清除等操做不會影響剪輯區域之外的內容。

因此咱們能夠建立一個剪輯區域,在這個剪輯區域中執行清除操做,就能夠擦除 canvas 畫布中指定的內容。

drawRect(0, 0, 100, 100);

context.save();

// 建立一個圓形路徑,並做爲剪輯區域,擦除該區域中的全部內容
drawCircle(0, 0, 50);
context.clip();
context.clearRect(0, 0, canvas.width, canvas.height);

context.restore();

drawRect(110, 0, 100, 100);
複製代碼

開始實現橡皮擦

本章須要用到的基礎知識就講到這裏啦,若是你們還有疑問,能夠查閱 w3school 上的 canvas 文檔

⚠️ 這個 demo 只能運行在手機上,瀏覽器須要打開手機模擬。

🚶 思路:

  1. 定義一個公用繪製剪輯區域的方法 drawEraser() ,該方法會將當前的路徑做爲剪輯路徑繪製,並::清除該路徑內的全部內容::;
  2. 手指按下時,觸發 touchstart 事件,開啓 dragging 狀態,調用 drawEraser() 方法,繪製第一個剪輯區域;
  3. 手指移動過程當中,不斷的調用 drawEraser() 方法,繪製剪輯區域,實現橡皮擦效果。
<!DOCTYPE html>
<html>

<head>
	<title>橡皮擦</title>
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
</head>

<body>

	<canvas id="canvas" width="300" height="300" style="background: #eee">
		Canvas not supported
	</canvas>

	<script> var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), ERASER_SIZE = 15, // 橡皮擦的大小 dragging = false; // 是否處在拖動狀態 /** * 轉換座標值 * 將鼠標點擊或移動時獲取的座標值,減去 canvas 相對窗口的座標值,就是在 canvas 畫布中的座標值 * @param {Obj} e 手指當前相對窗口的座標位置 */ function windowToCanvas(e) { let x = e.targetTouches[0].clientX, y = e.targetTouches[0].clientY, bbox = canvas.getBoundingClientRect(); return { x: x - bbox.left, y: y - bbox.top } } /** * 繪製剪輯區域,並清除該區域中的內容 * @param {Obj} loc 手指當前相對 canvas 畫布中的座標位置 */ function drawEraser(loc) { context.save(); context.beginPath(); context.arc(loc.x, loc.y, ERASER_SIZE, 0, Math.PI * 2, false); context.clip(); context.clearRect(0, 0, canvas.width, canvas.height); context.restore(); } /** * 頁面加載時,繪製一個鋪滿 canvas 畫布的矩形 * 該矩形用於被擦除 */ window.onload = function (e) { context.save(); context.fillStyle = '#666'; context.beginPath(); context.fillRect(0, 0, canvas.width, canvas.height); context.restore(); } /** * ① 手指按下時,開啓 dragging 狀態,並繪製剪輯區域 */ canvas.addEventListener('touchstart', function (e) { var loc = windowToCanvas(e); dragging = true; drawEraser(loc); }) /** * ② 手指移動時,不斷進行剪輯區域的繪製,以及路徑更新,實現擦除的效果 */ canvas.addEventListener('touchmove', function (e) { var loc; if (dragging) { loc = windowToCanvas(e); drawEraser(loc); } }) /** * ③ 手指離開,結束擦除過程 */ canvas.addEventListener('touchend', function (e) { dragging = false; }) </script>
</body>

</html>
複製代碼

結語

  • 使用 canvas 實現橡皮擦功能是很是簡單的。咱們能夠把橡皮擦的形狀換成矩形,多邊形,五角星等等;只須要改變繪製剪輯區域的路徑就好了;

  • 刮刮卡的功能,也徹底是由橡皮擦功能實現的,咱們能夠把 canvas 的背景圖片設置爲開獎的內容,文字等,用一個數組將這些圖片的路徑包含進去,每次加載頁面時,隨機調用一個數組元素;

  • 這期內容就講到這裏,下期帶你們用 canvas 實現 大轉盤抽獎,九宮格抽獎。3Q,阿里呀多。

原文地址 喜歡就給我個大大的 ✨ 吧!

相關文章
相關標籤/搜索