# canvas创建一个炫酷背景
怎么能让页面上的元素动起来呢:
- gif 图
- CSS3 动画
- js 控制
- svg
- Canvas
# 效果
这里主要是与鼠标之间的交互效果。
与鼠标之间有互动的效果主要是产生用户行为的反馈,比如在网页制作中,使用 hover 变色表示用户的鼠标在元素上方悬停,这就是用户行为的一种反馈。
我们经常使用的与鼠标之间的交互效果主要有两种:
- 鼠标跟随
- 视觉差
总结一下,炫酷的网页背景特效有哪些特点:
- 背景
- 单一颜色
- 渐变
- 平铺
- 炫酷
- 动
- 随机
- 特效
- 鼠标跟随
- 视觉差
# 实现随机粒子特效
# 创建全屏 Canvas
首先,需要一个全屏的 Canvas 画布。
var ctx = document.getElementById('canvas'),
content = ctx.getContext('2d'),
WIDTH,
HEIGHT;
WIDTH = document.documentElement.clientWidth;
HEIGHT = document.documentElement.clientHeight;
ctx.width = WIDTH;
ctx.height = HEIGHT;
使用 WIDTH、HEIGHT 两个常量储存屏幕宽度和高度信息。
# 设置 Round_item 类
在创建了一个全屏的 Canvas 之后,创建单个的 Round_item 类。
首先 Round_item 类需要有什么参数呢?设置的是位置随机、透明度随机、半径随机的圆。为了区分不同的圆,我们还应该设置一个唯一的 index 参数。
需要的参数有:
- x 坐标
- y 坐标
- 半径
- 透明度
- index
function Round_item(index,x,y) {
this.index = index;
this.x = x;
this.y = y;
this.r = Math.random() * 2 + 1;
var alpha = (Math.floor(Math.random() * 10) + 1) / 10 / 2;
this.color = "rgba(255,255,255," + alpha + ")";
}
这里我们使用了构造函数的方式来创建单个的圆,我们还需要一个变量 initRoundPopulation 来设置 round 的个数,然后我们便可以通过 for 循环创建出 initRoundPopulation 个圆。
# 设置 draw() 方法
在设置了单个的 Round_item 类之后,我们还要给每一个 round 设置 draw() 方法,所以我们需要将 draw() 方法设置在 Round_item 的原型中,这样我们创建出来的每一个 Round_item 实例对象都拥有了 draw() 方法。
Round_item.prototype.draw = function () {
content.fillStyle = this.color;
content.shadowBlur = this.r * 2;
content.beginPath();
content.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
content.closePath();
content.fill();
};
# 设置初始化 init() 函数
然后就需要设置初始化 init() 函数了,在 init() 函数中,我们的主要任务是创建单个的 round,然后使用其 draw() 方法。
function init() {
for(var i = 0; i < initRoundPopulation; i++ ){
round[i] = new Round_item(i,Math.random() * WIDTH,Math.random() * HEIGHT);
round[i].draw();
}
}
至此,已经完成了随机粒子的实现,完整的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html,body {
margin:0;
overflow:hidden;
width:100%;
height:100%;
cursor:none;
background:black;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var ctx = document.getElementById('canvas'),
content = ctx.getContext('2d'),
round = [],
WIDTH,
HEIGHT,
initRoundPopulation = 100;
WIDTH = document.documentElement.clientWidth;
HEIGHT = document.documentElement.clientHeight;
ctx.width = WIDTH;
ctx.height = HEIGHT;
function Round_item(index,x,y) {
this.index = index;
this.x = x;
this.y = y;
this.r = Math.random() * 2 + 1;
var alpha = (Math.floor(Math.random() * 10) + 1) / 10 ;
let a=parseInt(255*Math.random());
let b=parseInt(255*Math.random());
let c=parseInt(255*Math.random());
this.color = `rgba(${a},${b},${c},${alpha} )`;
}
Round_item.prototype.draw = function () {
content.fillStyle = this.color;
content.shadowBlur = this.r * 2;
content.beginPath();
content.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
content.closePath();
content.fill();
};
function init() {
for(var i = 0; i < initRoundPopulation; i++ ){
round[i] = new Round_item(i,Math.random() * WIDTH,Math.random() * HEIGHT);
round[i].draw();
}
}
init();
</script>
</body>
</html>
# 随机粒子动起来
# animate() 函数
其实,Canvas 制作动画是一个不断擦除再重绘的过程。
实现动画需要在很短的时间内不断的清除内容再重新绘制,新的图形和原先清除的图形之间有某种位置关系,速度足够快的话,就会看到动画的效果。
需要一个 animate() 函数,在这个函数中首先需要清除当前屏幕,这里的清除函数用到的是 content.clearRect() 方法。
先来看一下 canvas 的 content.clearRect() 方法:
context.clearRect(x,y,width,height);
- x:要清除的矩形左上角的 x 坐标
- y:要清除的矩形左上角的 y 坐标
- width:要清除的矩形的宽度
- height:要清除的矩形的高度
在刚刚的分析中可以得出,需要清除的区域是整个屏幕,所以 content.clearRect() 的参数就是 content.clearRect(0, 0, WIDTH, HEIGHT);,这里就用到了之前获取的屏幕宽度和高度的常量:WIDTH 和 HEIGHT。这样就将屏幕上的所有内容都清除了。
清除了屏幕内容之后就要重新绘制图形,重新绘制的图形是需要和原图形之间有一定的关系,先制作一个简单的效果 —— 粒子匀速上升。粒子匀速上升,也就是 y 坐标在不断地变化,既然是匀速的,那么也就是在相同的时间位移是相同的。
可以将粒子位移的变化函数 move() 写在 Round_item 的原型上。
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。
所以我们就使用 requestAnimationFrame() 函数递归的调用 animate() 函数来实现动画的效果。
function animate() {
content.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in round) {
round[i].move();
}
requestAnimationFrame(animate);
}
# 创建 move() 函数
使用 move() 函数来改变 round 的 y 坐标。
在 move() 方法中,我们只需要改变 round 的 y 坐标即可,并且设置边界条件,当 y 坐标的值小于 -10(也可以是其他负值),代表该 round 已经超出了屏幕,这个时候我们要将其移动到屏幕的最底端,这样才能保证我们创建的粒子数不变,一直是 initRoundPopulation 的值。
这样就是一个粒子在不断地上升,上升到了最顶端再移动到最底端的循环过程,看起来像是有源源不断的粒子,但其实总数是不变的。
在 y 坐标的变化之后,我们还需要使用新的 y 坐标再来重新绘制一下该 round。
经过上面的分析,move() 写起来是不是很简单呢?
Round_item.prototype.move = function () {
this.y -= 0.15;
if (this.y <= -10) {
this.y = HEIGHT + 10;
}
this.draw();
};
# 在 init() 中加入 animate()
想要实现动画的效果,还需要在 init() 中加入 animate() 函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html, body {
margin: 0;
overflow: hidden;
width: 100%;
height: 100%;
cursor: none;
background: black;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var ctx = document.getElementById('canvas'),
content = ctx.getContext('2d'),
round = [],
WIDTH,
HEIGHT,
initRoundPopulation = 60;
WIDTH = document.documentElement.clientWidth;
HEIGHT = document.documentElement.clientHeight;
ctx.width = WIDTH;
ctx.height = HEIGHT;
function Round_item(index, x, y) {
this.index = index;
this.x = x;
this.y = y;
this.r = Math.random() * 2 + 2;
var alpha = (Math.floor(Math.random() * 10) + 1) / 12 ;
let a=parseInt(255*Math.random());
let b=parseInt(255*Math.random());
let c=parseInt(255*Math.random());
this.color = `rgba(${a},${b},${c},${alpha})`;
}
Round_item.prototype.draw = function () {
content.fillStyle = this.color;
content.shadowBlur = this.r * 2;
content.beginPath();
content.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
content.closePath();
content.fill();
};
function animate() {
content.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in round) {
round[i].move();
}
requestAnimationFrame(animate)
}
Round_item.prototype.move = function () {
this.y -= 0.2;
if (this.y <= -10) {
this.y = HEIGHT + 10;
}
this.draw();
};
function init() {
for (var i = 0; i < initRoundPopulation; i++) {
round[i] = new Round_item(i, Math.random() * WIDTH, Math.random() * HEIGHT);
round[i].draw();
}
animate();
}
init();
</script>
</body>
</html>