实现javascript原生canvas动态画布效果,附完整代码,代码复制可用

😉 你好呀,我是爱编程的Sherry,很高兴在这里遇见你!我是一名拥有十多年开发经验的前端工程师。这一路走来,面对困难时也曾感到迷茫,凭借不懈的努力和坚持,重新找到了前进的方向。我的人生格言是——认准方向,坚持不懈,你终将迎来辉煌!欢迎关注我😁,我将在这里分享工作中积累的经验和心得,希望我的分享能够给你带来帮助。

效果展示

在这里插入图片描述

实现逻辑

实现一个动态画布效果,通常指的是在HTML5的<canvas>元素上绘制随时间变化的图形或动画。JavaScript是控制这些动画的关键。下面是创建一个简单动态画布效果的基本思路:

1. 设置Canvas

首先,在HTML文档中引入一个标签,它将作为绘图区域。通过JavaScript获取这个元素,并初始化其上下文对象,这是所有绘图操作的基础。

<canvas></canvas>
<script>
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
</script>
2. 设置Canvas样式

为了让画布充满整个视窗并且有一个明显的背景色,我们为添加CSS样式

canvas {
	position: absolute;
	left: 0;
	top: 0;
	z-index: 0;
	background-color: steelblue;
}
3. 初始化画布尺寸

确保画布大小适应当前窗口,并在窗口调整大小时重新设置。

// 初始化
function init() {
	cvs.width = window.innerWidth;
	cvs.height = window.innerHeight;
}
init();
4. 创建动画元素

确定你想要在画布上显示什么类型的动画元素。这可以是简单的几何形状,比如圆形、矩形,也可以是更复杂的图案或对象。对于每个元素,你需要定义其属性,如位置、速度、颜色等。

class Point {
    constructor() {
        this.r = 5; // 半径
        this.x = getRandom(0, cvs.width - this.r / 2);  // x坐标
        this.y = getRandom(0, cvs.height - this.r / 2); // y坐标
        this.xSpeed = getRandom(-80, 80);   // x坐标移动速度
        this.ySpeed = getRandom(-80, 80);   // y坐标移动速度
        this.lastDrawTime = null;   // 上一次画的时间
    }
 
    draw() {
        // 更新坐标
        if (this.lastDrawTime) {
            // 计算新的坐标
            const duration = (Date.now() - this.lastDrawTime) / 1000;
            const xDis = this.xSpeed * duration;
            const yDis = this.ySpeed * duration;
            let x = this.x + xDis;
            let y = this.y + yDis;
 
            // 限制移动范围
            if (x > cvs.width - this.r / 2) {
                x = cvs.width - this.r / 2;
                this.xSpeed = -this.xSpeed;
            } else if (x < this.r / 2) {
                x = this.r / 2;
                this.xSpeed = -this.xSpeed;
            }
            if (y > cvs.height - this.r / 2) {
                y = cvs.height - this.r / 2;
                this.ySpeed = -this.ySpeed;
            } else if (y < this.r / 2) {
                y = this.r / 2;
                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 {
    // pointNumber: 小球初始数量
    // maxDis: 小球之间最大的距离
    constructor(pointNumber = 50, maxDis = 350) {
        this.points = new Array(pointNumber).fill(0).map(() => new Point());
        this.maxDis = maxDis;
    }
 
    // 绘画
    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 = i + 1; j < this.points.length; j++) {
                const p2 = this.points[j];
                const d = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) // 两点距离公式
                // 如果两点距离超出最大距离,则不画线
                if (d > this.maxDis) {
                    continue;
                }
                ctx.beginPath();
                ctx.moveTo(p1.x, p1.y);
                ctx.lineTo(p2.x, p2.y);
                ctx.closePath();
                ctx.strokeStyle = `rgba(200,200,200, ${1 - d / this.maxDis})`; // 根据小球之间距离决定颜色深浅
                ctx.stroke();
            }
        }
    }
    addPoint(e) {
	    const p = new Point();
	    p.x = e.clientX;
	    p.y = e.clientY;
	    this.points[this.points.length++] = p;
	    p.draw();
	}
}

定义一个Point类来表示每个动画中的点。每个点都有自己的位置、速度以及边界反弹逻辑。同时,为了简化代码结构,还定义了一个Graph类来管理这些点及其之间的连接。

5. 随机数生成器
function getRandom(min, max) {  
    min = Math.ceil(min);  
    max = Math.floor(max);  
    return Math.floor(Math.random() * (max - min + 1)) + min;  
}
6. 增加交互性

添加事件监听器来响应用户的动作,例如鼠标移动、点击等,以改变动画元素的行为或者状态。

const g = new Graph();
g.draw();
cvs.addEventListener('click', e => {
    // 处理逻辑
    g.addPoint(e);
});
7. 完整代码
<!DOCTYPE html>
<html lang="utf-8">
<head>
	<title>利用canvas画布实现动态背景效果</title>
	<style>
	canvas {
		position: absolute;
		left: 0;
		top: 0;
		z-index: 0;
		background-color: steelblue;
	}
	</style>
</head>
<body>
<canvas></canvas>
<script>
const cvs = document.querySelector('canvas');
const ctx = cvs.getContext('2d');

// 初始化
function init() {
	cvs.width = window.innerWidth;
	cvs.height = window.innerHeight;
}
init();
function getRandom(min, max) {  
    min = Math.ceil(min);  
    max = Math.floor(max);  
    return Math.floor(Math.random() * (max - min + 1)) + min;  
}
class Point {
    constructor() {
        this.r = 5; // 半径
        this.x = getRandom(0, cvs.width - this.r / 2);  // x坐标
        this.y = getRandom(0, cvs.height - this.r / 2); // y坐标
        this.xSpeed = getRandom(-80, 80);   // x坐标移动速度
        this.ySpeed = getRandom(-80, 80);   // y坐标移动速度
        this.lastDrawTime = null;   // 上一次画的时间
    }
 
    draw() {
        // 更新坐标
        if (this.lastDrawTime) {
            // 计算新的坐标
            const duration = (Date.now() - this.lastDrawTime) / 1000;
            const xDis = this.xSpeed * duration;
            const yDis = this.ySpeed * duration;
            let x = this.x + xDis;
            let y = this.y + yDis;
 
            // 限制移动范围
            if (x > cvs.width - this.r / 2) {
                x = cvs.width - this.r / 2;
                this.xSpeed = -this.xSpeed;
            } else if (x < this.r / 2) {
                x = this.r / 2;
                this.xSpeed = -this.xSpeed;
            }
            if (y > cvs.height - this.r / 2) {
                y = cvs.height - this.r / 2;
                this.ySpeed = -this.ySpeed;
            } else if (y < this.r / 2) {
                y = this.r / 2;
                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 {
    // pointNumber: 小球初始数量
    // maxDis: 小球之间最大的距离
    constructor(pointNumber = 50, maxDis = 350) {
        this.points = new Array(pointNumber).fill(0).map(() => new Point());
        this.maxDis = maxDis;
    }
 
    // 绘画
    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 = i + 1; j < this.points.length; j++) {
                const p2 = this.points[j];
                const d = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) // 两点距离公式
                // 如果两点距离超出最大距离,则不画线
                if (d > this.maxDis) {
                    continue;
                }
                ctx.beginPath();
                ctx.moveTo(p1.x, p1.y);
                ctx.lineTo(p2.x, p2.y);
                ctx.closePath();
                ctx.strokeStyle = `rgba(200,200,200, ${1 - d / this.maxDis})`; // 根据小球之间距离决定颜色深浅
                ctx.stroke();
            }
        }
    }
    addPoint(e) {
	    const p = new Point();
	    p.x = e.clientX;
	    p.y = e.clientY;
	    this.points[this.points.length++] = p;
	    p.draw();
	}
}
const g = new Graph();
g.draw();
cvs.addEventListener('click', e => {
    // 处理逻辑
    g.addPoint(e);
});
</script>
</body>
</html>

总结

将上面代码保存为.html文件,在浏览器打开即可查看效果。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sherry Tian

打赏1元鼓励作者

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值