この記事は、ニフティグループ Advent Calendar 2023 13日目の記事です。
はじめに
こんにちは。新卒4年目の大里です。12月になって寒くなってきたため、今回はp5.jsで雪が降るようなアニメーションを実装しました。
p5.jsとは?
p5.jsとはクリエイティブ・コーディングのために作られたJavaScriptのライブラリです。クリエイティブ・コーディングはプログラミングを使ってアート作品などを創作することを指します。
公式ページ:https://p5js.org/
完成例
実行例
p5.jsを使って実装すればブラウザ上で表示できる以下のようなアニメーションを作成できます。
ファイル構成
p5.jsでアニメーションを制御しているファイルはsketch.jsです。index.htmlではsketch.jsのインポートとCDNからp5.jsの取得を行っています。
1 2 3 |
└─Snow index.html sketch.js |
index.htmlの中身
1 2 3 4 5 6 7 8 9 |
<html> <head> <meta charset="UTF-8"> <script src="https://cdn.jsdelivr.net/npm/p5@1.8.0/lib/p5.js"></script> <script src="./sketch.js"></script> </head> <body> </body> </html> |
sketch.jsの中身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); sides = 6; angle = TWO_PI/ this.sides; updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { push(); stroke(255); translate(this.x, this.y); for (let i = 0; i < this.sides; i++) { line(0, 0, this.size/2*cos(this.angle * i), this.size/2*sin(this.angle * i)); } pop(); } } |
実行方法
index.htmlをブラウザで開けば雪が降るアニメーションが表示されます。
作成手順
雪を降らすアニメーションの実装の仕方を段階を追って説明します。アニメーションはsketch.jsのみで実装しているため、これ以降はsketch.jsのみに言及します。
背景を描く
実装例
1 2 3 4 5 6 7 8 9 |
function setup(){ // キャンバスのサイズ指定 createCanvas(windowWidth, windowHeight); } function draw(){ // 背景を記述 background(30,30,55); } |
コード説明
setup関数はアニメーションを描写する前に一度だけ実行される関数です。このコードではcreateCanvasという関数を使ってキャンバス(画面)の大きさを設定しています。
draw関数はアニメーションを描写する際に1フレームごとに実行される関数です。デフォルトでは60fpsで実行されます。このコードではbackground関数を使ってキャンバスの背景の色をRGBで指定しています。
windowWidth, windowHeightはp5.jsが提供する環境変数です。ウィンドウの幅、高さを表しています。
実行例
円を描く
実装例
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function setup(){ createCanvas(windowWidth, windowHeight); } function draw(){ background(30,30,55); // この行以降の図形描写には輪郭線の描写なし noStroke(); // この行以降の図形描写では白色で塗りつぶす fill(255); // 円を描く circle(width/2, height/2, 100); } |
コード説明
circle関数では引数でx軸の座標、y軸の座標、大きさを指定しています。widthはキャンバスの幅、heightはキャンバスの高さを表す環境変数です。
p5.jsの原点はキャンバスの左上に位置しています。x軸の正方向は原点から右方向です。y軸の正方向は原点から下方向になります。
実行例
円を大量に描く
実装例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); // 雪の配列を作成する for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); // 配列全ての雪を描写する for(let snow of snows) { snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(0, height); size = random(6, 20); drawSnowFlake() { noStroke(); fill(255); circle(this.x, this.y, this.size); } } |
コード説明
雪(円)を大量に描くために雪クラスの配列を用いています。雪クラスではコンストラクターとして円のx座標, y座標, 円の大きさをランダムで設定しています。drawSnowFlakeメソッドで円を描くメソッドを定義しています。
実行例
それぞれの円を降らす
実装例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { // フレームごとに雪の位置を更新する snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { noStroke(); fill(255); circle(this.x, this.y, this.size); } } |
コード説明
雪を降らせるためにコンストラクターではy座標の修正と速度の設定を行いました。y座標はキャンバスから見えない上方向に設定しています。
updatePositionメソッドでフレームごとにy座標が増えていくように更新するようなメソッドを実装しました。加えて、雪がキャンバスから外れたときにキャンバスの上方向に戻るようにしています。
実行例
円を雪の結晶に変える
実装例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); sides = 6; angle = TWO_PI/ this.sides; updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { // 現在のスタイル設定と変換を保存する push(); // 輪郭線を白色で描写する stroke(255); // 原点を雪の位置に移動させる translate(this.x, this.y); // 線を6本放射線状に描く for (let i = 0; i < this.sides; i++) { line(0, 0, this.size/2*cos(this.angle * i), this.size/2*sin(this.angle * i)); } // スタイル設定と変換を元に戻す pop(); } } |
コード説明
円から雪の結晶に変えるためにdrawSnowFlakeメソッドを修正しました。push関数を使って現在の原点を保持しています。その後translate関数で原点を現在の雪の位置に移動させています。これをしておくと、雪の位置を原点として6本の放射線を描くための座標指定が簡単になります。最後にpop関数で原点をキャンバスの左上に戻します。push関数とpop関数はセットで使う必要があります。
実行例
おわりに
今回はp5.jsを使って雪が降るアニメーション作成を行いました。p5.js特有の関数や環境変数を覚える必要がありますが、簡単にアニメーションをプログラミングすることができました。p5.jsはJavaScriptのライブラリであるため、Webサイトの装飾にも使うことができると思います。
明日は、D_Wさんのエンジニア目線で考えるサービス設計です。 お楽しみに!