我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!
效果图
一个星星的实现
对视口进行处理: 获取视口是为了当canvas进行绘制的时候,可以在整个视口中进行操作。然后我们就是需要生成星星,为了更加美观好看,我们需要让星星在canvas操作区域中去随机生成星星的位置。当星星生成后,需要控制星星在x轴和y轴方向上按照一定的速度去运行,这样看起来更加有动画效果。最后就是将星星填充到canvas中。
// 获取视口的宽
var width = document.documentElement.clientWidth;
// 获取视口的高
var height = document.documentElement.clientHeight;
获取id为myCanvas的标签,对canvas处理:
// 获取canvas
var canvas = document.getElementById("myCanvas");
// 获取画笔
var ctx = canvas.getContext("2d");
// 赋值canvas的宽
canvas.width = width;
// 赋值canvas的高
canvas.height = height;
// 改变填充色
ctx.fillStyle = "white";
星星x坐标的位置和y坐标的位置进行处理操作:
// 定义星星x值
var x = parseInt(Math.random() * width);
// 定义星星y值
var y = parseInt(Math.random() * height);
// 星星移动速度的x方向
var x_speed = .6;
// 星星移动速度的y方向
var y_speed = .7;
// 星星的半径
var r = 2;
// 绘制星星
// 开启路径
ctx.beginPath();
// 绘制圆
ctx.arc(x, y, r, 0, Math.PI * 2);
// 闭合路径
ctx.closePath();
// 填充
ctx.fill();
最后一步设置定时器,在定时器中进行一系列的操作
// 开启定时器
var timer = setInterval(function() {
// 清屏
ctx.clearRect(0, 0, width, height);
// 移动
x -= x_speed;
y -= y_speed;
// 渲染
// 开启路径
ctx.beginPath();
// 绘制圆
ctx.arc(x, y, r, 0, Math.PI * 2);
// 闭合路径
ctx.closePath();
// 填充
ctx.fill();
}, 20)
优化为面向对象形式
第一步与上面不变,改变的是我们定义了start类,使用面向对象设计方法,看起来更方便,而且可以把星星的相关操作都放到一起。
// 定义Star类
function Star(ctx, x, y, r) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.r = r;
this.x_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1);
this.y_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1);
}
然后讲方法写到类上,定义了移动方法,这样可以在代码各个地方很方便去调用。这里触发边缘是,将方向进行反转操作。
// 方法要写在原型上
// 移动方法
Star.prototype.move = function() {
this.x -= this.x_speed;
this.y -= this.y_speed;
}
// 转向X方法
Star.prototype.changeX = function() {
this.x_speed = - this.x_speed;
}
// 转向Y方法
Star.prototype.changeY = function() {
this.y_speed = - this.y_speed;
}
然后进行渲染方法和初始化,闭合路径使用closePath,定义圆使用arc,填充使用fill方法。
// 渲染方法
Star.prototype.render = function() {
// 开启路径
this.ctx.beginPath();
// 绘制圆
this.ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
// 闭合路径
this.ctx.closePath();
// 填充
this.ctx.fill();
}
// 初始化星星对象
var star = new Star(ctx, Math.random() * width, Math.random() * height, 2);
star.render();
最后修改定时器,定时器中使用清屏clearRect方法,以及边界的判断操作。
// 开启定时器
var timer = setInterval(function() {
// 清屏
ctx.clearRect(0, 0, width, height);
// 移动
star.move();
// 判断边界
if (star.x < 0 || star.x > width) {
star.changeX();
}
if (star.y < 0 || star.y > height) {
star.changeY();
}
// 渲染
star.render();
}, 20)
浏览器显示结果
多个星星处理
我们在面向对象的基础上修改start类的定义
// 定义Star类
function Star(ctx, x, y, r) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.r = r;
this.x_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1) / 5;
this.y_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1) / 5;
}
多个星星需要我们定义一个数组进行存储,这里的100是刚打开页面,就会随机产生的数量,不至于刚打开页面看起来空白的感觉。如果不使用Math.randow,可能星星都出现在一起,看起来不自然。
// 定义数组 用于存放每一个星星对象
var arr = [];
// 初始化星星对象
for (var i = 0; i < 100; i++) {
arr.push(new Star(ctx, Math.random() * width, Math.random() * height, 1));
}
然后在定时器中使用数组循环,对每个星星的进行边界值判断,最后render进行渲染。
// 开启定时器
var timer = setInterval(function() {
// 清屏
ctx.clearRect(0, 0, width, height);
arr.forEach(function(value, index) {
// 移动
value.move();
// 判断边界
if (value.x < 0 || value.x > width) {
value.changeX();
}
if (value.y < 0 || value.y > height) {
value.changeY();
}
// 渲染
value.render();
})
}, 20)
效果图如下
星星连线
定义Star类,分别来表示x位置,y位置,范围和运行的速度。这里的速度自己可以控制,这里使用parseInt进行取整,以及使用Math的random方法进行随机取值,这样更贴切实际。
function Star(ctx, x, y, r) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.r = r;
this.x_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1) / 5;
this.y_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1) / 5;
}
这次我们需要对鼠标操作进行处理,在鼠标移动时,我们需要使用clientX去确定鼠标的位置便于对星星进行处理。将获取的位置赋值给移动的星星,这样鼠标移动到哪里就会于附近的星星进行连线操作。
// 创建鼠标星星对象
var mouse_star = new Star(ctx, 0, 0, 2);
document.onmousemove = function(e) {
// 获取鼠标的位置
var x = e.clientX;
var y = e.clientY;
// 赋值mouse_star对象中的x 和 y值
mouse_star.x = x;
mouse_star.y = y;
}
定时器中需要增加鼠标操作以及星星之间的关系处理,我们需要在星星运动后,对每个星星进行边界处理,这样当星星触到边界,就会进行反向运行。当星星与星星之间的距离在某个范围内时,然后将星星之间进行连线操作,这样看起来更有动画效果,更好看。我们需要注意的是当判断星星x和y范围时,当前星星与周围的星星和星星之前与星星之间的位置判断,都需要使用Math.abs进行处理。
// 开启定时器
var timer = setInterval(function() {
// 清屏
ctx.clearRect(0, 0, width, height);
// 渲染星星对象的方法
mouse_star.render();
arr.forEach(function(value, index) {
// 移动
value.move();
// 判断边界
if (value.x < 0 || value.x > width) {
value.changeX();
}
if (value.y < 0 || value.y > height) {
value.changeY();
}
// 渲染
value.render();
});
// 循环判断
arr.forEach(function(value, index) {
// value表示每一个星星,我们应该拿着这个星星与其它所有星星作比较
for (var i = index + 1; i < arr.length; i++) {
if (Math.abs(value.x - arr[i].x) < 50 && Math.abs(value.y - arr[i].y) < 50) {
// 连线
line(value.x, value.y, arr[i].x, arr[i].y);
}
}
// 判断星星与其它所有星星之间的关系
if (Math.abs(value.x - mouse_star.x) < 150 && Math.abs(value.y - mouse_star.y) < 150) {
// 连线
line(value.x, value.y, mouse_star.x, mouse_star.y);
}
})
}, 20);
然后我们需要添加点击事件,会不断生成星星,将生成的星星通过push方法,统一放到数组中。然后生成的星星就会慢慢移动,最后断开连线,移动到各个地方。这里需要注意的是点击鼠标点击事件是对document进行操作的。
// 给document添加点击事件
// 当点击的时候出现多个星星
document.onmousedown = function(e) {
for (var i = 0; i < 5; i++) {
arr.push(new Star(ctx, e.clientX, e.clientY, 1));
arr.shift();
}
}
最后进行封装,封装line这个函数,方便在各个地方去调用,这样就省去了重复的代码,便于维护起来。其中canvas中一些常用的操作moveTo、lineTo、closePath、stroke。
// 封装函数,传递两个点,绘制两个点之间的线段
function line(x1, y1, x2, y2) {
// 开启路径
ctx.beginPath();
// 移动画笔到某个位置
ctx.moveTo(x1, y1);
// 绘制路径
ctx.lineTo(x2, y2);
// 关闭路径
ctx.closePath();
// 描边
ctx.stroke();
}
完成代码,只需要新建一个html文件,复制全部代码粘贴就行,需要注意背景图片自己可以换一个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
body, html {
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
background-image: url(imgs/sf.jpg);
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script type="text/javascript">
// 获取视口的宽
var width = document.documentElement.clientWidth;
// 获取视口的高
var height = document.documentElement.clientHeight;
// 获取canvas
var canvas = document.getElementById("myCanvas");
// 获取画笔
var ctx = canvas.getContext("2d");
// 赋值canvas的宽
canvas.width = width;
// 赋值canvas的高
canvas.height = height;
// 改变填充色
ctx.fillStyle = "white";
// 改变线条颜色
ctx.strokeStyle = "rgba(255, 255, 123, .4)";
// 改变线宽
ctx.lineWidth = ".3";
// 定义Star类
function Star(ctx, x, y, r) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.r = r;
this.x_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1) / 5;
this.y_speed = (parseInt(Math.random() * 3) + 1) * Math.pow(-1, parseInt(Math.random() * 10) + 1) / 5;
}
// 方法要写在原型上
// 移动方法
Star.prototype.move = function() {
this.x -= this.x_speed;
this.y -= this.y_speed;
}
// 转向X方法
Star.prototype.changeX = function() {
this.x_speed = - this.x_speed;
}
// 转向Y方法
Star.prototype.changeY = function() {
this.y_speed = - this.y_speed;
}
// 渲染方法
Star.prototype.render = function() {
// 开启路径
this.ctx.beginPath();
// 绘制圆
this.ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
// 闭合路径
this.ctx.closePath();
// 填充
this.ctx.fill();
}
// 定义数组 用于存放每一个星星对象
var arr = [];
for (var i = 0; i < 100; i++) {
arr.push(new Star(ctx, Math.random() * width, Math.random() * height, 1));
}
// 创建鼠标星星对象
var mouse_star = new Star(ctx, 0, 0, 2);
document.onmousemove = function(e) {
// 获取鼠标的位置
var x = e.clientX;
var y = e.clientY;
// 赋值mouse_star对象中的x 和 y值
mouse_star.x = x;
mouse_star.y = y;
}
// 开启定时器
var timer = setInterval(function() {
// 清屏
ctx.clearRect(0, 0, width, height);
// 渲染星星对象的方法
mouse_star.render();
arr.forEach(function(value, index) {
// 移动
value.move();
// 判断边界
if (value.x < 0 || value.x > width) {
value.changeX();
}
if (value.y < 0 || value.y > height) {
value.changeY();
}
// 渲染
value.render();
});
// 循环判断
arr.forEach(function(value, index) {
// value表示每一个星星,我们应该拿着这个星星与其它所有星星作比较
for (var i = index + 1; i < arr.length; i++) {
if (Math.abs(value.x - arr[i].x) < 50 && Math.abs(value.y - arr[i].y) < 50) {
// 连线
line(value.x, value.y, arr[i].x, arr[i].y);
}
}
// 判断星星与其它所有星星之间的关系
if (Math.abs(value.x - mouse_star.x) < 150 && Math.abs(value.y - mouse_star.y) < 150) {
// 连线
line(value.x, value.y, mouse_star.x, mouse_star.y);
}
})
}, 20);
// 给document添加点击事件
// 当点击的时候出现多个星星
document.onmousedown = function(e) {
for (var i = 0; i < 5; i++) {
arr.push(new Star(ctx, e.clientX, e.clientY, 1));
arr.shift();
}
}
// 封装函数,传递两个点,绘制两个点之间的线段
function line(x1, y1, x2, y2) {
// 开启路径
ctx.beginPath();
// 移动画笔到某个位置
ctx.moveTo(x1, y1);
// 绘制路径
ctx.lineTo(x2, y2);
// 关闭路径
ctx.closePath();
// 描边
ctx.stroke();
}
</script>
</body>
</html>
一开始使用js进行代码书写,后来使用面向对象将很多方法进行封装,简化代码,这样写起来更容易,而且对多个星星进行操作更加方便,代码维护起来也比较方便。思路来源于以前看到的网站背景,觉得挺好玩的,就手动去实现了一个,这样以后也可以作为自己博客的背景动画效果。
每步的案例源代码已上传,需要的可以下载自己看看,做个网站背景来玩,挺好的。点击星星连线