HTML5 Canvas
使用 <canvas>
元素不是非常难,但你需要一些基本的HTML和JavaScript知识。除一些过时的浏览器不支持<canvas>
元素外,所有的新版本主流浏览器都支持它。Canvas 的默认大小为 300 像素 ×150 像素(宽 × 高,像素的单位是 px)。但是,可以使用 HTML 的高度和宽度属性来自定义 Canvas 的尺寸。为了在 Canvas 上绘制图形,我们使用一个 JavaScript 上下文对象,它能动态创建图像(creates graphics on the fly)。
<canvas>
标签定义图形,比如图表和其他图像,您必须使用脚本来绘制图形。
在画布上(Canvas)画一个红色矩形,渐变矩形,彩色矩形,和一些彩色的文字。
1.什么是 canvas?
HTML5 <canvas>
元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成.
<canvas>
标签只是图形容器,您必须使用脚本来绘制图形。
你可以通过多种方法使用 canvas 绘制路径,盒、圆、字符以及添加图像。
2.浏览器支持
表格中的数字表示支持 <canvas>
元素的第一个浏览器版本号。具体参考caniuse
3.创建一个画布(Canvas)
一个画布在网页中是一个矩形框,通过 <canvas>
元素来绘制.
注意: 默认情况下 <canvas ></canvas >
元素没有边框和内容。
<canvas></canvas>
简单实例如下:
<canvas id="myCanvas" width="200" height="100"></canvas>
注意: 标签通常需要指定一个id属性 (脚本中经常引用), width 和 height 属性定义的画布的大小.
**提示:**你可以在HTML页面中使用多个 <canvas>
元素.
使用 style 属性来添加边框:
实例
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;"> </canvas>
4.使用 JavaScript 来绘制图像
canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成:
实例
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
实例解析
首先,找到 <canvas>
元素:
var c=document.getElementById("myCanvas");
然后,创建 context 对象:
var ctx=c.getContext("2d");
getContext("2d")
对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
下面的两行代码绘制一个红色的矩形:
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
设置fillStyle属性可以是CSS颜色,渐变,或图案。fillStyle 默认设置是#000000(黑色)。
fillRect(*x,y,width,height*)
方法定义了矩形当前的填充方式。
5.Canvas 坐标
canvas 是一个二维网格。
canvas 的左上角坐标为 (0,0)
上面的 fillRect 方法拥有参数 (0,0,150,75)。
意思是:在画布上绘制 150x75 的矩形,从左上角开始 (0,0)。
坐标实例
如下图所示,画布的 X 和 Y 坐标用于在画布上对绘画进行定位。
6.Canvas - 路径
在Canvas上画线,我们将使用以下两种方法:
- moveTo(x,y) 定义线条开始坐标
- lineTo(x,y) 定义线条结束坐标
绘制线条我们必须使用到 "ink" 的方法,就像stroke().
实例(直线)
定义开始坐标(0,0), 和结束坐标 (200,100)。然后使用 stroke() 方法来绘制线条:
JavaScript:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.moveTo(0,0);
ctx.lineTo(200,100);
ctx.stroke();
在canvas中绘制圆形, 我们将使用以下方法:
arc(x,y,r,start,stop)
实际上我们在绘制圆形时使用了 "ink" 的方法, 比如 stroke() 或者 fill().
实例(弧线)
使用 arc() 方法 绘制一个圆:
JavaScript:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.arc(95,50,40,0,2*Math.PI);
ctx.stroke();
7.Canvas - 文本
使用 canvas 绘制文本,重要的属性和方法如下:
- font - 定义字体
- fillText(text,x,y) - 在 canvas 上绘制实心的文本
- strokeText(text,x,y) - 在 canvas 上绘制空心的文本
使用 fillText():
实例
使用 "Arial" 字体在画布上绘制一个高 30px 的文字(实心):
JavaScript:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.font="30px Arial";
ctx.fillText("Hello World",10,50);
使用 strokeText():
实例
使用 "Arial" 字体在画布上绘制一个高 30px 的文字(空心):
JavaScript:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.font="30px Arial";
ctx.strokeText("Hello World",10,50);
8.Canvas - 渐变
渐变可以填充在矩形, 圆形, 线条, 文本等等, 各种形状可以自己定义不同的颜色。
以下有两种不同的方式来设置Canvas渐变:
- createLinearGradient(x,y,x1,y1) - 创建线条渐变
- createRadialGradient(x,y,r,x1,y1,r1) - 创建一个径向/圆渐变
当我们使用渐变对象,必须使用两种或两种以上的停止颜色。
addColorStop()方法指定颜色停止,参数使用坐标来描述,可以是0至1.
使用渐变,设置fillStyle或strokeStyle的值为 渐变,然后绘制形状,如矩形,文本,或一条线。
使用 createLinearGradient():
实例
创建一个线性渐变。使用渐变填充矩形:
JavaScript:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
// 创建渐变
var grd=ctx.createLinearGradient(0,0,200,0);
grd.addColorStop(0,"red"); grd.addColorStop(1,"white");
// 填充渐变
ctx.fillStyle=grd; ctx.fillRect(10,10,150,80);
实例
使用 createRadialGradient():
创建一个径向/圆渐变。使用渐变填充矩形:
JavaScript:
var c=document.getElementById("myCanvas");
var ctx=c.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,80);
9.Canvas - 图像
把一幅图像放置到画布上, 使用以下方法:
- drawImage(image,x,y)
使用图像:
实例
把一幅图像放置到画布上:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
ctx.drawImage(img,10,10);
10.使用Canvas绘制背景动图
使用Canvas+ES6的class语法实现背景动图
代码如下
var canvas = document.querySelector('#canvas');
var ctx = canvas.getContext('2d');
//定义canvas的最大宽高
let min = 0;
let maxX;
let maxY;
let currentMouseX;
let currentMouseY;
//鼠标半径
let mouseRadius = 150;
//定义四个方向
const DIRECT_TOP = 0;
const DIRECT_RIGHT = 1;
const DIRECT_BOTTOM = 2;
const DIRECT_LEFT = 3;
//初始化
function init() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
maxX = canvas.width;
maxY = canvas.height;
}
init()
console.log(canvas.width);
console.log(canvas.height);
canvas.addEventListener('mousemove', function (event) {
var canvasRect = canvas.getBoundingClientRect();
var mouseX = event.clientX - canvasRect.left;
var mouseY = event.clientY - canvasRect.top;
currentMouseX = mouseX;
currentMouseY = mouseY;
// console.log("鼠标在canvas中的坐标:(" + mouseX + ", " + mouseY + ")");
});
/**
* 随机生成4个方向
* @returns
*/
function randomDirect() {
let direct = Math.floor(Math.random() * 10) % 4;
switch (direct) {
case 0:
return DIRECT_TOP;
case 1:
return DIRECT_RIGHT;
case 2:
return DIRECT_BOTTOM;
case 3:
return DIRECT_LEFT;
}
// return DIRECT_RIGHT;
}
//点的最大位移距离
const maxMoveDest = 100;
// 引力距离
const GravitationalDistance = 200;
/**
* 定义一个点的类
*/
class Point {
x;
y;
radius;
srcX;
srcY;
direct;
/**
* 创建一个对象,初始化条件,坐标(x,y),
* @param {*} x
* @param {*} y
* @param {*} radius 圆的半径,默认为3,
* @param {*} direct 随机初始一个方向(上、下、左、右)
*/
constructor(x, y, radius = 3, direct = randomDirect()) {
this.x = this.srcX = x;
this.y = this.srcY = y;
this.radius = radius;
this.direct = direct;
}
//绘制一个圆形
draw() {
ctx.beginPath()
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
ctx.fillStyle = '#FFF';
ctx.fill();
}
//随机移动,产生动画的前提
move() {
//每次移动的距离
let rndDest = 2;
switch (this.direct) {
//上移 y 减小
case DIRECT_TOP:
this.y = this.y - rndDest > 0 ? this.y - rndDest : 0;
break;
//右移 x 增加
case DIRECT_RIGHT:
this.x = this.x + rndDest < canvas.width ? this.x + rndDest : canvas.width;
break;
//下移 y 增加
case DIRECT_BOTTOM:
this.y = this.y + rndDest < canvas.height ? this.y + rndDest : canvas.height;
break;
//左移 x 减小
case DIRECT_LEFT:
this.x = this.x - rndDest > 0 ? this.x - Math.random() : 0;
break;
}
// console.log(`point(${this.x},${this.y})`);
//调整方向
this.mayBeChangeDirect();
//根据鼠标距离调整原点的大小
this.changeRadius();
}
/**
* 判断点的移动方向是否需要改变
* 1 移动过程中可能 不应该一直一个方向,移动超过 maxMoveDest 最大位移则随机调整方向
* 2 如果到达边界 则相反反向调整,
* 2.1 上移 且 y <= 0 则方向 向下
* 2.2 下移 且 y >= canvas的高度 则方向 向上
* 2.3 左移 且 x <= 0 则方向 向右
* 2.4 右移 且 x >= canvas的宽度 则方向 向左
*/
mayBeChangeDirect() {
//移动到一定距离之后 随机换方向,并记录开始移动的坐标,为了下一次计算移动距离
if (Math.abs(this.x - this.srcX) > maxMoveDest ||
Math.abs(this.y - this.srcY) > maxMoveDest) {
this.direct = randomDirect();
this.resetSrc();
}
// console.log(`this.x:${this.x},this.direct:${this.direct}, DIRECT_LEFT:${DIRECT_LEFT}`);
//到达左侧 向右
if (this.x <= 0 && this.direct == DIRECT_LEFT) {
this.direct = DIRECT_RIGHT
this.resetSrc();
}
//到达顶部,向下
if (this.y <= 0 && this.direct == DIRECT_TOP) {
this.direct = DIRECT_BOTTOM
this.resetSrc();
}
//到达右侧,向左
if (this.x >= canvas.width && this.direct == DIRECT_RIGHT) {
this.direct = DIRECT_LEFT;
this.resetSrc();
}
//到达底部,向上
if (this.y >= canvas.height && this.direct == DIRECT_BOTTOM) {
this.direct = DIRECT_TOP
this.resetSrc();
}
}
//改变方向之后重新计算 开始移动的原点
resetSrc() {
this.srcX = this.x;
this.srcY = this.y
}
/**
* 根据鼠标距离调整原点的大小
*/
changeRadius() {
let mouseDest = Math.sqrt((this.x - currentMouseX) ** 2 + (this.y - currentMouseY) ** 2);
if (mouseDest <= mouseRadius) {
this.radius = 10;
} else {
this.radius = 3;
}
}
/**
* 随机成成 点
* @param {*} x 点的当前x
* @param {*} y 点的当前y
* @param {*} radius 点的半径
* @param {*} direct 点的方向
* @returns
*/
static newPoint(x = Math.floor(Math.random() * (maxX - min) + min),
y = Math.floor(Math.random() * (maxY - min) + min), radius = 3, direct = randomDirect()) {
return new Point(x, y, radius, direct)
}
}
/**
* 图形类
*/
class Graph {
points = [];
/**
*
* @param {Number} nums 初始点数
* @param {Number} maxDigs 最大连线距离
*/
constructor(nums = 80, maxDigs = 120) {
this.maxDigs = maxDigs;
//初始 num个点
for (let i = 0; i < nums; i++) {
this.points.push(Point.newPoint());
}
}
/**
* 绘制点 和 点与点之间的连线
*/
draw() {
//绘制鼠标半径
ctx.beginPath()
ctx.arc(currentMouseX, currentMouseY, mouseRadius, 0, 2 * Math.PI)
ctx.strokeStyle = '#FFF';
ctx.stroke();
ctx.closePath();
for (let i = 0; i < this.points.length; i++) {
let startPoint = this.points[i];
startPoint.draw();
for (let j = i + 1; j < this.points.length; j++) {
let endPoint = this.points[j];
let dest = Math.sqrt((endPoint.x - startPoint.x) ** 2 + (endPoint.y - startPoint.y) ** 2);
// if(dest <this.maxDigs){
// 0 1
// x ?
// 300 0
let alpha = 1 - dest / this.maxDigs;
ctx.beginPath();
let rgba = 'rgba(200,200,200,' + alpha + ')';
// console.log(`dest:${dest} , this.maxDigs:${this.maxDigs} , rgbg:${rgba}`)
ctx.strokeStyle = rgba;
ctx.moveTo(startPoint.x, startPoint.y);
ctx.lineTo(endPoint.x, endPoint.y);
ctx.stroke();
ctx.closePath();
// }
}
}
}
/**
* 移动所有的点位
* 清空画布
* 重新绘制点位 和连线
*/
move() {
for (let i = 0; i < this.points.length; i++) {
let point = this.points[i];
point.move();
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.draw();
}
startAnimation() {
setInterval(() => {
this.move();
}, 100)
}
}
const graph = new Graph();
graph.draw();
graph.startAnimation();
// Math.PI
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div style="background-color:grey;position: fixed;width: 100vw;height: 100vh;">
<canvas id="canvas" width="100vw" height="100vw">
</canvas>
</div>
<script src="./js/canvas.js"></script>
</body>
</html>