canvas大树生长落叶动画效果代码
代码语言:html
所属分类:动画
代码描述:canvas大树生长落叶动画效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <style> * { border: 0; box-sizing: border-box; margin: 0; padding: 0; } body { background: hsl(223,10%,90%); display: flex; height: 100vh; } canvas { display: block; margin: auto; object-fit: contain; max-width: 100vw; max-height: 100vh; } /* Dark theme */ @media (prefers-color-scheme: dark) { body { background: hsl(223,10%,10%); } } </style> </head> <body > <canvas role="img" aria-label="A tree growing until it bears fruit, dropping its fruit, shrinking, and repeating the cycle"></canvas> <script > window.addEventListener("DOMContentLoaded", () => { const t = new Tree("canvas"); }); class Tree { constructor(qs) { this.C = document.querySelector(qs); this.c = this.C?.getContext("2d"); this.S = window.devicePixelRatio; this.W = 800; this.H = 800; this.branches = []; this.darkTheme = false; this.debug = false; this.decaying = false; this.floorY = 685; this.fruit = []; this.gravity = 0.098; this.loopDelay = 500; this.loopEnd = Utils.dateValue; this.maxGenerations = 10; if (this.C) this.init(); } get allBranchesComplete() { const { branches, maxGenerations } = this; return branches.filter(b => { const isLastGen = b.generation === maxGenerations; return b.progress >= 1 && isLastGen; }).length > 0; } get allFruitComplete() { return !!this.fruit.length && this.fruit.every(f => f.progress === 1); } get allFruitFalling() { return !!this.fruit.length && this.fruit.every(f => f.timeUntilFall <= 0); } get debugInfo() { return [ { item: 'Pixel Ratio', value: this.S }, { item: 'Branches', value: this.branches.length }, { item: 'Branches Complete', value: this.allBranchesComplete }, { item: 'Decaying', value: this.decaying }, { item: 'Fruit', value: this.fruit.length }, { item: 'Fruit Complete', value: this.allFruitComplete }]; } get lastGeneration() { const genIntegers = this.branches.map(b => b.generation); return [...new Set(genIntegers)].pop(); } get trunk() { return { angle: 0, angleInc: 20, decaySpeed: 0.0625, diameter: 10, distance: 120, distanceFade: 0.2, generation: 1, growthSpeed: 0.04, hadBranches: false, progress: 0, x1: 400, y1: 680, x2: 400, y2: 560 }; } detectTheme(mq) { this.darkTheme = mq.matches; } draw() { const { c, W, H, debug, branches, fruit } = this; c.clearRect(0, 0, W, H); const lightness = this.darkTheme ? 90 : 10; const foreground = `hsl(223,10%,${lightness}%)`; c.fillStyle = foreground; c.strokeStyle = foreground; // debug info if (debug === true) { const textX = 24; this.debugInfo.forEach((d, i) => { c.fillText(`${d.item}: ${d.value}`, textX, 24 * (i + 1)); }); } // branches branches.forEach(b => { c.lineWidth = b.diameter; c.beginPath(); c.moveTo(b.x1, b.y1); c.lineTo( b.x1 + (b.x2 - b.x1) * b.progress, b.y1 + (b.y2 - b.y1) * b.progress); c.stroke(); c.closePath(); }); // fruit fruit.forEach(f => { c.globalAlpha = f.decayTime < f.decayFrames ? f.decayTime / f.decayFrames : 1; c.beginPath(); c.arc(f.x, f.y, f.r * f.progress, 0, 2 * Math.PI); c.fill(); c.closePath(); c.globalAlpha = 1; }); } grow() { // start with the trunk if (!this.branches.length && Utils.dateValue - this.loopEnd > this.loopDelay) { this.branches.push(this.trunk); } if (!this.allBranchesComplete) { this.branches.forEach(b => { if (b.progress < 1) { // branch growth b.progress += b.growthSpeed; if (b.progress > 1) { b.progress = 1; // grow fruit if the generation is the las.........完整代码请登录后点击上方下载按钮下载查看
网友评论0