Learn JavaScript with p5.js
此書提到的JavaScript語法沒有甚麼好寫的,太簡單,還沒有使用ES6標準,網上有一大堆更好的總結和cheat sheets,而p5.js的語法直接看官網即可。所以,本文只是在此書的最後project中加些中文注釋。
p5.js是一個JavaScript圖形庫,可畫出多種不同的圖形(和文字),為其設置各種動態效果,十分簡單易用。通過此書,可以快速地入門JavaScript和p5.js,幾天時間就能夠讀完。
p5.js的基本架構是setup和draw函數:
function setup() {
createCanvas(800, 300);
}
function draw() {
background(0);
}
Final project
// 全局變量,讓p5.js中的setup和draw函數也能使用這些變量
var guessItem = null;
var interval = 60;
var results = [];
var solution = null;
var gameOver = false;
// p5.js中的基本函數,只運行一次,無需主動調用
function setup() {
// 設置圖的大小
createCanvas(800, 300);
}
// p5.js中的基本函數,會根據frameRate(默認60)不斷反覆運行
// frameCount會自動累積,每過一frame加一
function draw() {
var gameScore = getGameScore(results);
if (gameScore.loss === 3 || gameScore.total === 10) {
gameOver = true;
displayGameOver(gameScore);
return;
}
// 設定背景顏色為0, 0, 0,即黑色
background(0);
// frameCount % interval會循環得出0, 1, 2, ..., interval - 1的值
if (frameCount === 1 || frameCount % interval === 0) {
solution = null;
// 根據定義建立一個新的GuessItem object
guessItem = new GuessItem(width/2, height/2, 1);
}
if (guessItem) {
guessItem.render();
}
if (solution === true || solution === false) {
solutionMessage(gameScore.total, solution);
}
}
function solutionMessage(seed, solution) {
var trueMessages = [
'GOOD JOB!',
'DOING GREAT!',
'OMG',
'SUCH WIN!',
'I APPRECIATE YOU',
'IMPRESSIVE'
];
var falseMessages = [
'OH NO!',
'BETTER LUCK NEXT TIME!',
'PFTTTT',
':('
];
var messages;
push();
textAlign(CENTER, CENTER);
fill(237, 34, 93);
// 文字大小
textSize(36);
randomSeed(seed * 10000);
if (solution === true) {
background(255);
messages = trueMessages;
} else if (solution === false) {
background(0);
messages = falseMessages;
}
// parseInt將浮點數轉為整數,做到去除小數點的效果
text(messages[parseInt(random(messages.length), 10)], width/2, height/2);
pop();
}
function displayGameOver(score) {
push();
background(255);
textSize(24);
textAlign(CENTER, CENTER);
translate(width/2, height/2);
fill(237, 34, 93);
text('GAME OVER!', 0, 0);
translate(0, 36);
fill(0);
text('You have ' + score.win + ' correct guesses', 0, 0);
translate(0, 100);
textSize(16);
// sin函數得出-1~1之間的值,map函數將這個值由-1~1的區間變成0~255的區間
var alternatingValue = map(sin(frameCount / 10), -1, 1, 0, 255);
fill(237, 34, 93, alternatingValue);
text('PRESS ENTER', 0, 0);
pop();
}
function getGameScore(score) {
var wins = 0;
var losses = 0;
var total = score.length;
for (var i = 0; i < total; i++) {
var item = score[i];
if (item === true) {
wins = wins + 1;
} else {
losses = losses + 1;
}
}
return {'win': wins, 'loss': losses, 'total': total};
}
function restartTheGame() {
results = [];
solution = null;
gameOver = false;
}
// 用於檢測鍵盤按鍵event的函數,不用主動調用
function keyPressed() {
if (gameOver === true) {
// 如果按的鍵是Enter
if (keyCode === ENTER) {
console.log('restart the game');
restartTheGame();
return;
}
}
if (guessItem != null) {
// key儲存是所按鍵的值(一個string)
console.log('you pressed: ', key);
solution = guessItem.solve(key);
console.log(solution);
if (solution) {
// 加入一個值到results array
results.push(true);
} else {
results.push(false);
}
guessItem = null;
} else {
console.log('nothing to be solved');
}
}
// 通過constructor function定義JavaScript中的一個object
function GuessItem(x, y, scl) {
this.x = x;
this.y = y;
this.scale = scl;
this.scaleIncrement = 0.25;
this.clr = 255;
this.content = getContent();
this.alpha = 255;
this.alphaDecrement = 3;
this.solved = null;
this.contentMap = {
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
'5': 'five',
'6': 'six',
'7': 'seven',
'8': 'eight',
'9': 'nine',
'0': 'zero'
};
this.colors = [
[63, 184, 175],
[127, 199, 175],
[218, 216, 167],
[255, 158, 157],
[255, 61, 127],
[55, 191, 211],
[159, 223, 82],
[234, 209, 43],
[250, 69, 8],
[194, 13, 0]
];
// object中的一個普通函數
function getContent() {
// 將整數變為string
return String(parseInt(random(10), 10));
}
// object中的method
this.solve = function(input) {
if (input === this.content) {
solved = true;
} else {
solved = false;
}
this.solved = solved;
return solved;
}
this.drawEllipse = function(size, strkWeight, speedMultiplier, seed) {
// push()和pop()同時出現,其中做的格式設置和變換不影響外界的格式設置和變換
push();
randomSeed(seed);
translate(this.x, this.y);
var ellipseSize = this.scale * speedMultiplier;
scale(ellipseSize);
var clr = this.colors[parseInt(random(this.colors.length), 10)];
// 線條顏色
stroke(clr);
// 不填充
noFill();
// 線條粗幼
strokeWeight(strkWeight);
// 畫圓或橢圓
ellipse(0, 0, size, size);
pop();
}
this.render = function () {
push();
this.drawEllipse(100, 15, 2, 1 * this.content * 1000);
this.drawEllipse(60, 7, 2, 1 * this.content * 2000);
this.drawEllipse(35, 3, 1.2, 1 * this.content * 3000);
pop();
push();
// 對文字填色,最後一個參數是其透明度
fill(this.clr, this.alpha);
// 坐標點處為文字中央
textAlign(CENTER, CENTER);
// 平移,文字的坐標點不再在左上角
translate(this.x, this.y);
// 縮放
scale(this.scale);
text(this.contentMap[this.content], 0, 0)
this.scale = this.scale + this.scaleIncrement;
this.alpha = this.alpha - this.alphaDecrement;
pop();
}
}