我的处女作《Canvas系列教程》在我的Github上正在连载更新,希望能得到您的关注和支持,让我有更多的动力进行创作。
教程介绍、教程目录等能在README里查阅。
介绍
实现100个随机弹射的小球在Canvas中碰撞,另外用一个玻璃窗格作为控制台,控制动画的开始与暂停。
演示链接
https://827652549.github.io/Canvas/Unit1/BouncingBalls.html
实现效果
思路
-
将玻璃窗格控制器潜入Canvas中
-
生成100个不同属性的小球
-
通过短间隔的计时器不断的擦除并绘制新的Canvas图层
注意:在Canvas标签中使用HTML元素是不行的,因为只有在Canvas不被支持时,标签内的元素才被显示(一般都是<canvas
>Canvas not supported</canvas>),所以只能让其他元素看起来像是Canvas范围内。
代码实现
<!DOCTYPE html>
<html>
<head>
<title>Bouncing Balls运动小球</title>
<meta charset="UTF-8">
<style>
body {
background: #dddddd;
}
#canvas {
margin-left: 10px;
margin-top: 10px;
background: #ffffff;
border: thin solid #aaaaaa;
}
#glasspane {
position: absolute;
left: 50px;
top: 50px;
padding: 0px 20px 10px 10px;
background: rgba(0, 0, 0, 0.3);
border: thin solid rgba(0, 0, 0, 0.6);
color: #eeeeee;
font-family: Droid Sans, Arial, Helvetica, sans-serif;
font-size: 12px;
cursor: pointer;
-webkit-box-shadow: rgba(0, 0, 0, 0.5) 5px 5px 20px;
-moz-box-shadow: rgba(0, 0, 0, 0.5) 5px 5px 20px;
box-shadow: rgba(0, 0, 0, 0.5) 5px 5px 20px;
}
#glasspane h2 {
font-weight: normal;
}
#glasspane .title {
font-size: 2em;
color: rgba(255, 255, 0, 0.8);
}
#glasspane a:hover {
color: yellow;
}
#glasspane a {
text-decoration: none;
color: #cccccc;
font-size: 3.5em;
}
#glasspane p {
margin: 10px;
color: rgba(65, 65, 220, 1.0);
font-size: 12pt;
font-family: Palatino, Arial, Helvetica, sans-serif;
}
</style>
</head>
<body>
<!--玻璃窗格 glass pane-->
<div id='glasspane'>
<h2 class='title'>弹射小球</h2>
<p>一百个小球弹射</p>
<a id='startButton'>Start</a>
</div>
<canvas id='canvas' width='750' height='500'>
Canvas not supported
</canvas>
<script>
var context = document.getElementById('canvas').getContext('2d'),
startButton = document.getElementById('startButton'),
glasspane = document.getElementById('glasspane'),
paused = true,
circles = [];
drawGird(context, 'lightgray', 10, 10);
context.lineWidth = 0.5;
context.font = '32pt Ariel';
//给100个小球设置不同的方向,半径,颜色
for (var i = 0; i < 100; i++) {
circles[i] = {
x: 100,
y: 100,
//向x方向移动的像素
velocityX: 3 * Math.random(),
velocityY: 3 * Math.random(),
radius: 50 * Math.random(),
color: 'rgba(' + (Math.random() * 255).toFixed(0) + ',' +
(Math.random() * 255).toFixed(0) + ',' +
(Math.random() * 255).toFixed(0) + ',1.0)'
};
}
startButton.onclick = function (ev) {
//取消默认行为
ev.preventDefault();
//阻止冒泡
ev.stopPropagation();
paused = !paused;
startButton.innerText = paused ? '启动' : '暂停';
};
glasspane.onmousedown = function (ev) {
ev.preventDefault();
ev.stopPropagation();
};
context.canvas.onmousedown = function (ev) {
ev.preventDefault();
ev.stopPropagation();
};
setInterval(function () {
if (!paused) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
drawGird(context, 'lightgray', 10, 10);
circles.forEach(function (circle) {
context.beginPath();
context.arc(circle.x, circle.y, circle.radius,
0, Math.PI * 2, false);
context.fillStyle = circle.color;
context.fill();
adjustPosition(circle);
});
}
}, 1000 / 60);
/**
* 调整每个球的位置,如果触碰到边缘,就调整方向
* @param circle
*/
function adjustPosition(circle) {
if (circle.x + circle.velocityX + circle.radius > context.canvas.width || circle.x - circle.radius + circle.velocityX<0)
circle.velocityX = -circle.velocityX;
if (circle.y + circle.velocityY + circle.radius > context.canvas.height || circle.y - circle.radius + circle.velocityY<0)
circle.velocityY = -circle.velocityY;
circle.x += circle.velocityX;
circle.y += circle.velocityY;
}
/**
* 画网格线
* @param context
* @param color
* @param stepX X方向网格间距
* @param stepY Y方向网格间距
*/
function drawGird(context,color,stepX,stepY) {
context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepX+0.5;i<context.canvas.width;i+=stepX){
context.beginPath();
context.moveTo(i,0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepY + 0.5; i < context.canvas.height; i+=stepY) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
</script>
</body>
</html>