家好! 歡迎來到本教程,我們將深入了解使用 HTML 畫布和 JavaScript 在代碼中創(chuàng)建有趣的氣泡的世界。 最好的部分? 我們將只使用一點 HTML 和所有 JavaScript,而不是 CSS 來實現(xiàn)所有這一切。
今天,我們要掌握以下幾個概念:
使用畫布上下文的 arc 方法創(chuàng)建圓。
利用 requestAnimationFrame 函數(shù)實現(xiàn)平滑的圓形動畫。
利用 JavaScript 類的強大功能來創(chuàng)建多個圓圈,而無需重復代碼。
向我們的圓圈添加描邊樣式和填充樣式以獲得 3D 氣泡效果。
你可以跟著我一起看,或者如果你想看源代碼,可以使用最終的codepen
首先,我們需要一個 HTML5 Canvas 元素。 Canvas 是創(chuàng)建形狀、圖像和圖形的強大元素。 這就是氣泡將產(chǎn)生的地方。 讓我們來設置一下 -
<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;
}
讓我們進入有趣的部分。 我們將通過單擊畫布來創(chuàng)建氣泡。 為了實現(xiàn)這一點,我們首先創(chuàng)建一個點擊事件處理程序:
canvas.addEventListener('click', handleDrawCircle);
由于我們需要知道在畫布上單擊的位置,因此我們將在句柄 DrawCircle 函數(shù)中跟蹤它并使用事件的坐標 -
//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);
};
為了創(chuàng)建圓圈,我們將利用畫布上下文中可用的 arc 方法。 Arc 方法接受 x 和 y - 圓心、半徑、起始角和結(jié)束角,對于我們來說,這將是 0 和 2* Math.PI,因為我們正在創(chuàng)建一個完整的圓。
const drawCircle = (x, y) => {
context.beginPath();
context.arc(x, y, 50, 0, 2 * Math.PI);
context.strokeStyle = 'white';
context.stroke();
};
現(xiàn)在我們有了圓圈,讓我們讓它們移動,因為……
GIF
請記住,當我們創(chuàng)建圓時,我們使用了 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;
我們可以將其移至函數(shù)內(nèi) -
let x, y;
const move = () => {
const dx = Math.random() * 3;
const dy = Math.random() * 7; x = x + dx;
y = y - dy;
};
為了讓我們的圓圈無縫移動,我們將創(chuàng)建一個動畫函數(shù)并使用瀏覽器的 requestAnimationFrame 方法來創(chuàng)建一個移動的圓圈。
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();
現(xiàn)在我們已經(jīng)創(chuàng)建了一個圓圈,是時候創(chuàng)建多個圓圈了!
但在我們創(chuàng)建多個圓圈之前,讓我們準備一下我們的代碼。為了避免重復我們的代碼,我們將使用類并引入 Particle 類。 粒子是我們動態(tài)藝術(shù)作品和動畫的構(gòu)建塊。 每個氣泡都是一個粒子,具有自己的位置、大小、運動和顏色屬性。 讓我們定義一個 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 方法將負責在畫布上渲染粒子。 我們已經(jīng)在drawCircle中實現(xiàn)了這個功能,所以讓我們將它移動到我們的類中并將變量更新為類變量
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 函數(shù) -
move() {
this.x = this.x + this.dx;
this.y = this.y - this.dy;
}
現(xiàn)在,我們需要確保在事件處理程序中調(diào)用 Particle 類。
const handleDrawCircle = (event) => {
const x = event.pageX;
const y = event.pageY;
const particle = new Particle(x, y);
};canvas.addEventListener('click', handleDrawCircle);
由于我們需要在 animate 函數(shù)中訪問該粒子,以便調(diào)用其 move 方法,因此我們將該粒子存儲在一個名為 molecularArray 的數(shù)組中。 當創(chuàng)建大量粒子時,這個數(shù)組也會很有幫助。 這是反映這一點的更新代碼 -
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);
記得也要更新動畫功能 -
此時,您將在屏幕上看到這個粒子 -
驚人的! 現(xiàn)在,到了有趣的部分! 讓我們創(chuàng)建很多圓圈并設計它們的樣式,使它們看起來像氣泡。
為了創(chuàng)建大量氣泡,我們將使用 for 循環(huán)創(chuàng)建粒子并將它們添加到我們在此處創(chuàng)建的粒子數(shù)組中。
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);
在動畫函數(shù)中,我們將通過清除畫布并在新位置重新繪制粒子來不斷更新畫布。 這會給人一種圓圈在移動的錯覺。
const animate = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
particleArray.forEach((particle) => {
particle?.move();
particle?.draw();
}); requestAnimationFrame(animate);
};animate();
現(xiàn)在我們有了移動的氣泡,是時候給它們添加顏色,使它們看起來像氣泡了!
我們將通過向氣泡添加漸變填充來實現(xiàn)此目的。 這可以使用 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 創(chuàng)建了一些超級有趣的東西。 您已經(jīng)學習了如何使用 arc 方法、利用 requestAnimationFrame、利用 JavaScript 類的強大功能以及使用漸變設計氣泡以實現(xiàn) 3D 氣泡效果。
請隨意嘗試顏色、速度和大小,使您的動畫真正獨一無二。
請隨意嘗試顏色、速度和大小,使您的動畫真正獨一無二。
我希望您在學習本教程時能像我在創(chuàng)建它時一樣獲得樂趣。 現(xiàn)在,輪到你進行實驗了。 我很想看看你是否嘗試過這個以及你創(chuàng)造了什么。 與我分享您的代碼鏈接,我很樂意查看。
試,程序員極力避免的事情,因為這樣只會在代碼中產(chǎn)生更多的錯誤。.
即使是最優(yōu)秀的程序員,也會發(fā)現(xiàn)自己沒有辦法寫,沒有錯誤的代碼。這就是為什么你應該總是需要調(diào)試代碼。
調(diào)試JavaScript代碼最佳方法之一是console.log()。除此之外還有更好的辦法。
這就是本文的重點講述與控制臺交互的方法。在復雜的ide中輸入consol可以提供自動完成提示功能。
除了console.log(),這里還有其他更好的選擇。使用這些選項可以讓調(diào)試過程變得更加容易和更快。
當存在可以停止應用程序工作的錯誤時,使用console.log來調(diào)試它是行不通的。
這會是你的控制臺消息變得異常混亂,讓你找不到你想要找的消息。
使用console.warn() 和 console.error() 是克服這個問題的好方法。
console.warn("This is a warning");
console.error("This is an error")
想看看這段代碼運行需要多長時間?
使用console.time()
首先,創(chuàng)建一個計時器并為其指定一個唯一的名稱。
console.time("Loop timer")
然后,運行該代碼段。
for(i = 0; i < 10000; i++){
// Some code here
}
然后調(diào)用 timeEnd().
console.timeEnd("Loop timer")
這是所有的代碼。
console.time("Loop timer")
for(i = 0; i < 10000; i++){
// Some code here
}
console.timeEnd("Loop timer")
這對于需要一些時間密集型應用程序非常有用,例如神經(jīng)網(wǎng)絡或 HTML Canvas 讀取。
想看看函數(shù)是如何被調(diào)用的嗎?
function trace(){
console.trace()
}
function randomFunction(){
trace();
}
在這里,有一個名為randomFunction 調(diào)用trace,它又調(diào)用console.trace()。
所以當你調(diào)用randomFunction, 你會得到類似如下的輸出。
上圖顯示的那個匿名的方法調(diào)用了 randomFunction, 然后調(diào)用 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();
下面就是結(jié)果。
沒有什么特別的,我只是又清除了一遍,讓我們繼續(xù)。
讓我們添加表格,以更好的可視化數(shù)據(jù),想象一下我們有兩個對象。
var person1 = {name: "Weirdo", age : "-23", hobby: "singing"}
var person2 = {name: "SomeName", age : "Infinity", hobby: "programming"}
簡單的使用console.log 只會讓數(shù)據(jù)看起來很混亂。
使用表格就會更好一點。
console.table({person1, person2})
從未見過JavaScript控制臺可以如此的干凈,對吧?
是的,你沒看錯,你可以將css添加到控制臺。
console.log("%c I love JavaScript!",
"color: red; background-color: lightblue; border: solid");
注意這個%c 標識. 這就是發(fā)揮魔法的地方。.
感謝你的閱讀,謝謝!
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;
//背景數(shù)量
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);
}
}
}
//定義小綠數(shù)量
var sts=new Array();
//定義小紅數(shù)量
var drs=new Array();
//小紅小綠范圍內(nèi)相遇刪除對方
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++){
//判斷是否在范圍內(nèi)
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);//刪除小紅
}
}
}
}
}
//小紅數(shù)量循環(huán)
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);
}
}
}
//小綠數(shù)量循環(huán)
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小時內(nèi)與您取得聯(lián)系。