Canvas绘制
- 一、介绍
- 效果图
- 二、画圆
- 1 写一个页面
- 2 画一个圆(点)
- 3 效果
- 三 画直线
- 1 写一个页面
- 2 画直线
- 3 效果
- 四 用直线连接两个点
- 1 写一个页面
- 2 连线
- 3 效果
- 五 画随机点
- 1 写一个页面
- 2 随机点
- 3 效果
- 六 画随机点并连线
- 1 写一个页面
- 2 画点连线
- 3 效果
- 七 动起来(动画)
- 1 写一个页面
- 2 动起来
- 3 效果
- 八 添加鼠标
- 1 写一个页面
- 2 添加鼠标
- 3 效果
- 九 鼠标吸附
- 1 写一个页面
- 2 鼠标吸附
- 3 效果
Canvas是HTML5新增的一个元素,它提供了JS绘制图形的API。
一、介绍
Canvas常用的4种API:
- 画直线 lineTo
- 画弧线 arc
- 画文字 fillText 或 strokeText
- 画图片 drawImage
今日任务:手撸下面的动图(使用了弧线,直线,鼠标监听,动画)
- 编写步骤:
画点 --> 画线 -> 随机N个点 -> 所有点连线 -> 动画 -> 鼠标进入/移出 -> 鼠标吸附 -> 完成
效果图
二、画圆
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 画一个圆(点)
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
// 初始化画布
function init() {
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
}
init()
// 画一个空心圆
ctx.beginPath();
// xy 圆心坐标, 半径, 起始角度,结束角度
ctx.arc(100,50,6,0,2 * Math.PI)
// 描边颜色
ctx.strokeStyle = '#fff';
// 画 描边
ctx.stroke();
// 画一个实心圆
ctx.beginPath();
// xy 圆心坐标, 半径, 起始角度,结束角度
ctx.arc(200,100,6,0,2 * Math.PI)
// 填充颜色
ctx.fillStyle = '#fff';
// 画 填充
ctx.fill();
3 效果
三 画直线
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 画直线
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
// 初始化画布
function init() {
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
}
init()
// 开启画路径
ctx.beginPath();
// 将画笔移动到此处 开始画
ctx.moveTo(100,50);
// 画笔经过的点
ctx.lineTo(200,100);
ctx.lineTo(300,300);
ctx.lineTo(300,500);
// 关闭路径(首位自动相连)
ctx.closePath();
// // 描边颜色
// ctx.strokeStyle = '#fff';
// // 画 描边
// ctx.stroke();
// 填充颜色
ctx.fillStyle = '#fff';
// 画 填充
ctx.fill();
3 效果
四 用直线连接两个点
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 连线
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
// 初始化画布
function init() {
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
}
init()
// 开启画路径
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(300,200);
ctx.strokeStyle = '#fff'
ctx.stroke();
// 画点 ()
ctx.beginPath();
ctx.arc(100,100,6,0,2 * Math.PI);
ctx.fillStyle='#fff';
ctx.fill();
ctx.beginPath();
ctx.arc(300,200,6,0,2 * Math.PI);
ctx.fillStyle='#fff';
ctx.fill();
3 效果
五 画随机点
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 随机点
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
// 点数量
const pointNumber = 88;
// 初始化画布
function init() {
cvs.width = window.innerWidth * devicePixelRatio;
cvs.height = window.innerHeight * devicePixelRatio;
}
init()
/**
* 获取[min, max]范围内的随机整数
*/
function getRandom(min, max) {
return Math.floor(Math.random() * (max + 1 - min) + min)
}
/**
* 画点
* 随机生成点坐标
*/
class Point {
//构造
constructor() {
this.r = 6;
this.x = getRandom(0,cvs.width - this.r / 2)
this.y = getRandom(0,cvs.height - this.r / 2)
}
// 开始画
draw(){
//画点
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2 * Math.PI);
ctx.fillStyle='rgb(200,200,200)';
ctx.fill();
this.lastDrawTime = Date.now();
}
}
class Graph{
constructor() {
this.points = new Array(pointNumber).fill(0).map(() => new Point())
}
draw(){
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
p1.draw();
}
}
}
const g = new Graph();
g.draw();
3 效果
六 画随机点并连线
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 画点连线
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
// 点数量
const pointNumber = 88;
// 两点连线最大距离
const maxDis = 150;
// 初始化画布
function init() {
cvs.width = window.innerWidth * devicePixelRatio;
cvs.height = window.innerHeight * devicePixelRatio;
}
init()
/**
* 获取[min, max]范围内的随机整数
*/
function getRandom(min, max) {
return Math.floor(Math.random() * (max + 1 - min) + min)
}
/**
* 画点
* 随机生成点坐标
*/
class Point {
//构造
constructor() {
this.r = 6;
this.x = getRandom(0,cvs.width - this.r / 2)
this.y = getRandom(0,cvs.height - this.r / 2)
}
// 开始画
draw(){
//画点
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2 * Math.PI);
ctx.fillStyle='rgb(200,200,200)';
ctx.fill();
this.lastDrawTime = Date.now();
}
}
class Graph{
constructor() {
this.points = new Array(pointNumber).fill(0).map(() => new Point())
}
draw(){
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
p1.draw();
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
for (let j = 0; j < this.points.length; j++) {
const p2 = this.points[j];
// 计算两点间距离(利用直角三角形 a方 = b方 = c方, c就是两点间距离)
const d = Math.sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)
if(d > maxDis){
continue;
}
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = `rgba(200,200,200, ${1 - d / maxDis})`
ctx.stroke();
}
}
}
}
}
const g = new Graph();
g.draw();
3 效果
七 动起来(动画)
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 动起来
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
// 点数量
const pointNumber = 88;
// 两点连线最大距离
const maxDis = 150;
// 初始化画布
function init() {
cvs.width = window.innerWidth * devicePixelRatio;
cvs.height = window.innerHeight * devicePixelRatio;
}
init()
/**
* 获取[min, max]范围内的随机整数
*/
function getRandom(min, max) {
return Math.floor(Math.random() * (max + 1 - min) + min)
}
/**
* 画点
* 随机生成点坐标
*/
class Point {
//构造
constructor() {
this.r = 2;
this.x = getRandom(0,cvs.width - this.r / 2)
this.y = getRandom(0,cvs.height - this.r / 2)
this.xSpeed = getRandom(-50,50);
this.ySpeed = getRandom(-50,50);
this.lastDrawTime = null;
}
// 开始画
draw(){
//更新坐标
if(this.lastDrawTime){
const duration = (Date.now() - this.lastDrawTime) / 1000;
//距离
const xDis = this.xSpeed * duration,
yDis = this.ySpeed * duration;
// 新的坐标
let x = this.x + xDis,
y = this.y + yDis;
// 点到边界回弹
if(x > cvs.width - this.r / 2){
x = cvs.width - this.r / 2
this.xSpeed = -this.xSpeed;
}else if(x < 0){
x = 0;
this.xSpeed = -this.xSpeed;
}
if(y > cvs.height - this.r / 2){
y = cvs.height - this.r / 2
this.ySpeed = -this.ySpeed;
}else if(y < 0){
y = 0;
this.ySpeed = -this.ySpeed;
}
this.x = x;
this.y = y;
}
//画点
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2 * Math.PI);
ctx.fillStyle='rgb(200,200,200)';
ctx.fill();
this.lastDrawTime = Date.now();
}
}
class Graph{
constructor() {
this.points = new Array(pointNumber).fill(0).map(() => new Point())
}
draw(){
requestAnimationFrame(() => {
this.draw()
})
// 清除画布
ctx.clearRect(0,0,cvs.width,cvs.height)
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
p1.draw();
for (let j = 0; j < this.points.length; j++) {
const p2 = this.points[j];
// 计算两点间距离(利用直角三角形 a方 = b方 = c方, c就是两点间距离)
const d = Math.sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)
if(d > maxDis){
continue;
}
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = `rgba(200,200,200, ${1 - d / maxDis})`
ctx.stroke();
}
}
}
}
const g = new Graph();
g.draw();
3 效果
不上动图了,还要制作gif,麻烦…
八 添加鼠标
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 添加鼠标
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
//鼠标最后的坐标
let mouseLastX = 0;
let mouseLastY = 0;
// 点数量
const pointNumber = 88;
// 两点连线最大距离
const maxDis = 150;
// 初始化画布
function init() {
cvs.width = window.innerWidth * devicePixelRatio;
cvs.height = window.innerHeight * devicePixelRatio;
// 鼠标移动事件
cvs.addEventListener('mousemove', function(event) {
// 鼠标进入 怎加鼠标 点,
var canvasRect = cvs.getBoundingClientRect();
var mouseX = event.clientX - canvasRect.left;
var mouseY = event.clientY - canvasRect.top;
// console.log("鼠标在canvas中的坐标:(" + mouseX + ", " + mouseY + ")");
const d = Math.sqrt((mouseLastX-mouseX)**2 + (mouseLastY-mouseY)**2)
// 移动距离
if(d > 5){
if(g.points.length > pointNumber){
g.points.pop();
}
g.points.push(new Point(mouseX,mouseY,0));
mouseLastX = mouseX;
mouseLastY = mouseY;
}
});
// 添加鼠标离开事件监听器
cvs.addEventListener('mouseleave', function(event) {
g.points.pop();
mouseLastX = 0;
mouseLastY = 0;
});
}
init()
/**
* 获取[min, max]范围内的随机整数
*/
function getRandom(min, max) {
return Math.floor(Math.random() * (max + 1 - min) + min)
}
/**
* 画点
* 随机生成点坐标
*/
class Point {
//构造
constructor(x = null,y = null, r = 2) {
this.r = r;
if(x == null){
this.x = getRandom(0,cvs.width - this.r / 2)
this.y = getRandom(0,cvs.height - this.r / 2)
}else{
this.x =x;
this.y =y;
}
this.xSpeed = getRandom(-50,50);
this.ySpeed = getRandom(-50,50);;
this.lastDrawTime = null;
}
// 开始画
draw(isUpdate = true){
// 是否需要更新坐标
if(isUpdate){
//更新坐标
if(this.lastDrawTime){
const duration = (Date.now() - this.lastDrawTime) / 1000;
//距离
const xDis = this.xSpeed * duration,
yDis = this.ySpeed * duration;
// 新的坐标
let x = this.x + xDis,
y = this.y + yDis;
// 点到边界回弹
if(x > cvs.width - this.r / 2){
x = cvs.width - this.r / 2
this.xSpeed = -this.xSpeed;
}else if(x < 0){
x = 0;
this.xSpeed = -this.xSpeed;
}
if(y > cvs.height - this.r / 2){
y = cvs.height - this.r / 2
this.ySpeed = -this.ySpeed;
}else if(y < 0){
y = 0;
this.ySpeed = -this.ySpeed;
}
this.x = x;
this.y = y;
}
}
//画点
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2 * Math.PI);
ctx.fillStyle='rgb(200,200,200)';
ctx.fill();
this.lastDrawTime = Date.now();
}
}
class Graph{
constructor() {
this.points = new Array(pointNumber).fill(0).map(() => new Point())
}
draw(){
requestAnimationFrame(() => {
this.draw()
})
// 清除画布
ctx.clearRect(0,0,cvs.width,cvs.height)
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
// 最后一个是鼠标,不更新坐标
p1.draw(i < pointNumber);
for (let j = 0; j < this.points.length; j++) {
const p2 = this.points[j];
// 计算两点间距离(利用直角三角形 a方 = b方 = c方, c就是两点间距离)
const d = Math.sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)
if(d > maxDis){
continue;
}
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = `rgba(200,200,200, ${1 - d / maxDis})`
ctx.stroke();
}
}
}
}
const g = new Graph();
g.draw();
3 效果
九 鼠标吸附
1 写一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas {
position: fixed;
left: 0px;
top: 0px;
background: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="./canvasjs03.js"></script>
</body>
</html>
2 鼠标吸附
// 在画布上画 随机点,点与点之间连线,鼠标移动,鼠标移出,吸附 例子
// 获取画布
const cvs = document.querySelector('canvas');
// 获取画笔
const ctx = cvs.getContext('2d');
//鼠标最后的坐标
let mouseLastX = 0;
let mouseLastY = 0;
// 点数量
const pointNumber = 88;
// 两点连线最大距离
const maxDis = 150;
// 初始化画布
function init() {
cvs.width = window.innerWidth * devicePixelRatio;
cvs.height = window.innerHeight * devicePixelRatio;
// 鼠标移动事件
cvs.addEventListener('mousemove', function(event) {
// 鼠标进入 怎加鼠标 点,
var canvasRect = cvs.getBoundingClientRect();
var mouseX = event.clientX - canvasRect.left;
var mouseY = event.clientY - canvasRect.top;
// console.log("鼠标在canvas中的坐标:(" + mouseX + ", " + mouseY + ")");
const d = Math.sqrt((mouseLastX-mouseX)**2 + (mouseLastY-mouseY)**2)
// 移动距离
if(d > 2){
if(g.points.length > pointNumber){
//移除鼠标 点
g.points.pop();
}
//添加鼠标 点
g.points.push(new Point(mouseX,mouseY,0));
// 鼠标移动 吸附效果()
if(mouseLastX > 0){
// 最后一个是 鼠标 不计算
for (let i = 0; i < g.points.length - 1; i++) {
const p0 = g.points[i];
// 计算点与鼠标的距离
const d0 = Math.sqrt((p0.x-mouseX)**2 + (p0.y-mouseY)**2)
// 在鼠标附近的点 进入 吸附效果
if(d0 < maxDis && d0 > maxDis-50){
// 计算xy 是加是减
p0.x > mouseX ? p0.x -= 2 : p0.x += 2;
p0.y > mouseY ? p0.y -= 2 : p0.y += 2;
// // 无需重画
// p0.draw();
}
}
}
//记录鼠标坐标
mouseLastX = mouseX;
mouseLastY = mouseY;
}
});
// 添加鼠标离开事件监听器
cvs.addEventListener('mouseleave', function(event) {
// 移出鼠标 点
g.points.pop();
mouseLastX = 0;
mouseLastY = 0;
});
}
init()
/**
* 获取[min, max]范围内的随机整数
*/
function getRandom(min, max) {
return Math.floor(Math.random() * (max + 1 - min) + min)
}
/**
* 画点
* 随机生成点坐标
*/
class Point {
/**
* 构造
* @param x 点x坐标
* @param y 点y左边
* @param r 点半径
*/
constructor(x = null,y = null, r = 2) {
this.r = r;
if(x == null){
this.x = getRandom(0,cvs.width - this.r / 2)
this.y = getRandom(0,cvs.height - this.r / 2)
}else{
this.x =x;
this.y =y;
}
// 点随机变化位置时的 步长
this.xSpeed = getRandom(-50,50);
this.ySpeed = getRandom(-50,50);
this.lastDrawTime = null;
}
// 开始画
draw(isUpdate = true){
// 是否需要更新坐标
if(isUpdate){
//更新坐标
if(this.lastDrawTime){
const duration = (Date.now() - this.lastDrawTime) / 1000;
//距离
const xDis = this.xSpeed * duration,
yDis = this.ySpeed * duration;
// 新的坐标
let x = this.x + xDis,
y = this.y + yDis;
// 点到边界回弹
if(x > cvs.width - this.r / 2){
x = cvs.width - this.r / 2
this.xSpeed = -this.xSpeed;
}else if(x < 0){
x = 0;
this.xSpeed = -this.xSpeed;
}
if(y > cvs.height - this.r / 2){
y = cvs.height - this.r / 2
this.ySpeed = -this.ySpeed;
}else if(y < 0){
y = 0;
this.ySpeed = -this.ySpeed;
}
this.x = x;
this.y = y;
}
}
//画点
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2 * Math.PI);
ctx.fillStyle='rgb(200,200,200)';
ctx.fill();
this.lastDrawTime = Date.now();
}
}
/**
* 画线
*/
class Graph{
constructor() {
// 生成 pointNumber 个点
this.points = new Array(pointNumber).fill(0).map(() => new Point())
}
draw(){
/**
* 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
*/
requestAnimationFrame(() => {
this.draw()
})
// 清除画布
ctx.clearRect(0,0,cvs.width,cvs.height)
for (let i = 0; i < this.points.length; i++) {
const p1 = this.points[i];
// 最后一个是鼠标,不更新坐标
p1.draw(i < pointNumber);
// 链接两点间 直线
for (let j = 0; j < this.points.length; j++) {
const p2 = this.points[j];
// 计算两点间距离(利用直角三角形 a方 = b方 = c方, c就是两点间距离)
const d = Math.sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)
if(d > maxDis){
continue;
}
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = `rgba(200,200,200, ${1 - d / maxDis})`
ctx.stroke();
}
}
}
}
const g = new Graph();
g.draw();