前言
微信小遊戲推出後一度爆火,一個簡單的跳一跳讓大家玩的不亦樂乎,那麼很多人就在想自己嘗試學學微信小遊戲,開發一個屬於自己的微信小遊戲。現在微信小遊戲的開發都離不開遊戲引擎,用原生小遊戲開發工具開發的很少很少。但是畢竟我不是專業遊戲開發,所有遊戲引擎就不搞了,我們就單純來看原生微信小遊戲開發。
原生微信小遊戲開發全是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 | 使用異或操作對源圖像與目標圖像進行組合。 |