H5和微信小游戏 Canvas API 整理前言

时间:2022-06-11
本文章向大家介绍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

使用异或操作对源图像与目标图像进行组合。

随机文章