H5和微信小遊戲 Canvas API 整理 前言

前言

微信小遊戲推出後一度爆火,一個簡單的跳一跳讓大家玩的不亦樂乎,那麼很多人就在想自己嘗試學學微信小遊戲,開發一個屬於自己的微信小遊戲。現在微信小遊戲的開發都離不開遊戲引擎,用原生小遊戲開發工具開發的很少很少。但是畢竟我不是專業遊戲開發,所有遊戲引擎就不搞了,我們就單純來看原生微信小遊戲開發。

原生微信小遊戲開發全是js,界面上所有的可見元素都是通過js canvas畫出來的。所以這就是這篇博客的內容,我們要來整理下微信小遊戲Canvas的繪圖api。爲什麼要單獨寫篇博客整理呢,因爲微信小遊戲的官方文檔並沒有提供(反正我是沒有找到)。因爲微信小遊戲的canvas繪製和H5的canvas繪製基本沒有卻別,這本身是屬於H5的範疇,並不是微信小遊戲的範疇,所以,廢話說了這麼多,下面開始正文。

(1)獲取canvas

要使用canvas繪製,首先得獲取到canvas實例,在H5中獲取canvas和獲取其它標籤一樣,通過document獲取。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

但是微信小程序做了封裝,它不允許用戶直接操作dom,所以不能通過document獲取canvas,而是提供了一個微信api。

let canvas =wx.createCanvas();
let ctx = canvas.getContext('2d');

(2)填充色和線條色

填充色:線條封閉區域內全部着色

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#0000ff";
ctx.fillRect(20, 20, 150, 100);

線條色:只給線條着色,着色寬度就是線條寬度

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#0000ff";
ctx.strokeRect(20, 20, 150, 100);

(3)陰影

陰影顏色:陰影的本質就是光線被擋而形成的暗淡,所以建議不要給陰影設置很鮮豔的顏色

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.shadowColor = "black";
ctx.fillStyle = "blue";
ctx.fillRect(20, 20, 100, 80);

陰影大小:所謂陰影大小就是陰影擴散的範圍

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.shadowBlur = 20;
ctx.shadowColor = "black";
ctx.fillStyle = "blue";
ctx.fillRect(20, 20, 100, 80);

陰影偏移:光源的方位決定的陰影投射的方向

var canvas = document.getElementById("myCanvas");
var ctx = canvas .getContext("2d");
ctx.shadowOffsetX = 20;
ctx.fillStyle = "blue";
ctx.fillRect(20, 20, 100, 80);
var canvas = document.getElementById("myCanvas");
var ctx = canvas .getContext("2d");
ctx.shadowOffsetY = 20;
ctx.fillStyle = "blue";
ctx.fillRect(20, 20, 100, 80);

(4)漸變

漸變就要涉及到漸變顏色和漸變方向,H5中漸變方向是通過起始點決定的,在createLinearGradient方法中傳入兩個點的座標,這兩個點的連線方向就是漸變的方向。設置漸變顏色是通過addColorStop方法添加。

var canvas = document.getElementById("myCanvas");
var ctx = canva.getContext("2d");
var my_gradient = ctx.createLinearGradient(0, 0, 0, 170);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
ctx.fillStyle = my_gradient;
ctx.fillRect(20, 20, 150, 100);

我們嘗試一下從做到右的漸變

var canvas = document.getElementById("myCanvas");
var ctx = canva.getContext("2d");
var my_gradient = ctx.createLinearGradient(0, 0, 170, 0);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
ctx.fillStyle = my_gradient;
ctx.fillRect(20, 20, 150, 100);

漸變色可以添加多個,我們嘗試添加三個漸變色:黑 -> 紅 -> 白

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var my_gradient = ctx.createLinearGradient(0, 0, 170, 0);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(0.5, "red");
my_gradient.addColorStop(1, "white");
ctx.fillStyle = my_gradient;
ctx.fillRect(20,20,150,100);

當然也可以給線條設置漸變

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var gradient = ctx.createLinearGradient(0, 0, 170, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
ctx.strokeStyle = gradient;
ctx.lineWidth = 5;
ctx.strokeRect(20, 20, 150, 100);

甚至可以給文字設置漸變

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var gradient = ctx.createLinearGradient(0, 0, 170, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
ctx.strokeStyle = gradient;
ctx.strokeText("Big smile!", 10, 50);

(5)元素重複

createPattern() 方法在指定的方向內重複指定的元素。元素可以是圖片、視頻,或者其他 <canvas> 元素。被重複的元素可用於繪製/填充矩形、圓形或線條等等。

重複模式:repeat、repeat-x、repeat-y、no-repeat

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 300, 150); 
var img = document.getElementById("img")
var pat = ctx.createPattern(img, "repeat");
ctx.rect(0, 0, 260, 130);
ctx.fillStyle = pat;
ctx.fill();

(6)放射漸變

沒有和漸變放在一起,主要是函數不一樣

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var grd = ctx.createRadialGradient(75, 50, 5, 90, 60, 100);
grd.addColorStop(0, "red");
grd.addColorStop(1, "white");
ctx.fillStyle = grd;
ctx.fillRect(10, 10, 150, 100);
參數 描述
x0 漸變的開始圓的 x 座標
y0 漸變的開始圓的 y 座標
r0 開始圓的半徑
x1 漸變的結束圓的 x 座標
y1 漸變的結束圓的 y 座標
r1 結束圓的半徑

這個參數理解起來有點麻煩,我們改下代碼再看效果就明顯多了。

var canvas = document.getElementById("myCanvas");
var ctx =canvas.getContext("2d");
var grd = ctx.createRadialGradient(75, 50, 50, 150, 50, 50);
grd.addColorStop(0, "red");
grd.addColorStop(1, "blue");
ctx.fillStyle = grd;
ctx.fillRect(10, 10, 150, 100);

從上圖我們可以看出,漸變區域是由兩個圓決定的,超出兩個圓的區域,漸變停止,用外圍像素填充。

(7)添加漸變色

前面講了這麼多漸變,最重要的一個函數卻沒有說,所有的漸變色都通過addColorStop()方法添加的。
addColorStop()這個函數是可以添加很多顏色,按照順序, 一次均勻漸變。

var canvas =document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var grd = ctx.createLinearGradient(0, 0, 170, 0);
grd.addColorStop(0, "black");
grd.addColorStop("0.3", "magenta");
grd.addColorStop("0.5", "blue");
grd.addColorStop("0.6", "green");
grd.addColorStop("0.8", "yellow");
grd.addColorStop(1, "red");
ctx.fillStyle = grd;
ctx.fillRect(20, 20, 150, 100);

(8)線端樣式

H5中支持三種線端樣式:

描述
butt 默認,向線條的每個末端添加平直的邊緣
round 向線條的每個末端添加圓形線帽
square 向線條的每個末端添加正方形線帽
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineCap = "round";
ctx.moveTo(20, 20);
ctx.lineTo(20, 200);
ctx.stroke();

(9)線交樣式

H5中支持三種線端樣式:

描述
bevel 創建斜角
round 創建圓角
miter 默認,創建尖角
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineJoin = "round";
ctx.moveTo(20, 20);
ctx.lineTo(100, 50);
ctx.lineTo(20, 100);
ctx.stroke();

斜接長度
這裏不得不提一個很冷門的屬性叫斜接長度,它是隻兩條線段相交時,並且lineJoin="miter",內角和外交的距離。


...
ctx.lineJoin = "miter";
ctx.miterLimit = 5;
...

爲了避免斜接長度過長,我們可以使用 miterLimit 屬性。如果斜接長度超過 miterLimit 的值,邊角會以 lineJoin 的 "bevel" 類型來顯示


(10)設置線寬

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 10;
ctx.strokeRect(30, 30, 200, 80);

(11)繪製矩形

我們可以直接調用fillRect繪製矩形

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#00bcd4";
ctx.fillRect(20, 20, 150, 100);

也可以先調用rect,再調用fill

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#00bcd4";
ctx.rect(20, 20, 150, 100);
ctx.fill();

把fill換成stroke也是一樣,效果一個是填充,一個是描邊。

(12)清除像素

clearRect()方法可以清除一塊區域的所有像素

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 300, 150);
ctx.clearRect(20, 20, 100, 50);

(13)貝塞爾曲線

這是一個大頭,和Android一樣,貝塞爾曲線是構建平面圖形很重要的一個知識點。H5中提供的貝塞爾曲線api還沒有Android中豐富,但是也足夠用了。

函數 釋義
beginPath() 開始一段路徑
moveTo() 移動至一個新的起點,注意區分和beginPath的差異
closePath() 關閉一段路徑
lineTo() 連接到指定點
clip() 從畫布中裁剪出一個可視區域,只有被剪切區域內的像素纔可見
quadraticCurveTo() 二次貝塞爾曲線
bezierCurveTo() 三階貝塞爾曲線
arc() 創建圓弧
arcTo() 創建介於兩個切線之間的弧
isPointInPath() 判斷一個點是不是在封閉路徑內

先畫個最簡單的路徑

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(300, 150);
ctx.stroke();

再畫個三次貝塞爾曲線

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(20, 100, 200, 100, 200, 20);
ctx.stroke();

畫弧線

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(100, 75, 50, 0, Math.PI + Math.PI / 2);
ctx.stroke();

連接切線弧
抱歉,這個api的參數我看了半天,還是沒懂,和我預期效果不一樣。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.beginPath();
ctx.moveTo(20, 20);           // 創建開始點
ctx.lineTo(100, 20);          // 創建水平線
ctx.arcTo(150, 20, 150, 70, 50); // 創建弧
ctx.lineTo(150, 120);         // 創建垂直線
ctx.stroke();                // 進行繪製

(14)畫布操作

在任何繪圖語言中,都少不了操作畫布,js也一樣,canvas也支持幾種操作。

縮放

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.strokeRect(5, 5, 25, 15);
ctx.scale(2, 2);
ctx.strokeRect(5, 5, 25, 15);
ctx.scale(2, 2);
ctx.strokeRect(5, 5, 25, 15);
ctx.scale(2, 2);
ctx.strokeRect(5, 5, 25, 15);

旋轉

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.rotate(20 * Math.PI / 180);
ctx.fillRect(50, 20, 100, 50);

位移

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillRect(10, 10, 100, 50);
ctx.translate(70, 70);
ctx.fillRect(10, 10, 100, 50);

變換
繪製一個矩形;通過 transform() 添加一個新的變換矩陣,再次繪製矩形;添加一個新的變換矩陣,然後再次繪製矩形。請注意,每當您調用 transform() 時,它都會在前一個變換矩陣上構建

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

ctx.fillStyle="yellow";
ctx.fillRect(0, 0, 250, 100)

ctx.transform(1, 0.5, -0.5, 1, 30, 10);
ctx.fillStyle="red";
ctx.fillRect(0, 0, 250, 100);

ctx.transform(1, 0.5, -0.5, 1, 30, 10);
ctx.fillStyle="blue";
ctx.fillRect(0, 0, 250, 100);

transform() 方法替換當前的變換矩陣。它以下面描述的矩陣來操作當前的變換矩陣:

a  c  e
b  d  f
0  0  1
參數 描述
a 水平縮放繪圖
b 水平傾斜繪圖
c 垂直傾斜繪圖
d 垂直縮放繪圖
e 水平移動繪圖
f 垂直移動繪圖

重置變換矩陣
不管之前的變換矩陣是什麼,setTransform()都會重置掉,然後構建新的變換矩陣。所以在下面的例子中,不會顯示紅色矩形,因爲它在藍色矩形下面。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

ctx.fillStyle = "yellow";
ctx.fillRect(0, 0, 250, 100)

ctx.setTransform(1, 0.5, -0.5, 1, 30, 10);
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 250, 100);

ctx.setTransform(1, 0.5, -0.5, 1, 30, 10);
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 250, 100);

(15)字體

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "italic small-caps bold 40px Arial";
ctx.fillStyle = "red";
ctx.fillText("Hello World", 10, 50);
描述
font-style 規定字體樣式。可能的值:normal italic oblique
font-variant 規定字體變體。可能的值:normal small-caps
font-weight 規定字體的粗細。可能的值:normal bold bolder lighter 100 200 300 400 500 600 700 800 900
font-size / line-height 規定字號和行高,以像素計。
font-family 規定字體系列。
caption 使用標題控件的字體(比如按鈕、下拉列表等)。
icon 使用用於標記圖標的字體。
menu 使用用於菜單中的字體(下拉列表和菜單列表)。
message-box 使用用於對話框中的字體。
small-caption 使用用於標記小型控件的字體。
status-bar 使用用於窗口狀態欄中的字體。

(16)文字對齊基線

水平基線

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

// 在位置 150 創建藍線
ctx.strokeStyle = "blue";
ctx.moveTo(150, 20);
ctx.lineTo(150, 170);
ctx.stroke();

ctx.font = "15px Arial";
// 顯示不同的 textAlign 值
ctx.textAlign = "start";
ctx.fillText("textAlign = start", 150, 60);
ctx.textAlign = "end";
ctx.fillText("textAlign = end", 150, 80);
ctx.textAlign = "left";
ctx.fillText("textAlign = left", 150, 100);
ctx.textAlign = "center";
ctx.fillText("textAlign = center", 150, 120);
ctx.textAlign = "right";
ctx.fillText("textAlign = right", 150, 140);
描述
start 默認,文本在指定的位置開始。
end 文本在指定的位置結束。
center 文本的中心被放置在指定的位置。
left 文本左對齊。
right 文本右對齊。

垂直基線

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

//在位置 y=100 繪製藍色線條
ctx.strokeStyle="blue";
ctx.moveTo(5,100);
ctx.lineTo(395,100);
ctx.stroke();

ctx.font = "20px Arial"
//在 y = 200 以不同的 textBaseline 值放置每個單詞
ctx.textBaseline = "top";
ctx.fillText("Top", 5, 100);
ctx.textBaseline = "bottom";
ctx.fillText("Bottom", 50, 100);
ctx.textBaseline = "middle";
ctx.fillText("Middle", 120, 100);
ctx.textBaseline = "alphabetic";
ctx.fillText("Alphabetic", 190, 100);
ctx.textBaseline = "hanging";
ctx.fillText("Hanging", 290, 100);
描述
alphabetic 默認。文本基線是普通的字母基線。
top 文本基線是 em 方框的頂端。
hanging 文本基線是懸掛基線。
middle 文本基線是 em 方框的正中。
ideographic 文本基線是表意基線。
bottom 文本基線是 em 方框的底端。

(17)文字測量

注意:這個測量只能拿到寬度,不要想當然地取高度。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "25px Arial";
var txt = "Hello World"
ctx.fillText("width:" + ctx.measureText(txt).width, 10, 50);
ctx.fillText(txt, 10, 100);

(18)繪製圖片

繪製圖片提供了三個層級的api:簡單繪製、可控大小、可控裁剪

在看代碼之前有必要說一下,和獲取canvas對象一樣,微信小遊戲和H5獲取image對象也不一樣,H5中是通過document.getElementById()獲取的,而微信小遊戲是通過wx.createImage()函數獲取的。

簡單繪製

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("image");
ctx.drawImage(img, 10, 10);

可控大小

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("image");
ctx.drawImage(img, 10, 10, 240, 160);

可控裁剪

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("image");
ctx.drawImage(img, 90, 130, 90, 80, 20, 20, 90, 80);

(19)ImageData

這是一個比較好玩的類,它定義一個Image的數據,我們可以自己創建一個空的ImageData,然後手動給每一個像素設置RGBA。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var imageData = ctx.createImageData(100, 100);
for (var i = 0;i < imageData.data.length; i+=4)
{
  imageData.data[i + 0] = 255;
  imageData.data[i + 1] = 0;
  imageData.data[i + 2] = 0;
  imageData.data[i + 3] = 255;
}
ctx.putImageData(imageData, 10, 10);

createImageData方法會創建一個空的ImageData,它是一個數組,數組長度是width * height * 4。每連續的4位代表一個像素,分別是R、G、B、A,默認都是0。所以懂點色彩基礎的都知道,默認就是全透明黑色。

上面例子中,我們給每個像素都賦值綠色,最後調用ctx.putImageData把像素繪製到屏幕上。

另外,還提供了一個方法可以根據一個ImageData創建一個同樣大小的ImageData,但是不會複製數據。

var imageData = context.createImageData(imageData);

另外,還有一個更神奇的方法:getImageData,這個方法可以獲取屏幕上任意區域的像素信息。

var imgData = ctx.getImageData(10, 10, 100, 100);

利用這個可以實現一些很好玩的效果,比如對屏幕圖像做色彩反向。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("image");
ctx.drawImage(img, 0, 0);
var imgData = ctx.getImageData(0, 0, c.width, c.height);
// 反轉顏色
for (var i = 0; i < imgData.data.length; i += 4)
{
  imgData.data[i] = 255 - imgData.data[i];
  imgData.data[i + 1] = 255 - imgData.data[i + 1];
  imgData.data[i + 2] = 255 - imgData.data[i + 2];
  imgData.data[i + 3] = 255;
}
ctx.putImageData(imgData, 0, 0);

稍微有點色彩理論基礎的都知道,255 - color就等於color的反響色。當然這是最簡單的,你還可以做很多其它的效果,比如老照片,浮雕等等。

(20)全局透明度

globalAlpha可以設置全局透明度,不同顏色會疊加顯示。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(20, 20, 75, 50);
// 調節透明度
ctx.globalAlpha = 0.2;
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 75, 50);
ctx.fillStyle = "green";
ctx.fillRect(80, 80, 75, 50);

(21)圖層混合模式

這個和Android中的XFermode差不多。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

ctx.fillStyle = "red";
ctx.fillRect(20, 20, 75, 50);
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 75, 50);

ctx.fillStyle = "red";
ctx.fillRect(150, 20, 75, 50);
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = "blue";
ctx.fillRect(180, 50, 75, 50);
描述
source-over 默認。在目標圖像上顯示源圖像。
source-atop 在目標圖像頂部顯示源圖像。源圖像位於目標圖像之外的部分是不可見的。
source-in 在目標圖像中顯示源圖像。只有目標圖像內的源圖像部分會顯示,目標圖像是透明的。
source-out 在目標圖像之外顯示源圖像。只會顯示目標圖像之外源圖像部分,目標圖像是透明的。
destination-over 在源圖像上方顯示目標圖像。
destination-atop 在源圖像頂部顯示目標圖像。源圖像之外的目標圖像部分不會被顯示。
destination-in 在源圖像中顯示目標圖像。只有源圖像內的目標圖像部分會被顯示,源圖像是透明的。
destination-out 在源圖像外顯示目標圖像。只有源圖像外的目標圖像部分會被顯示,源圖像是透明的。
lighter 顯示源圖像 + 目標圖像。
copy 顯示源圖像。忽略目標圖像。
xor 使用異或操作對源圖像與目標圖像進行組合。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章