文章目录
- 二、绘制功能
- 2.2、绘制路径
- 2.3、绘制圆弧(动态圆形示例)
- 2.4、设置线型透明度(小球碰撞案例)
- 2.5、线型的属性
- 2.6、设置文字
- 2.7、渐变(线型与径向)
- 2.8、设置阴影
前言
本篇博客是Canvas的学习笔记,若文章中出现相关问题,请指出!
所有博客文件目录索引:博客目录索引(持续更新)
一、导航
初始化
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(); //绘制描绘的路径。
目的:设置一个不规则的多边形状态,路径都是闭合的,使用路径进行绘制的时候需要既定的步骤。
如下几个步骤:
- 设置路径的起点。
- 使用绘制命令画出路径。
- 封闭路径。
- 填充或者绘制已经封闭路径的形状。
<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();
示例:
<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、线型的属性
核心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-6
ctx.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*895
let 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
超级特等奖
```
示例
用 canvas 做个好玩的网站背景:网站背景粒子效果。
参考文章
[1]. 廖雪峰canvas
我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习
注明:转载可,需要附带上文章链接