[VanillaJS]生長したツリーを作成-2
42598 ワード
Demo
第1編では、スクリーンの真ん中に木を描きました.
この編では、クリックすることで、木の描画活動と、下から徐々に成長する効果を実現しましょう!
クリック活動は簡単です.
アプリケーションがTreeオブジェクトを自動的に作成すると、Treeの
スクリーンをクリックすると、木がクリックした位置で成長しているのが見えます.😀
クリックすると木々が生えてきて、今度は底からゆっくり成長していきます.
枝の成長効果を与えるにはどうすればいいですか?
図に示すように、区間を区切って
奥応?!長いのは長いが、長いのは長い木の感じではない.🥲
考えてみれば、
コードを修正して、枝を深さで挿入し、深さの枝を描き終わってから、次の深さを描きます.
最後に、すべての樹木を描画すると、
このようにコードを修正した結果!
スピードが遅すぎるので、
樹木の生長効果を達成した😀
好みで色を変えてもいいです.
筆者はいくつかの色を決め、ランダムに色を指定した後、深さと白を混ぜ合わせ、Interactive Developer金鍾民の作品に似た効果を生み出した.
Interactive Developer金鍾民(キム・ジョンミン)の動画Googleから入社提案を受けたポートフォリオのPlant Treeを見て、想像以上に難易度が高かった.しかし、金鍾民の動画を見て学んだのか、問題に直面した時も難なく解決できた.
終わってから、コードはあまり長くありませんでしたが、6~7時間くらいしました.😂
お尻が重すぎて、最初から仕事をしていたので、そのせいか完成するとぐったりしていました.
それでも、仕事の中で生活しているうちに、久しぶりに芸術作品の仕事をしたり、頭を働かせたり、途中で成果を見たりして、仕事が面白かったです.
これからもたまにやります.😃
下一篇:[VanillaJS]生長したツリーを作成-1
第1編では、スクリーンの真ん中に木を描きました.
この編では、クリックすることで、木の描画活動と、下から徐々に成長する効果を実現しましょう!
クリックイベント
クリック活動は簡単です.
アプリケーションがTreeオブジェクトを自動的に作成すると、Treeの
draw()
関数が自動的に実行され、クリックイベントが発生するとTreeオブジェクトが生成されます.App.js
import { Tree } from './tree.js';
class App {
constructor() {
this.canvas = document.createElement('canvas');
document.body.appendChild(this.canvas);
this.ctx = this.canvas.getContext('2d');
this.pixelRatio = window.devicePixelRatio > 1 ? 2 : 1;
// click이벤트 추가
window.addEventListener('resize', this.resize.bind(this), false);
window.addEventListener('click', this.click.bind(this), false);
this.resize();
}
resize() {
this.stageWidth = document.body.clientWidth;
this.stageHeight = document.body.clientHeight;
this.canvas.width = this.stageWidth * this.pixelRatio;
this.canvas.height = this.stageHeight * this.pixelRatio;
this.ctx.scale(this.pixelRatio, this.pixelRatio);
this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight);
}
// click 함수 추가
click(event) {
const { clientX } = event;
new Tree(this.ctx, clientX, this.stageHeight);
}
}
window.onload = () => {
new App();
};
APPではclick()
関数を実装し、マウスのx座標と画面の最下層座標this.stageHeight
を使用してTreeオブジェクトを生成します.スクリーンをクリックすると、木がクリックした位置で成長しているのが見えます.😀
クリックすると木々が生えてきて、今度は底からゆっくり成長していきます.
▼▼ナスの成長効果
枝の成長効果を与えるにはどうすればいいですか?
図に示すように、区間を区切って
requestAnimationFrame()
関数を用いてdraw()
を呼び出し、Gapに相当する長さを描き続けると、成長の効果が得られる.branch.js
export class Branch {
constructor(startX, startY, endX, endY, lineWidth) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
this.color = '#000000';
this.lineWidth = lineWidth;
this.frame = 100; // 가지를 100등분으로 나누기 위한 변수 frame 선언
this.cntFrame = 0; // 현재 frame
// 가지의 길이를 frame으로 나누어 구간별 길이를 구함
this.gapX = (this.endX - this.startX) / this.frame;
this.gapY = (this.endY - this.startY) / this.frame;
// 구간별 가지가 그려질 때 끝 좌표
this.currentX = this.startX;
this.currentY = this.startY;
}
draw(ctx) {
// 현재 frame인 cntFrame이 설정한 frame과 같다면 draw를 하지 않는다.
if (this.cntFrame === this.frame) return;
ctx.beginPath();
// 구간별 길이를 더해주어 다음 구간의 끝 좌표를 구함
this.currentX += this.gapX;
this.currentY += this.gapY;
ctx.moveTo(this.startX, this.startY);
ctx.lineTo(this.currentX, this.currentY); // 끝 좌표를 currentX,Y로
if (this.lineWidth < 3) {
ctx.lineWidth = 0.5;
} else if (this.lineWidth < 7) {
ctx.lineWidth = this.lineWidth * 0.7;
} else if (this.lineWidth < 10) {
ctx.lineWidth = this.lineWidth * 0.9;
} else {
ctx.lineWidth = this.lineWidth;
}
ctx.fillStyle = this.color;
ctx.strokeStyle = this.color;
ctx.stroke();
ctx.closePath();
this.cntFrame++; // 현재 프레임수 증가
}
}
次のように100セグメントに分割し、ブランチを描画し続けます.tree.js
import { Branch } from './branch.js';
export class Tree {
...
draw() {
for (let i = 0; i < this.branches.length; i++) {
this.branches[i].draw(this.ctx);
}
requestAnimationFrame(this.draw.bind(this));
}
...
}
次に、tree.js
からdraw()
の関数の下で、requestAnimationFrame()
の関数を用いてdraw()
を再帰的に呼び出し、枝分かれ成長の効果を実現することができる.奥応?!長いのは長いが、長いのは長い木の感じではない.🥲
考えてみれば、
tree.js
で枝が生成され、それらを1つの配列に配置し、その後、すべての枝に対してdraw()
関数を呼び出すので、問題は枝がすべて同時に成長することである.▼▼木が育つ効果
コードを修正して、枝を深さで挿入し、深さの枝を描き終わってから、次の深さを描きます.
tree.js
import { Branch } from './branch.js';
export class Tree {
constructor(ctx, posX, posY) {
this.ctx = ctx;
this.posX = posX;
this.posY = posY;
this.branches = [];
this.depth = 11;
this.cntDepth = 0; // depth별로 그리기 위해 현재 depth 변수 선언
this.animation = null; // 현재 동작하는 애니메이션
this.init();
}
init() {
// depth별로 가지를 저장하기 위해 branches에 depth만큼 빈배열 추가
for (let i = 0; i < this.depth; i++) {
this.branches.push([]);
}
this.createBranch(this.posX, this.posY, -90, 0);
this.draw();
}
createBranch(startX, startY, angle, depth) {
if (depth === this.depth) return;
const len = depth === 0 ? this.random(10, 13) : this.random(0, 11);
const endX = startX + this.cos(angle) * len * (this.depth - depth);
const endY = startY + this.sin(angle) * len * (this.depth - depth);
// depth에 해당하는 위치의 배열에 가지를 추가
this.branches[depth].push(
new Branch(startX, startY, endX, endY, this.depth - depth)
);
this.createBranch(endX, endY, angle - this.random(15, 23), depth + 1);
this.createBranch(endX, endY, angle + this.random(15, 23), depth + 1);
}
draw() {
// 다 그렸으면 requestAnimationFrame을 중단해 메모리 누수가 없게 함.
if (this.cntDepth === this.depth) {
cancelAnimationFrame(this.animation);
}
// depth별로 가지를 그리기
for (let i = this.cntDepth; i < this.branches.length; i++) {
let pass = true;
for (let j = 0; j < this.branches[i].length; j++) {
pass = this.branches[i][j].draw(this.ctx);
}
if (!pass) break;
this.cntDepth++;
}
this.animation = requestAnimationFrame(this.draw.bind(this));
}
}
branch.js
export class Branch {
...
draw(ctx) {
// 가지를 다 그리면 true 리턴
if (this.cntFrame === this.frame) return true;
ctx.beginPath();
this.currentX += this.gapX;
this.currentY += this.gapY;
ctx.moveTo(this.startX, this.startY);
ctx.lineTo(this.currentX, this.currentY);
ctx.lineWidth = this.lineWidth;
ctx.fillStyle = this.color;
ctx.strokeStyle = this.color;
ctx.stroke();
ctx.closePath();
this.cntFrame++;
// 다 안그렸으면 false를 리턴
return false;
}
}
branch.js
のdraw()
関数では、枝が描かれた場合はtrue
、そうでなければfalse
を返します.tree.js
に深さ別にブランチを格納し、draw
関数に深さ別にブランチを描画します.現在、深さでブランチをすべて描画します.pass == true
になると、次の深さが行われますが、描画が完了しません.pass == false
であれば、draw()
関数を終了し、次の深さのブランチを描画できなくなります.最後に、すべての樹木を描画すると、
cancelAnimationFrame()
が呼び出され、不要なアニメーションがメモリ漏れを繰り返すことを回避します.このようにコードを修正した結果!
スピードが遅すぎるので、
branch.js
のframe
を10程度に修正しましょう.樹木の生長効果を達成した😀
好みで色を変えてもいいです.
筆者はいくつかの色を決め、ランダムに色を指定した後、深さと白を混ぜ合わせ、Interactive Developer金鍾民の作品に似た効果を生み出した.
後記
Interactive Developer金鍾民(キム・ジョンミン)の動画Googleから入社提案を受けたポートフォリオのPlant Treeを見て、想像以上に難易度が高かった.しかし、金鍾民の動画を見て学んだのか、問題に直面した時も難なく解決できた.
終わってから、コードはあまり長くありませんでしたが、6~7時間くらいしました.😂
お尻が重すぎて、最初から仕事をしていたので、そのせいか完成するとぐったりしていました.
それでも、仕事の中で生活しているうちに、久しぶりに芸術作品の仕事をしたり、頭を働かせたり、途中で成果を見たりして、仕事が面白かったです.
これからもたまにやります.😃
下一篇:[VanillaJS]生長したツリーを作成-1
Reference
この問題について([VanillaJS]生長したツリーを作成-2), 我々は、より多くの情報をここで見つけました https://velog.io/@heekang/Vanilla-JS-자라나는-나무-만들기-2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol