家好! 欢迎来到本教程,我们将深入了解使用 HTML 画布和 JavaScript 在代码中创建有趣的气泡的世界。 最好的部分? 我们将只使用一点 HTML 和所有 JavaScript,而不是 CSS 来实现所有这一切。
今天,我们要掌握以下几个概念:
使用画布上下文的 arc 方法创建圆。
利用 requestAnimationFrame 函数实现平滑的圆形动画。
利用 JavaScript 类的强大功能来创建多个圆圈,而无需重复代码。
向我们的圆圈添加描边样式和填充样式以获得 3D 气泡效果。
你可以跟着我一起看,或者如果你想看源代码,可以使用最终的codepen
首先,我们需要一个 HTML5 Canvas 元素。 Canvas 是创建形状、图像和图形的强大元素。 这就是气泡将产生的地方。 让我们来设置一下 -
<canvas id="canvas"></canvas>
为了使用画布做任何有意义的事情,我们需要访问它的上下文。 Context 提供了在画布上渲染对象和绘制形状的接口。
让我们访问画布及其上下文。
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
我们将设置画布以使用整个窗口的高度和宽度 -
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
让我们通过添加一些 css 为画布提供一个漂亮的舒缓浅蓝色背景。 这是我们要使用的唯一 CSS。 如果您愿意,也可以使用 JavaScript 来完成此操作。
#canvas {
background: #00b4ff;
}
让我们进入有趣的部分。 我们将通过单击画布来创建气泡。 为了实现这一点,我们首先创建一个点击事件处理程序:
canvas.addEventListener('click', handleDrawCircle);
由于我们需要知道在画布上单击的位置,因此我们将在句柄 DrawCircle 函数中跟踪它并使用事件的坐标 -
//We are adding x and y here because we will need it later.
let x, y
const handleDrawCircle = (event) => {
x = event.pageX;
y = event.pageY;
// Draw a bubble!
drawCircle(x, y);
};
为了创建圆圈,我们将利用画布上下文中可用的 arc 方法。 Arc 方法接受 x 和 y - 圆心、半径、起始角和结束角,对于我们来说,这将是 0 和 2* Math.PI,因为我们正在创建一个完整的圆。
const drawCircle = (x, y) => {
context.beginPath();
context.arc(x, y, 50, 0, 2 * Math.PI);
context.strokeStyle = 'white';
context.stroke();
};
现在我们有了圆圈,让我们让它们移动,因为……
GIF
请记住,当我们创建圆时,我们使用了 arc 方法,它接受 x 和 y 坐标 - 圆的中心。 如果我们快速移动圆的 x 和 y 坐标,就会给人一种圆在移动的印象。 让我们试试吧!
//Define a speed by which to increment to the x and y coordinates
const dx = Math.random() * 3;
const dy = Math.random() * 7;//Increment the center of the circle with this speed
x = x + dx;
y = y - dy;
我们可以将其移至函数内 -
let x, y;
const move = () => {
const dx = Math.random() * 3;
const dy = Math.random() * 7; x = x + dx;
y = y - dy;
};
为了让我们的圆圈无缝移动,我们将创建一个动画函数并使用浏览器的 requestAnimationFrame 方法来创建一个移动的圆圈。
const animate = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
move();
drawCircle(x,y); requestAnimationFrame(animate);
};//Don't forget to call animate at the bottom
animate();
现在我们已经创建了一个圆圈,是时候创建多个圆圈了!
但在我们创建多个圆圈之前,让我们准备一下我们的代码。为了避免重复我们的代码,我们将使用类并引入 Particle 类。 粒子是我们动态艺术作品和动画的构建块。 每个气泡都是一个粒子,具有自己的位置、大小、运动和颜色属性。 让我们定义一个 Particle 类来封装这些属性:
class Particle {
constructor(x = 0, y = 0) {}
draw() {
// Drawing the particle as a colored circle
// ...
} move() {
// Implementing particle movement
// ...
}
}
让我们将一些已设置的常量移至 Particle 类 -
class Particle {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
this.radius = Math.random() * 50;
this.dx = Math.random() * 3;
this.dy = Math.random() * 7;
}
draw() {
// Drawing the particle as a colored circle
// ...
} move() {
// Implementing particle movement
// ...
}
}
draw 方法将负责在画布上渲染粒子。 我们已经在drawCircle中实现了这个功能,所以让我们将它移动到我们的类中并将变量更新为类变量
class Particle {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
this.radius = Math.random() * 50;
this.dx = Math.random() * 3;
this.dy = Math.random() * 7;
this.color = 'white';
}
draw() {
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
context.strokeStyle = this.color;
context.stroke(); context.fillStyle = this.color;
context.fill();
} move() {}
}
同样,让我们在类中移动 move 函数 -
move() {
this.x = this.x + this.dx;
this.y = this.y - this.dy;
}
现在,我们需要确保在事件处理程序中调用 Particle 类。
const handleDrawCircle = (event) => {
const x = event.pageX;
const y = event.pageY;
const particle = new Particle(x, y);
};canvas.addEventListener('click', handleDrawCircle);
由于我们需要在 animate 函数中访问该粒子,以便调用其 move 方法,因此我们将该粒子存储在一个名为 molecularArray 的数组中。 当创建大量粒子时,这个数组也会很有帮助。 这是反映这一点的更新代码 -
const particleArray = [];
const handleDrawCircle = (event) => {
const x = event.pageX;
const y = event.pageY; const particle = new Particle(x, y);
particleArray.push(particle);
};canvas.addEventListener('click', handleDrawCircle);
记得也要更新动画功能 -
此时,您将在屏幕上看到这个粒子 -
惊人的! 现在,到了有趣的部分! 让我们创建很多圆圈并设计它们的样式,使它们看起来像气泡。
为了创建大量气泡,我们将使用 for 循环创建粒子并将它们添加到我们在此处创建的粒子数组中。
const handleDrawCircle = (event) => {
const x = event.pageX;
const y = event.pageY;
for (let i = 0; i < 50; i++) {
const particle = new Particle(x, y);
particleArray.push(particle);
}
};canvas.addEventListener('click', handleDrawCircle);
在动画函数中,我们将通过清除画布并在新位置重新绘制粒子来不断更新画布。 这会给人一种圆圈在移动的错觉。
const animate = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
particleArray.forEach((particle) => {
particle?.move();
particle?.draw();
}); requestAnimationFrame(animate);
};animate();
现在我们有了移动的气泡,是时候给它们添加颜色,使它们看起来像气泡了!
我们将通过向气泡添加渐变填充来实现此目的。 这可以使用 context.createRadialGradient 方法来完成。
const gradient = context.createRadialGradient(
this.x,
this.y,
1,
this.x + 0.5,
this.y + 0.5,
this.radius
);
gradient.addColorStop(0.3, 'rgba(255, 255, 255, 0.3)');
gradient.addColorStop(0.95, '#e7feff');context.fillStyle = gradient;
恭喜! 您刚刚仅使用 HTML Canvas 和 JavaScript 创建了一些超级有趣的东西。 您已经学习了如何使用 arc 方法、利用 requestAnimationFrame、利用 JavaScript 类的强大功能以及使用渐变设计气泡以实现 3D 气泡效果。
请随意尝试颜色、速度和大小,使您的动画真正独一无二。
请随意尝试颜色、速度和大小,使您的动画真正独一无二。
我希望您在学习本教程时能像我在创建它时一样获得乐趣。 现在,轮到你进行实验了。 我很想看看你是否尝试过这个以及你创造了什么。 与我分享您的代码链接,我很乐意查看。
试,程序员极力避免的事情,因为这样只会在代码中产生更多的错误。.
即使是最优秀的程序员,也会发现自己没有办法写,没有错误的代码。这就是为什么你应该总是需要调试代码。
调试JavaScript代码最佳方法之一是console.log()。除此之外还有更好的办法。
这就是本文的重点讲述与控制台交互的方法。在复杂的ide中输入consol可以提供自动完成提示功能。
除了console.log(),这里还有其他更好的选择。使用这些选项可以让调试过程变得更加容易和更快。
当存在可以停止应用程序工作的错误时,使用console.log来调试它是行不通的。
这会是你的控制台消息变得异常混乱,让你找不到你想要找的消息。
使用console.warn() 和 console.error() 是克服这个问题的好方法。
console.warn("This is a warning");
console.error("This is an error")
想看看这段代码运行需要多长时间?
使用console.time()
首先,创建一个计时器并为其指定一个唯一的名称。
console.time("Loop timer")
然后,运行该代码段。
for(i = 0; i < 10000; i++){
// Some code here
}
然后调用 timeEnd().
console.timeEnd("Loop timer")
这是所有的代码。
console.time("Loop timer")
for(i = 0; i < 10000; i++){
// Some code here
}
console.timeEnd("Loop timer")
这对于需要一些时间密集型应用程序非常有用,例如神经网络或 HTML Canvas 读取。
想看看函数是如何被调用的吗?
function trace(){
console.trace()
}
function randomFunction(){
trace();
}
在这里,有一个名为randomFunction 调用trace,它又调用console.trace()。
所以当你调用randomFunction, 你会得到类似如下的输出。
上图显示的那个匿名的方法调用了 randomFunction, 然后调用 trace().
对控制台消息进行分组,可以使控制台更易于阅读。
console.log("Test1!");
console.group("My message group");
console.log("Test2!");
console.log("Test2!");
console.log("Test2!");
console.groupEnd()
所有的 Test2 归于 ‘My message group’组之下.
如果你遵循本教程,那么你的控制台将会非常满。让我们把它清除掉。
console.clear();
下面就是结果。
没有什么特别的,我只是又清除了一遍,让我们继续。
让我们添加表格,以更好的可视化数据,想象一下我们有两个对象。
var person1 = {name: "Weirdo", age : "-23", hobby: "singing"}
var person2 = {name: "SomeName", age : "Infinity", hobby: "programming"}
简单的使用console.log 只会让数据看起来很混乱。
使用表格就会更好一点。
console.table({person1, person2})
从未见过JavaScript控制台可以如此的干净,对吧?
是的,你没看错,你可以将css添加到控制台。
console.log("%c I love JavaScript!",
"color: red; background-color: lightblue; border: solid");
注意这个%c 标识. 这就是发挥魔法的地方。.
感谢你的阅读,谢谢!
lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<html>
<body>
<canvas id='canvas'></canvas>
<div id="write"></div>
</body>
</html>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
//画布
var c=document.getElementById('canvas');
var ctx=c.getContext('2d');
//画布尺寸
var h=650;
var w=800;
c.width=w;
c.height=h;
//背景数量
var num=100;
//大红尺寸
var gh=h/num;
var gw=w/num;
//小绿尺寸
var gamex=40;
var gamey=30;
//背景布置
function bk(){
for(var x=0;x<num;x++){
for(var y=0;y<num;y++){
ctx.fillStyle="green";
ctx.fillRect(10*(x-1)+x,10*(y-1)+y,gw,gh);
}
}
}
//定义小绿数量
var sts=new Array();
//定义小红数量
var drs=new Array();
//小红小绿范围内相遇删除对方
function dels(){
if(sts.length>0){
for(var i=0;i<sts.length;i++){
//范围大小
var a=sts[i]['x']+20;
var b=sts[i]['x']-20;
var aa=sts[i]['y']+20;
var bb=sts[i]['y']-20;
$('#write').text(a+'---'+b);
for(var s=0;s<drs.length;s++){
//判断是否在范围内
if((drs[s]['x']<a && drs[s]['x']>b) && (drs[s]['y']<aa && drs[s]['y']>bb)){
//sts.splice(i,1);//删除小绿
drs.splice(s,1);//删除小红
}
}
}
}
}
//小红数量循环
function drss(){
var dr=new Array();
dr['x']=Math.floor((Math.random()*w)+1);
dr['y']=0;
//dr['y']=Math.floor((Math.random()*h)+1);
drs.push(dr);
if(drs.length>0){
for(var i=0;i<drs.length;i++){
if(drs[i]['y']>h){
drs.splice(i,1);
}else{
drs[i]['y']+=1;
}
ctx.fillStyle="orange";
ctx.fillRect(drs[i]['x'],drs[i]['y'],gw,gh);
}
}
}
//小绿数量循环
function st(){
ctx.fillStyle="black";
var st=new Array();
st['x']=10*(gamex-1)+gamex+10;
st['y']=10*(gamey-1)+gamey-10;
sts.push(st);
ctx.fillRect(10*(gamex-1)+gamex+10,10*(gamey-1)+gamey-10,gw,gh);
}
//小绿移动
function stsup(){
if(sts.length>0){
for(var i=0;i<sts.length;i++){
if(sts[i]['y']<0){
sts.splice(i,1);
}else{
sts[i]['y']-=1;
}
ctx.fillStyle="red";
ctx.fillRect(sts[i]['x'],sts[i]['y'],gw,gh);
}
}
}
//大红位置移动
function game(gamex,gamey){
ctx.fillStyle="red";
//ctx.fillRect(10*(gamex-1)+gamex,10*(gamey-1)+gamey,gw,gh);
ctx.fillRect(10*(gamex-1)+gamex,10*(gamey-1)+gamey,20,20);
}
bk();
game(gamex,gamey);
//时间戳
setInterval(function(){
//按键判断
document.onkeydown=function(event){
var e = event || window.event || arguments.callee.caller.arguments[0];
if(e.keyCode=='37'){
gamex=gamex-1;
}
else if(e.keyCode=='38'){
gamey=gamey-1;
}
else if(e.keyCode=='39'){
gamex=gamex+1;
}
else if(e.keyCode=='40'){
gamey=gamey+1;
}
if(e.keyCode=='32'){
st();
}
}
//清空画布
c.width=w;
c.height=h;
bk();
game(gamex,gamey);
drss();
stsup();
dels();
},10);
</script>
*请认真填写需求信息,我们会在24小时内与您取得联系。