文章目录

  • 前言
  • 一、导航
    • 初始化
    • 属性与方法
    • 小demo
  • 二、绘制功能
    • 2.1、绘制矩形
      • 2.1.1、非面向思想实现动画
      • 2.1.2、面向对象思维实现动画
    • 2.2、绘制路径
    • 2.3、绘制圆弧(动态圆形示例)
    • 2.4、设置线型透明度(小球碰撞案例)
    • 2.5、线型的属性
    • 2.6、设置文字
    • 2.7、渐变(线型与径向)
    • 2.8、设置阴影
  • 三、绘制图片
  • 四、资源管理器(手动创建类进行管理)
  • 五、变形
    • 5.1、transloate(移动画布)
    • 5.2、rotate(画布旋转)
    • 5.3、scale(缩小绘制目标)
    • transform(三个元素统一写)
  • 六、合成(globalCompositeOperation)
  • 实战
    • 1、车轮滚动(核心使用rotate)
    • 2、开刮刮乐(核心使用合成)
  • 示例
  • 参考文章
  • 示例
  • 参考文章

前言

本篇博客是Canvas的学习笔记,若文章中出现相关问题,请指出!

所有博客文件目录索引:博客目录索引(持续更新)

一、导航

初始化

Canvas API中文文档首页地图

VScode快捷canvas提示,添加下面一条代码即可:

/** @type {HTMLCanvasElement} */

初始化

<canvas id="test-canvas" width="200" height="200"><p>Current Price: 25.51</p>
</canvas>
<script>/** @type {HTMLCanvasElement} *///通过id来拿到cavas对象var canvas = document.getElementById('test-canvas');if (canvas.getContext) {  //true表示支持;false表示不支持console.log('你的浏览器支持Canvas!');} else {console.log('你的浏览器不支持Canvas!');}//绘制2D图像var context = canvas.getContext('2d');
</script>

canvas的大小由width与height限制,js中创建>这两个值的不会生效!


属性与方法

属性与方法

//清除画布
ctx.clearRect(0, 0, 800, 300);//起点为(0,0),宽800高300//矩形填充
context.fillstyle=blue;   //(选择填充色),默认为black
context.fillRect(10, 10, 100, 100);  //(绘制矩形填充)//矩形绘制
contex.strokeStyle="blue";   //设置描边样式(路径、形状、文字,可设置参数有颜色、渐变、图案)
context.strokeRect(x, y, width, height);  //参数:起点横坐标、起点纵坐标、宽度、高度//***绘制圆形(必须三步骤)***
context.beginPath(); //表示开始新的路径了,和之前路径分开
//context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
context.arc(100, 100, 100, 0, Math.PI / 2 , true);  //Math.PI * 2表示一整个圆,初始从圆的右半径开始顺时针,true的话逆向画圆到指定位置
context.stroke();  //对路径进行描边绘制(真正进行绘制的操作)
//***绘制圆形***

其他属性

contex.lineWidth= 2.0; //线宽度,默认值为1px,单位为px
//方法
context.moveTo(100, 120);  //移动到坐标为(100,120)//前提条件:进行画线点的定位移动ctx.moveTo(50, 20),否则无法画线
context.lineTo(200, 100);  //当前坐标横坐标为200 =》当前坐标的纵坐标100位置,描述绘制路线

小demo

1、路径绘画的较好方法

借助Path2D()

//定义路径
var path = new Path2D();
path.arc(75, 75, 50, 0, Math.PI * 2, true);  //整个圆
path.moveTo(110, 75);  //移动到指定位置(之后进行stoke绘画时省略这段距离)。这里若是不进行移动就会额外划线
path.arc(75, 75, 35, 0, Math.PI, false);   //笑脸
path.moveTo(65, 65);
path.arc(60, 65, 5, 0, Math.PI * 2, true);
path.moveTo(95, 65);
path.arc(90, 65, 5, 0, Math.PI * 2, true);
ctx.strokeStyle = '#0000ff';
ctx.stroke(path);

注意:记住画笔的点一定要移动至绘画开始的地方,对于画圆、矩形这类其中的参数只是进行画的物体的描述,绘画的笔的位置为当前位置!!!所以需要进行额外moveTo定位移动。还有就是第一次进行绘画的位置根据指定的形状或方式决定!

绘制文字

var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = '#666666';
ctx.font = '24px Arial';
ctx.fillStyle = '#333333';
ctx.fillText('带阴影的文字', 20, 40);


二、绘制功能

2.1、绘制矩形

2.1.1、非面向思想实现动画

动画过程:清屏—更新—渲染。

<body><canvas id="mycavas" width="800" height="200px"></canvas><script>/** @type {HTMLCanvasElement} */const canvas = document.getElementById("mycavas");const ct = canvas.getContext("2d");ct.fillstyle = "blue";// console.log(ct);let left = 100;const timer = setInterval(() => {ct.clearRect(0, 0, 800, 200);   //清屏left++;if (left > 600) {clearInterval(timer);}ct.fillRect(left, 100, 200, 200);  //绘制矩形});</script>
</body>


2.1.2、面向对象思维实现动画

面向对象思想实现动画:使用面向对象的方式来维持canvas需要的属性与状态。

<body><canvas id="mycanvas" width="800" height="300px"></canvas><script>/** @type {HTMLCanvasElement} */const mcanvas = document.getElementById("mycanvas");const ctx = mcanvas.getContext("2d");//面向对象思维function rect(x, y, w, h, color) {this.x = x;this.y = y;this.w = w;this.h = h;this.color = color;}//更新操作rect.prototype.update = function () {this.x++}//执行渲染操作rect.prototype.render = function () {ctx.fillStyle = this.color;ctx.fillRect(this.x, this.y, this.w, this.h);}//实例化对象const r1 = new rect(50, 50, 100, 100, "blue");const r2 = new rect(50, 200, 100, 150, "red");setInterval(() => {ctx.clearRect(0, 0, 800, 300);// 更新r1.update();r2.update();// 渲染r1.render();r2.render();}, 10);</script>
</body>


2.2、绘制路径

基本路径绘制

核心API

ctx.moveTo(x,y); //移动到目标位置(x,y),这个移动路径不会被绘制
ctx.lineTo(x,y);// 从当前路径绘制到目标位置(x,y)
ctx.fill();  //填充绘制的路线
ctx.stoke();  //绘制描绘的路径。

目的:设置一个不规则的多边形状态,路径都是闭合的,使用路径进行绘制的时候需要既定的步骤。

如下几个步骤:

  1. 设置路径的起点。
  2. 使用绘制命令画出路径。
  3. 封闭路径。
  4. 填充或者绘制已经封闭路径的形状。
<body><canvas id="mycanvas" width="800" height="300px"></canvas><script>/** @type {HTMLCanvasElement} */const mcanvas = document.getElementById("mycanvas");const ctx = mcanvas.getContext("2d");//创建一个路径ctx.beginPath();// 指定目标点(100,100)ctx.moveTo(100, 100);// 画线至(200,100),下面就是进行绘制一个矩形ctx.lineTo(200, 100);ctx.lineTo(200, 200);ctx.lineTo(100, 200);// 关闭路径ctx.closePath();// 进行绘制ctx.stroke();</script>
</body>

配合定时器动态画线

动态绘制路径:

<body><canvas id="mycanvas" width="800" height="300px"></canvas><script>/** @type {HTMLCanvasElement} */const mcanvas = document.getElementById("mycanvas");const ctx = mcanvas.getContext("2d");//创建一个路径ctx.beginPath();ctx.moveTo(100, 100);//进行动态绘制const arr = [];arr.push({ x: 200, y: 100 });arr.push({ x: 200, y: 200 });arr.push({ x: 100, y: 200 });arr.push({ x: 100, y: 100 });let i = 0;const timer = setInterval(() => {if (i == arr.length) {clearInterval(timer);ctx.fillStyle = "red";ctx.fill();  //填充绘制的路径}ctx.lineTo(arr[i].x, arr[i].y);ctx.stroke();i++;}, 1000);</script>
</body>


2.3、绘制圆弧(动态圆形示例)

核心API:

ctx.beginPath();  //一定要调用开始路径,否则会出现连线情况!
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);
ctx.fill();

示例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>canvas {/* border: 1px solid #000; */background-color: (0, 0, 0, 0.452);position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 10;}.mbody {width: 800px;height: 800px;border: 1px solid #000;z-index: 999;}</style>
</head><body><canvas id="mycanvas" width="1680px" height="1280px"></canvas><div class="mbody"></div><script>/** @type {HTMLCanvasElement} */const mcanvas = document.getElementById("mycanvas");const ctx = mcanvas.getContext("2d");// 存放圆形的数组const cirArr = [];// 实例化圆function circle(x, y, r, color) {this.x = x;this.y = y;this.r = r;this.color = color;// 指定移动方向 [-5-5]this.dictX = parseInt(Math.random() * 10 + 1) - 5;this.dictY = parseInt(Math.random() * 10 + 1) - 5;}// 更新状态circle.prototype.update = function () {//半径减小this.r -= 0.5;//方向改变this.x += this.dictX;this.y += this.dictY;// 一旦半径<0,直接移除该元素if (this.r < 0) {this.remove();}}// 移除当前元素circle.prototype.remove = function () {for (let i = 0; i < cirArr.length; i++) {if (this === cirArr[i]) {cirArr.splice(i, 1);break;}}}// 执行渲染操作circle.prototype.render = function () {ctx.fillStyle = this.color;ctx.beginPath();  //一定要调用开始路径,否则会出现连线情况!ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);ctx.fill();}// 监听鼠标移入事件(实例化圆)mcanvas.addEventListener('mousemove', (e) => {console.log(e.offsetX, e.offsetY);cirArr.push(new circle(e.offsetX, e.offsetY, 20, getColor()));}, false);// 监听器(进行更新以及渲染操作)setInterval(() => {ctx.clearRect(0, 0, mcanvas.width, mcanvas.height);for (let i = 0; i < cirArr.length; i++) {cirArr[i].update();if (cirArr[i] !== undefined) {cirArr[i].render();}}}, 30);// 获取随机颜色function getColor() {const colorStr = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F";const colorArr = colorStr.split(",");let color = "#";for (let i = 0; i < 6; i++) {const num = parseInt(Math.random() * 16);color += colorArr[num];}return color;}console.log(getColor());;console.log(getColor());;</script>
</body></html>


2.4、设置线型透明度(小球碰撞案例)

核心API:

ctx.globalAlpha = 0.1;   //透明度的值为0.0 (完全透明)到 1.0(完全不透明)

示例:

<!doctype html>
<html><head><title>Love</title><meta charset="utf-8" />
</head><body><canvas id="mycanvas">您的浏览器不支持canvas</canvas><script>const mycanvas = document.getElementById("mycanvas");const ctx = mycanvas.getContext("2d");// 设置画布尺寸为当前浏览器的尺寸mycanvas.width = document.documentElement.clientWidth - 21;mycanvas.height = document.documentElement.clientHeight - 21;//小球半径const ballRadius = 10;const lineToLinePath = 120;// 定义小球与鼠标的状态,设置于小球的属性中const condition = 0;  //0=>初始状态(与鼠标无任何关系)  1=>进入鼠标临近范围   2=>小球与鼠标重合   3=>脱离鼠标范围// 获取随机颜色function getColor() {const colorStr = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F";const colorArr = colorStr.split(",");let color = "#";for (let i = 0; i < 6; i++) {const num = parseInt(Math.random() * 16);color += colorArr[num];}return color;}// 定义小球function ball(index) {this.x = Math.random() * mycanvas.width - ballRadius;this.y = Math.random() * mycanvas.height - ballRadius;do {this.directX = parseInt(Math.random() * 15 + 1) - 8;this.directY = parseInt(Math.random() * 15 + 1) - 8;} while (this.directX == 0 || this.directY == 0);this.r = ballRadius;this.color = getColor();this.index = index;this.condition = 0;}//更新操作ball.prototype.update = function () {this.x += this.directX;this.y += this.directY;//鼠标范围中情况if (this.offsetX && this.offsetY) {console.log("xx", Math.abs(this.x - this.offsetX), Math.abs(this.y - this.offsetX), this.offsetX, this.offsetY);if (Math.abs(this.x - this.offsetX) <= 5 && Math.abs(this.y - this.offsetY) <= 5) {this.condition = 2; //进入范围console.log("xxxxx", Math.abs(this.x - this.offsetX), Math.abs(this.y - this.offsetY));this.x = this.offsetX;this.y = this.offsetY;this.directX = 0;this.directY = 0;}if (this.condition == 3) {do {this.directX = parseInt(Math.random() * 15 + 1) - 8;this.directY = parseInt(Math.random() * 15 + 1) - 8;} while (this.directX == 0 || this.directY == 0);this.condition = 0;}}//如果碰到边界了if (this.x < ballRadius || this.x > (mycanvas.width - ballRadius)) {this.directX = -this.directX;}if (this.y < ballRadius || this.y > (mycanvas.height - ballRadius)) {this.directY = -this.directY;}// console.log(this.x, this.y)return this;}//渲染操作ball.prototype.render = function () {ctx.fillStyle = this.color;ctx.beginPath();ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);//画圆// this.drawLine();//画线ctx.fill();// ctx.stroke();// this.drawLine();return this;}//画线操作ball.prototype.drawLine = function () {for (let i = this.index; i < ballArr.length; i++) {if (Math.abs(this.x - ballArr[i].x) < lineToLinePath && Math.abs(this.y - ballArr[i].y) < lineToLinePath) {ctx.beginPath();// ctx.globalAlpha = Math.abs(this.x - ballArr[i].x) / lineToLinePath;ctx.globalAlpha = 0.1;ctx.moveTo(this.x, this.y);ctx.lineTo(ballArr[i].x, ballArr[i].y);ctx.closePath();ctx.stroke();}}}// 小球数组const ballArr = [];//初始化(function init() {for (let i = 0; i < 20; i++) {ballArr.push(new ball(i));}})();console.log(ballArr)// 添加监听器mycanvas.addEventListener("mousemove", (e) => {// console.log(e.offsetX, e.offsetY);for (let i = 0; i < ballArr.length; i++) {if (Math.abs(e.offsetX - ballArr[i].x) < lineToLinePath && Math.abs(e.offsetY - ballArr[i].y) < lineToLinePath) {//保存当前鼠标的位置ballArr[i].offsetX = e.offsetX;ballArr[i].offsetY = e.offsetY;ballArr[i].condition = 1; //进入范围ctx.beginPath();ctx.moveTo(e.offsetX, e.offsetY);ctx.lineTo(ballArr[i].x, ballArr[i].y);ctx.closePath();ctx.stroke();// 改变小球的行径方向 向鼠标靠拢ballArr[i].directX = Math.abs(e.offsetX - ballArr[i].x) > 10 ? (e.offsetX - ballArr[i].x) / 40 : (e.offsetX - ballArr[i].x) / 20;ballArr[i].directY = Math.abs(e.offsetY - ballArr[i].y) > 10 ? (e.offsetY - ballArr[i].y) / 40 : (e.offsetY - ballArr[i].y) / 20;console.log(ballArr[i].directX, ballArr[i].directY)} else if (ballArr[i].condition == 1) {ballArr[i].condition = 3;}}}, false);// 定时器执行更新与渲染操作setInterval(() => {ctx.clearRect(0, 0, mycanvas.width, mycanvas.height);for (let i = 0; i < ballArr.length; i++) {ballArr[i].update().render();}}, 20);</script>
</body></html>


2.5、线型的属性

线型 Line styles

核心API:

ctx.linewidth = 20;  //线型宽度,默认为1.0
ctx.lineCap = "round"; //线的顶部与末端改变形状,包含butt、round、square,默认为butt,使用square会跟随宽度而改变大小
ctx.lineJoin = "round";  //两线连接处的样式,包含round、bevel、miter,默认为miter尖尖的//虚线
ctx.setLineDash([10,20]);  //第一个参数为虚线的长度,第二个为虚线之间的距离长度。也可以是多个数,多个数时就是对应你想要线型的规律。
ctx.lineDashOffset = 10;//虚线开始的起始偏移量,设置了之后虚线的位置向后偏移指定位置

2.6、设置文字

绘制文本

核心API:

//样式
ctx.font = "48px serif";  //设置文字字体
textAlign = "center"; //位置
//填充文本
ctx.fillText("changlu", 100, 100);  //设置文字,后面两个参数为x轴与y轴
//文本用当前的边框样式被绘制
ctx.strokeText("changlu", 100, 100);
//测量文本宽度
var text = ctx.measureText("foo"); // 拿到文本对象,可以参考其中的属性
text.width; // 16;

2.7、渐变(线型与径向)

核心:使用渐变可以赋值给fillStyle或其他样式,之后进行绘制路径就会有对应的效果!

线型渐变:一个矩形

示例:

// 设置起点x,y以及终点x,y轴
var gradient = ctx.createLinearGradient(20, 0, 220, 0);// 整个过程为0-1,你中间可以设置多个颜色
gradient.addColorStop(0, 'green');
gradient.addColorStop(.5, 'cyan');
gradient.addColorStop(1, 'green');
// 设置填充对象就是上面的gradient
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 200, 100);

径向渐变:放射型渐变

示例:实现发光小球

// 前三个参数为圆的坐标及半径,后三个同样如此
var gradient = ctx.createRadialGradient(100, 100, 100, 100, 100, 50);
gradient.addColorStop(0, "white");//设置白色
gradient.addColorStop(1, "yellow");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);


2.8、设置阴影

示例:核心API为如下1-4行

ctx.shadowOffsetX = 2;//负值表示阴影会往上或左延伸,正值则表示会往下或右延伸
ctx.shadowOffsetY = 2;//负值表示阴影会往上或左延伸,正值则表示会往下或右延伸
ctx.shadowBlur = 2;//设定阴影的模糊程度
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";//设定阴影颜色效果
ctx.font = "50px serif";  //设置文字字体
ctx.fillStyle = "red"
ctx.fillText("❤", 100, 100);


三、绘制图片

核心:必须要onload之后才能够绘制图片。

//实例化image属性
const img = new Image();
img.src = "./images/test.png";
// 必须要在onload挂载之后绘制图片
img.onload = function () {ctx.drawImage(img, 0, 0); //后两个参数为绘画的路径位置
}

上面是三个参数的,若是有五个参数,多出来的参数能够设置宽、高:

ctx.drawImage(img, 0, 0, 600, 400);

若是有9个参数,除开第一个前面四个指的是原图进行的切片,后面四个指的是切片放置的位置:

  • 含义:从原图上的(408,81)位置切下来宽147、高182的切片图放置到canvas中(200,200)位置的宽147、高182。

切片结果:

目的:就是截取原图上指定区域并且最终绘制到我们的指定canvas画布上!

实操

我们实操一下:

//实例化image属性
const img = new Image();
img.src = "./images/test.png";
// 必须要在onload挂载之后绘制图片
img.onload = function () {ctx.drawImage(img, 369, 435, 1181, 360, 0, 0, 1181, 360);
}

达到目标:


四、资源管理器(手动创建类进行管理)

设立资源管理员的原因是在开发例如游戏的时候,会有一些静态资源需要请求回来的,若是直接开始,某些静态资源没有获取到页面上就会显示空或者直接报错,举例子如游戏背景图界面等,所以我们要手动创建一个资源管理器来进行管理我们的整个进程。

示例:一旦我们的资源加载完毕了,就可以开始使用资源了如下图的绘制多张图片操作!

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>使用桃心形方程绘制爱心</title>
</head><body><canvas id="mycanvas" width="1500" height="400"></canvas><script>/** @type {HTMLCanvasElement} */var canvas = document.getElementById('mycanvas');var ctx = canvas.getContext('2d');//创建资源管理器function Game() {this.dom = document.getElementById('mycanvas');this.ctx = this.dom.getContext("2d");this.R = {"1": "./images/1.png","2": "./images/2.jpg","3": "./images/3.jpg","4": "./images/4.png",}// 加载资源const counts = Object.keys(this.R).length;let count = 0;let height = 50;for (let key in this.R) {const src = this.R[key];this.R[key] = new Image();this.R[key].src = src;const self = this;// 异步方法:游戏开始方法函数只能够写在其内部中进行!!!this.R[key].onload = () => {count++;this.ctx.clearRect(10, 10, 200, 50);self.ctx.font = "12px serif";self.ctx.fillText("资源" + count + "已加载完毕!", 10, 30);console.log(height);// self.ctx.drawImage(self.R[key], 10, height, 200, 150);height += 150;if (count == counts) {this.start();//等待资源加载完毕之后可以开始游戏!}}}}// 等待资源结束之后开始执行......Game.prototype.start = function () {let width = 10;//业务逻辑for (let item in this.R) {this.ctx.drawImage(this.R[item], width, 50, 200, 150)width += 220;}}console.log(new Game());;</script>
</body></html>


五、变形

5.1、transloate(移动画布)

核心API

//变形
ctx.transloate(50,50); //将canvas画布进行x轴、y轴平移,平移之后绘制图时就需要按照平移后的坐标进行
//保存当前状态
ctx.save();//举个例子吧你是用translate进行画布平移前使用保存,之后恢复状态再
//回复到以前的状态(包含画布原本位置,线、填充样式等等),已经绘画好的不会消失
ctx.restore();

通常进行变形的画布会不可控,所以我们需要配合save与restore来进行保存与恢复状态,从而可控。

示例

<body><canvas id="mycanvas" width="1500" height="400"></canvas><script>/** @type {HTMLCanvasElement} */var canvas = document.getElementById('mycanvas');var ctx = canvas.getContext('2d');// 保存画布状态ctx.save();ctx.fillStyle = "red";// 移动画布位置ctx.translate(100, 40);// ctx.rotate(0.5);ctx.arc(0, 0, 40, 0, Math.PI * 2, false);ctx.fill();// 恢复画布之前状态ctx.restore();ctx.fillRect(0, 100, 50, 50);</script>
</body>


5.2、rotate(画布旋转)

效果:将整个画布来进行来进行指定角度的旋转,左上角为中心点。

核心API:

ctx.rotate(0.5);   //表示整个画布顺时针旋转5°

注意点:对于restore之前的绘制的图片好像木有影响!

示例

<body><canvas id="mycanvas" width="1500" height="400"></canvas><script>/** @type {HTMLCanvasElement} */var canvas = document.getElementById('mycanvas');var ctx = canvas.getContext('2d');ctx.save();ctx.fillStyle = "red";ctx.translate(100, 40);// ctx.rotate(0.5);ctx.arc(0, 0, 40, 0, Math.PI * 2, false);ctx.fill();ctx.restore();// 进行旋转,需要在绘制前进行旋转才有效果ctx.rotate(5 * Math.PI / 180);ctx.fillRect(0, 100, 50, 50);</script>
</body>


5.3、scale(缩小绘制目标)

包含两个参数:分别表示宽、高缩小的比例。

核心API

ctx.scale(0.8, 0.8);//缩小80%,注意不能够进行放大!

示例

<body><canvas id="mycanvas" width="1500" height="400"></canvas><script>/** @type {HTMLCanvasElement} */var canvas = document.getElementById('mycanvas');var ctx = canvas.getContext('2d');ctx.fillStyle = "black";ctx.scale(1.0, 1.0);//初始状态ctx.fillRect(0, 0, 100, 100);ctx.scale(0.8, 0.8);//缩小80%ctx.fillStyle = "blue";ctx.fillRect(0, 0, 100, 100);ctx.scale(.5, .5)// 缩小50%ctx.fillStyle = "red";ctx.fillRect(0, 0, 100, 100);</script>
</body>

transform(三个元素统一写)

核心API:可以对指定待绘制的元素进行移动、倾斜(旋转)、缩放的设置,就正好对应了前面的三个属性

ctx.setTransform(a, b, c, d, e, f);
a (m11)水平缩放。
b (m12)垂直倾斜。
c (m21)水平倾斜。
d (m22)垂直缩放。
e (dx)水平移动。
f (dy)垂直移动。

示例

<body><canvas id="mycanvas" width="1500" height="400"></canvas><script>/** @type {HTMLCanvasElement} */var canvas = document.getElementById('mycanvas');var ctx = canvas.getContext('2d');//水平缩放:1  垂直倾斜:1  水平倾斜:0  1-3  垂直倾斜指的是垂直向下倾斜;反之平行向右//垂直缩放:1  水平移动:0  垂直移动:0  4-6ctx.setTransform(1, 0, 1, 1, 0, 0);ctx.fillRect(0, 0, 100, 100);</script>
</body>


六、合成(globalCompositeOperation)

官文:组合 Compositing

核心API

# 在绘制的两个图案之间设置属性,不仅可以在已有图形后面再画新图形,还可以用来遮盖指定区域,清除画布中的某些部分
ctx.globalCompositeOperation = "destination-out";   # 该属性就是后面绘制的图案会压盖掉原本的图案

示例

<body><canvas id="mycanvas" width="400px" height="400px"></canvas><div class="mbody"></div><script>/** @type {HTMLCanvasElement} */const mcanvas = document.getElementById("mycanvas");const ctx = mcanvas.getContext("2d");//绘制的第一个图案ctx.fillRect(50, 50, 100, 100);//设置合成属性  ctx=globalCompositeOperation = "destination-out"; // 绘制的第二个图案ctx.beginPath();ctx.fillStyle = "red";ctx.arc(150, 150, 50, 0, 7, false);ctx.fill();</script>
</body>

当在canvas板上绘制多个图案时,如下图后绘制的会有压盖效果:

若是我们想要对压盖的内容做一些处理,如压盖部分清除、只留下压盖部分等等等等,都可以在中间设置指定的合成样式就会有对应的效果啦!

对应案例如下:

还有…


实战

1、车轮滚动(核心使用rotate)

提前知识预备

需要用到属性:rotate、translate、drawImg…

核心是rotate属性进行轮子旋转转动:我们知道rotate属性是针对于canvas画布左上角作为中心点进行顺时针移动的,我们要想让某个图片实现自转效果,需要将该图片圆心位置置于右上角,之后配合rotate属性实现滚动效果!!!

例如:图书属性为400*400,我们要想让图片中心位于画布左上角,则需要指定绘制位置

const img = new Image();
img.src = "./images/wheel.png";
img.onload = () => {ctx.drawImage(img, -400 / 2, -400 / 2);  //通常这里来进行指定绘画位置
}
  • 注意:若是图片过大,请直接在外部设置图片的长宽,不要使用scale属性缩小,之后在进行旋转时会出现问题!

接着我们设置一个定时器即可让其进行转动起来!

OK了之后我们配合translate属性将其移至到我们屏幕中央,这就又需要配合save与restore属性了,直接见案例!

实际示例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>使用桃心形方程绘制爱心</title><style>canvas {border: 1px solid #000;}</style>
</head><body><canvas id="mycanvas" width="1500" height="400"></canvas><script>/** @type {HTMLCanvasElement} */var canvas = document.getElementById('mycanvas');var ctx = canvas.getContext('2d');const img = new Image();img.src = "./images/wheel.png";//895*895let deg = 0.1;//旋转角度let movePos = 200;//初始移动位置let moveR = 5;//向右移动位置img.onload = () => {// ctx.scale(0.2, 0.2);   //禁止使用scale来进行图片缩放对于滚动效果,会有问题!setInterval(() => {ctx.clearRect(0, 0, canvas.width, canvas.height)deg += 0.5;movePos += moveR;if (movePos == 1500 || movePos == 0) {moveR = -moveR;}//由于使用到了translate就一定要使用save与restore!ctx.save();ctx.translate(movePos, 400 / 2);ctx.rotate(deg)ctx.drawImage(img, -400 / 2, -400 / 2);ctx.restore();}, 30);}</script>
</body></html>


2、开刮刮乐(核心使用合成)

通过利用合成属性来实现的开刮刮乐效果:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>使用桃心形方程绘制爱心</title><style>div {width: 150px;height: 60px;font-size: 20px;line-height: 60px;/* border: 1px solid #000; */text-align: center;position: relative;/* 设置文字不能被选中,也就是不能选中拖动查看文字! */user-select: none;}canvas {position: absolute;left: 0;top: 0;right: 0;}</style>
</head><body><div>超级特等奖<canvas width="150px" height="60px"></canvas></div><script>/** @type {HTMLCanvasElement} */var canvas = document.querySelector("canvas");var ctx = canvas.getContext('2d');// 将div元素进行覆盖ctx.fillStyle = "gray";ctx.fillRect(0, 0, 150, 60);// 使用合成效果(当有新的绘制内容压盖上来时就会清除掉原本的内容)ctx.globalCompositeOperation = "destination-out";// 实现鼠标按下移动才能够进行刮刮乐//监控鼠标是否按下,只有按下了移动才会有擦除功能!let isDown = false;canvas.onmousedown = function () {isDown = true;}canvas.onmouseup = function () {isDown = false;}canvas.onmousemove = function (e) {if (isDown) {console.log(e.offsetX, e.offsetY);ctx.beginPath();ctx.arc(e.offsetX, e.offsetY, 8, 0, 7, false);ctx.fill();}}</script>
</body></html>


示例

用 canvas 做个好玩的网站背景:网站背景粒子效果。


参考文章

[1]. 廖雪峰canvas

超级特等奖

```

[外链图片转存中…(img-WInbZUfD-1652052808606)]


示例

用 canvas 做个好玩的网站背景:网站背景粒子效果。


参考文章

[1]. 廖雪峰canvas

我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习
注明:转载可,需要附带上文章链接

Canvas学习笔记 Canvas的基础知识点相关推荐

  1. Android学习笔记:Android基础知识点(不断更新中)

    1.Android学习笔记:OkHttp 2.Android学习笔记:更新UI的方法(UI线程和非UI线程) 3.Android学习笔记:Volley 4.Android学习笔记:Handler 5. ...

  2. 深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-01-CNN基础知识点

    转载自:http://blog.csdn.net/niuwei22007/article/details/47399913 <CNN基础知识点>From:Convolutional Neu ...

  3. 【学习笔记】Java基础知识点——第7章·集合

    第7章  集合 7.1  Java集合框架 数组其实就是一个集合.集合实际上就是一个容器,是一个对象,可以来容纳其它类型的数据. 集合不能直接存储基本数据类型,另外集合也不能直接存储Java对象,集合 ...

  4. canvas学习笔记

    canvas学习笔记 canvas API中文网 - Canvas API中文文档首页地图 使用canvas来绘制图形 - Web API 接口参考 | MDN 什么是Canvas? Canvas是H ...

  5. unity番外学习笔记-canvas在3d里的设置以及3d物体之间的碰撞

    学习笔记 canvas在3d里的设置 3d物体之间的碰撞 canvas在不同系统中如何做到适配 canvas在3d里的设置 需要在canvas里找到worldspace,canvas才能匹配3d的大小 ...

  6. Canvas 学习笔记1

    #Canvas 学习笔记1 @[Canvas,Nunn,HTML5,javascript] ##前言 相信大家多多少少都有了解过`Canvas`,这里我就不多做解释了,网上也充斥了这方面的知识,很多人 ...

  7. Canvas学习笔记之画线

    Canvas学习笔记之画线 步骤 1.使用getElementById()获取canvas元素. 2.获取canvas的绘制环境getContext(). 3.进行绘制画笔的粗细和颜色定义,分别是li ...

  8. 《Java并发编程实践》学习笔记之一:基础知识

    <Java并发编程实践>学习笔记之一:基础知识 1.程序与进程 1.1 程序与进程的概念 (1)程序:一组有序的静态指令,是一种静态概念:  (2)进程:是一种活动,它是由一个动作序列组成 ...

  9. 菜鸟学习笔记:Java基础篇7(包装类、时间相关类、文件类、异常处理类)

    菜鸟学习笔记:Java其他常用类 基本数据类型包装类 时间处理和文件处理相关类 Date时间类 SimpleDateFormat Calendar日历类 文件类 异常机制 异常的概念 Java异常处理 ...

最新文章

  1. TextKit及应用
  2. java获取当月有几天_你真的能在JAVA开发这条路上面一直坚持下去吗?
  3. 宏定义与const的区别
  4. Python语言规范
  5. 调试Spring源码 5.2.2.RELEASE
  6. java runtime类高级,java入门 -- RunTime类
  7. c mysql二进制,MySQL运用connector C/C+读取二进制字段
  8. canvas绘制图像image
  9. Java script生成apk_lua脚本实现自动生成APK包
  10. ajax排行页面,ajax简单页面
  11. 宁波大学2020计算机技术复试线,宁波大学2020年硕士研究生复试分数线
  12. 别再骂中年人了,对他们好点吧~
  13. 以太坊源码 交易池(一) 基本流程解析
  14. webservice框架jersey简单总结
  15. 将公式直接转化为Latex代码的神器-snip
  16. 基于TCP/IP的展厅智能中控系统
  17. Dubbo-接口数据序列化Serialization
  18. 【源码解析】Activity的启动模式
  19. android-自定义锁屏界面
  20. 阿额企鹅我恶趣味企鹅我去恶趣味

热门文章

  1. html中将scss转编译为css,SASS把scss转化为css的四种转化方式与命令
  2. Tableau基础-第一章(初学者)
  3. 全文索引elasticsearch
  4. POJ3169 Layout , 最短路算法解差分约束问题
  5. [蓝桥杯python] 印章:共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率
  6. java cookie能存到服务器_Cookie技术用于将会话过程中的数据保存到( )中,从而使浏览器和服务器可以更好地进行数据交互。(5.0分)_学小易找答案...
  7. ios9系统无法下载应用问题分析与解决
  8. mysql 按照中文拼音首字母排序
  9. Excel2019选择数据后,指定数据作为x轴
  10. 红米android如何把电池百分比,红米Note4电池怎么显示百分比?简单两步即可轻松搞定!...