diff --git a/mini/pages/wish-tree/wish-tree.js b/mini/pages/wish-tree/wish-tree.js index ab73773..aa2252a 100644 --- a/mini/pages/wish-tree/wish-tree.js +++ b/mini/pages/wish-tree/wish-tree.js @@ -235,26 +235,31 @@ Page({ // 根据树类型返回不同的树枝配置 const configs = { pine: [ - { x: centerX - 80, y: baseY - 200 }, - { x: centerX + 60, y: baseY - 220 }, - { x: centerX - 40, y: baseY - 280 }, - { x: centerX + 90, y: baseY - 260 }, - { x: centerX, y: baseY - 320 }, - { x: centerX - 100, y: baseY - 150 }, - { x: centerX + 100, y: baseY - 180 }, + { x: centerX - 60, y: baseY - 180 }, + { x: centerX + 55, y: baseY - 190 }, + { x: centerX - 35, y: baseY - 220 }, + { x: centerX + 40, y: baseY - 230 }, + { x: centerX, y: baseY - 250 }, + { x: centerX - 80, y: baseY - 150 }, + { x: centerX + 75, y: baseY - 160 }, + { x: centerX - 20, y: baseY - 200 }, + { x: centerX + 25, y: baseY - 210 }, ], sakura: [ { x: centerX - 70, y: baseY - 180 }, - { x: centerX + 80, y: baseY - 200 }, - { x: centerX - 30, y: baseY - 250 }, - { x: centerX + 50, y: baseY - 280 }, - { x: centerX, y: baseY - 220 }, + { x: centerX + 65, y: baseY - 190 }, + { x: centerX - 45, y: baseY - 220 }, + { x: centerX + 50, y: baseY - 230 }, + { x: centerX, y: baseY - 250 }, + { x: centerX - 30, y: baseY - 200 }, + { x: centerX + 35, y: baseY - 210 }, ], bamboo: [ - { x: centerX - 60, y: baseY - 150 }, - { x: centerX + 60, y: baseY - 180 }, - { x: centerX - 30, y: baseY - 220 }, - { x: centerX + 30, y: baseY - 250 }, + { x: centerX - 30, y: baseY - 160 }, + { x: centerX, y: baseY - 200 }, + { x: centerX + 30, y: baseY - 140 }, + { x: centerX - 25, y: baseY - 120 }, + { x: centerX + 25, y: baseY - 180 }, ], }; @@ -393,45 +398,271 @@ Page({ const centerX = width / 2; const baseY = height * 0.75; - // 树干 - ctx.setFillStyle('#4a3728'); + // 根据树类型绘制不同风格的树 + const treeType = tree.type || 'pine'; + + if (treeType === 'sakura') { + this.drawSakuraTree(ctx, centerX, baseY); + } else if (treeType === 'bamboo') { + this.drawBambooTree(ctx, centerX, baseY); + } else { + this.drawPineTree(ctx, centerX, baseY); + } + }, + + // 绘制松树(祈福树) + drawPineTree(ctx, centerX, baseY) { + // 树干 - 用贝塞尔曲线画自然弯曲 + ctx.setFillStyle('#3d2914'); ctx.beginPath(); - ctx.moveTo(centerX - 25, baseY); - ctx.lineTo(centerX - 15, baseY - 150); - ctx.lineTo(centerX + 15, baseY - 150); - ctx.lineTo(centerX + 25, baseY); + ctx.moveTo(centerX - 20, baseY); + // 左边缘曲线 + ctx.bezierCurveTo( + centerX - 18, baseY - 50, + centerX - 12, baseY - 100, + centerX - 8, baseY - 140 + ); + // 树干顶部 + ctx.lineTo(centerX + 8, baseY - 140); + // 右边缘曲线 + ctx.bezierCurveTo( + centerX + 12, baseY - 100, + centerX + 18, baseY - 50, + centerX + 20, baseY + ); ctx.closePath(); ctx.fill(); - // 树冠 - 多层 - const layers = [ - { radius: 120, y: baseY - 180, color: '#1e4d2b' }, - { radius: 100, y: baseY - 220, color: '#2d6a3e' }, - { radius: 80, y: baseY - 260, color: '#3d8b4f' }, - { radius: 60, y: baseY - 300, color: '#4dac61' }, + // 树皮纹理 + ctx.setStrokeStyle('#2a1d0f'); + ctx.setLineWidth(1); + for (let i = 0; i < 5; i++) { + const y = baseY - 20 - i * 30; + ctx.beginPath(); + ctx.moveTo(centerX - 15 + i * 2, y); + ctx.quadraticCurveTo(centerX, y - 10, centerX + 15 - i * 2, y); + ctx.stroke(); + } + + // 主枝干 + this.drawBranch(ctx, centerX, baseY - 140, -60, 80, 3); + this.drawBranch(ctx, centerX, baseY - 140, 60, 80, 3); + this.drawBranch(ctx, centerX, baseY - 160, -30, 60, 2); + this.drawBranch(ctx, centerX, baseY - 160, 30, 60, 2); + this.drawBranch(ctx, centerX, baseY - 180, 0, 50, 2); + + // 树冠 - 用多个不规则圆形叠加,模拟松针层次 + const crownLayers = [ + { offsetY: -200, radiusX: 100, radiusY: 60, color: '#1a3d1f', alpha: 0.9 }, + { offsetY: -240, radiusX: 85, radiusY: 55, color: '#2d5a2d', alpha: 0.85 }, + { offsetY: -280, radiusX: 70, radiusY: 50, color: '#3d7a3d', alpha: 0.8 }, + { offsetY: -320, radiusX: 55, radiusY: 45, color: '#4d9a4d', alpha: 0.75 }, + { offsetY: -350, radiusX: 40, radiusY: 35, color: '#5dba5d', alpha: 0.7 }, ]; - layers.forEach(layer => { + crownLayers.forEach(layer => { + ctx.save(); + ctx.setGlobalAlpha(layer.alpha); ctx.setFillStyle(layer.color); + + // 绘制不规则椭圆形 ctx.beginPath(); - ctx.arc(centerX, layer.y, layer.radius, 0, Math.PI * 2); + for (let angle = 0; angle < Math.PI * 2; angle += 0.1) { + const noise = Math.sin(angle * 5) * 8 + Math.cos(angle * 3) * 5; + const rx = layer.radiusX + noise; + const ry = layer.radiusY + noise * 0.6; + const x = centerX + Math.cos(angle) * rx; + const y = baseY + layer.offsetY + Math.sin(angle) * ry; + if (angle === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + } + ctx.closePath(); ctx.fill(); + ctx.restore(); }); - // 装饰果实 - ctx.setFillStyle('#ffd700'); - for (let i = 0; i < 8; i++) { - const angle = (i / 8) * Math.PI * 2; - const r = 60 + Math.random() * 40; + // 松针细节 + ctx.setStrokeStyle('#6dca6d'); + ctx.setLineWidth(1.5); + for (let i = 0; i < 30; i++) { + const angle = (i / 30) * Math.PI * 2; + const r = 50 + Math.random() * 40; const x = centerX + Math.cos(angle) * r; - const y = baseY - 220 + Math.sin(angle) * r * 0.6; + const y = baseY - 280 + Math.sin(angle) * r * 0.5; + const len = 8 + Math.random() * 8; ctx.beginPath(); - ctx.arc(x, y, 6, 0, Math.PI * 2); + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(angle) * len, y + Math.sin(angle) * len); + ctx.stroke(); + } + + // 装饰果实(松果) + ctx.setFillStyle('#8b6914'); + for (let i = 0; i < 6; i++) { + const angle = (i / 6) * Math.PI * 2 + 0.5; + const r = 45 + Math.random() * 30; + const x = centerX + Math.cos(angle) * r; + const y = baseY - 260 + Math.sin(angle) * r * 0.4; + + // 画松果形状 + ctx.beginPath(); + ctx.ellipse(x, y, 5, 8, angle, 0, Math.PI * 2); ctx.fill(); } }, + // 绘制樱花树(姻缘树) + drawSakuraTree(ctx, centerX, baseY) { + // 树干 - 更纤细优雅 + ctx.setFillStyle('#4a3728'); + ctx.beginPath(); + ctx.moveTo(centerX - 12, baseY); + ctx.bezierCurveTo( + centerX - 10, baseY - 60, + centerX - 6, baseY - 120, + centerX - 4, baseY - 160 + ); + ctx.lineTo(centerX + 4, baseY - 160); + ctx.bezierCurveTo( + centerX + 6, baseY - 120, + centerX + 10, baseY - 60, + centerX + 12, baseY + ); + ctx.closePath(); + ctx.fill(); + + // 主枝干 - 更分散 + this.drawBranch(ctx, centerX, baseY - 160, -70, 90, 2); + this.drawBranch(ctx, centerX, baseY - 160, 70, 90, 2); + this.drawBranch(ctx, centerX, baseY - 180, -40, 70, 2); + this.drawBranch(ctx, centerX, baseY - 180, 40, 70, 2); + this.drawBranch(ctx, centerX, baseY - 200, 0, 60, 2); + + // 樱花树冠 - 粉色系 + const sakuraLayers = [ + { offsetY: -220, radiusX: 90, radiusY: 50, color: '#ffb7c5', alpha: 0.6 }, + { offsetY: -260, radiusX: 75, radiusY: 45, color: '#ffc9d4', alpha: 0.55 }, + { offsetY: -300, radiusX: 60, radiusY: 40, color: '#ffdbe3', alpha: 0.5 }, + { offsetY: -330, radiusX: 45, radiusY: 35, color: '#ffedf2', alpha: 0.45 }, + ]; + + sakuraLayers.forEach(layer => { + ctx.save(); + ctx.setGlobalAlpha(layer.alpha); + ctx.setFillStyle(layer.color); + + ctx.beginPath(); + for (let angle = 0; angle < Math.PI * 2; angle += 0.1) { + const noise = Math.sin(angle * 7) * 10 + Math.cos(angle * 4) * 6; + const rx = layer.radiusX + noise; + const ry = layer.radiusY + noise * 0.5; + const x = centerX + Math.cos(angle) * rx; + const y = baseY + layer.offsetY + Math.sin(angle) * ry; + if (angle === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + } + ctx.closePath(); + ctx.fill(); + ctx.restore(); + }); + + // 樱花花瓣 + ctx.setFillStyle('#fff0f5'); + for (let i = 0; i < 20; i++) { + const angle = Math.random() * Math.PI * 2; + const r = 40 + Math.random() * 50; + const x = centerX + Math.cos(angle) * r; + const y = baseY - 260 + Math.sin(angle) * r * 0.5; + const size = 3 + Math.random() * 4; + + // 画花瓣形状 + ctx.beginPath(); + ctx.ellipse(x, y, size, size * 1.5, angle, 0, Math.PI * 2); + ctx.fill(); + } + }, + + // 绘制竹子(事业树) + drawBambooTree(ctx, centerX, baseY) { + // 竹竿 - 多根 + const bambooPositions = [ + { offsetX: -30, height: 200 }, + { offsetX: 0, height: 240 }, + { offsetX: 30, height: 180 }, + ]; + + bambooPositions.forEach(pos => { + const x = centerX + pos.offsetX; + const topY = baseY - pos.height; + + // 竹竿主体 + ctx.setFillStyle('#5a8a3a'); + ctx.beginPath(); + ctx.moveTo(x - 6, baseY); + ctx.lineTo(x - 5, topY); + ctx.lineTo(x + 5, topY); + ctx.lineTo(x + 6, baseY); + ctx.closePath(); + ctx.fill(); + + // 竹节 + ctx.setStrokeStyle('#4a7a2a'); + ctx.setLineWidth(2); + for (let y = baseY - 40; y > topY; y -= 40) { + ctx.beginPath(); + ctx.moveTo(x - 6, y); + ctx.lineTo(x + 6, y); + ctx.stroke(); + } + + // 竹叶 + ctx.setFillStyle('#6aaa4a'); + for (let i = 0; i < 8; i++) { + const leafY = topY + 20 + i * 20; + const side = i % 2 === 0 ? 1 : -1; + + ctx.beginPath(); + ctx.moveTo(x, leafY); + ctx.quadraticCurveTo( + x + side * 25, leafY - 10, + x + side * 40, leafY + 5 + ); + ctx.quadraticCurveTo( + x + side * 25, leafY + 15, + x, leafY + 10 + ); + ctx.closePath(); + ctx.fill(); + } + }); + }, + + // 绘制树枝 + drawBranch(ctx, startX, startY, angle, length, width) { + const rad = (angle * Math.PI) / 180; + const endX = startX + Math.sin(rad) * length; + const endY = startY - Math.cos(rad) * length; + + ctx.setStrokeStyle('#3d2914'); + ctx.setLineWidth(width); + ctx.setLineCap('round'); + + ctx.beginPath(); + ctx.moveTo(startX, startY); + // 用曲线让树枝更自然 + const midX = startX + Math.sin(rad) * length * 0.5 + (Math.random() - 0.5) * 10; + const midY = startY - Math.cos(rad) * length * 0.5; + ctx.quadraticCurveTo(midX, midY, endX, endY); + ctx.stroke(); + }, + // 绘制丝带 drawRibbons(ctx, windLevel) { if (!this.ribbons) return;