<!-- 绘制冒泡效果 -->
<!DOCTYPE html>
<html>
<head>
<title>Canvas</title>
</head>
<style type="text/css">
#canvas{
border: 1px solid #eee;
}
</style>
<body>
<canvas id="canvas" width="500" height="300" ></canvas>
<!-- 不建议使用css控制常宽,因为如果设置的宽高与初始比例 300:150 不同,有可能出现扭曲的现象 -->
<!-- 假如浏览器不支持canvas可以直接 <canvas>您的浏览器不支持canvas</canvas> 浏览器会渲染替代内容 -->
</body>
<script type="text/javascript">
class Bubble{ // ES6新增Class语法糖
constructor(ctx){ // 构造函数
this.colorList = [[254,158,159], [147,186,255], [217,153,249], [129,199,132], [255,202,98], [255,164,119]]; // 颜色方案
this.ctx = ctx.getContext("2d"); // 二维绘图
this.circleList = []; // 气泡数组
}
randomInt(a, b) { // 返回随机数
return Number.parseInt(Math.random() * (b - a + 1) + a); //取a-b之间包括ab的随机值
}
newCircle(){ // 新气泡
if(this.randomInt(0,50)) return 0; // 控制生成气泡的数量
var canvasHeight = this.ctx.canvas.height; // 获取画布高度
var circle = {}; // 定义一个新的气泡对象
circle.r = this.randomInt(10,50); // 随机半径
circle.x = this.randomInt(0, this.ctx.canvas.width); // 初始化气泡X坐标
circle.xMoveSpeed = this.randomInt(-10,10); // X方向移动速度以及方向
circle.y = canvasHeight + circle.r; // 初始化气泡Y坐标
circle.yMoveSpeed = this.randomInt(5,10); // Y方向的移动速度
circle.color = this.colorList[this.randomInt(0,this.colorList.length-1)]; // 获取气泡颜色
this.circleList.push(circle); // 将对象放入数组
}
destroyCircle(){ // 销毁气泡
this.circleList.forEach((v,i) => {
if(v.y < -v.r) this.circleList.splice(i,1); // 气泡超过上边界就销毁气泡对象
})
}
draw(){ // 绘制气泡
this.newCircle(); // 调用产生新气泡
this.ctx.clearRect(0,0,this.ctx.canvas.width,this.ctx.canvas.height); // 清空画布
this.ctx.save(); // 保存画布状态
this.circleList.forEach( v => {
this.ctx.beginPath(); // 起始一条路径
this.ctx.fillStyle = `rgb(${v.color[0]},${v.color[1]},${v.color[2]},0.6)`; // 设置背景颜色
this.ctx.strokeStyle = `rgb(${v.color[0]},${v.color[1]},${v.color[2]})`; // 设置边线颜色
this.ctx.arc(v.x,v.y,v.r,0,Math.PI * 2); // 绘制圆 x坐标 y坐标 半径 起始角度 结束角度 顺/逆时针绘制
this.ctx.fill(); // 绘制填充
this.ctx.stroke(); // 绘制边线
v.y -= v.yMoveSpeed * 0.1; // Y轴移动
v.x += v.xMoveSpeed * 0.05; // X轴移动
})
this.ctx.restore(); // 恢复画布状态
this.destroyCircle(); // 销毁气泡
requestAnimationFrame(() => {this.draw();}); // 递归调用
}
start(){
// setInterval(() => {this.draw();},16.7); // 定时器绘制动画效果
requestAnimationFrame(() => {this.draw();}); // 使用请求动画帧来绘制图像,根据刷新率(60HZ则为每16.7ms刷新一次),需要递归调用
// requestAnimationFrame当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,setInterval需要使用加入visibilitychange监听来清除与重设定时器
}
}
(function(){
var ctx = document.getElementById("canvas"); // 获取canvas对象
var bubble = new Bubble(ctx); // 实例化Bubble
bubble.start(); // 开始绘制
})();
</script>
</html>