fix(wish-tree): 重新绘制大树,更自然的树形
Publish Mini Program Dev Version / publish (push) Successful in 54s

- 松树:贝塞尔曲线树干、树皮纹理、不规则树冠、松针细节、松果
- 樱花树:纤细树干、粉色系树冠、樱花花瓣
- 竹子:多根竹竿、竹节、竹叶
- 添加树枝绘制函数,曲线更自然
- 更新树枝挂载点位置
This commit is contained in:
gouki
2026-08-07 18:59:01 +00:00
parent 01e67d4bea
commit 97c0d4d4e1
+267 -36
View File
@@ -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;