- 松树:贝塞尔曲线树干、树皮纹理、不规则树冠、松针细节、松果 - 樱花树:纤细树干、粉色系树冠、樱花花瓣 - 竹子:多根竹竿、竹节、竹叶 - 添加树枝绘制函数,曲线更自然 - 更新树枝挂载点位置
This commit is contained in:
@@ -235,26 +235,31 @@ Page({
|
|||||||
// 根据树类型返回不同的树枝配置
|
// 根据树类型返回不同的树枝配置
|
||||||
const configs = {
|
const configs = {
|
||||||
pine: [
|
pine: [
|
||||||
{ x: centerX - 80, y: baseY - 200 },
|
{ x: centerX - 60, y: baseY - 180 },
|
||||||
{ x: centerX + 60, y: baseY - 220 },
|
{ x: centerX + 55, y: baseY - 190 },
|
||||||
{ x: centerX - 40, y: baseY - 280 },
|
{ x: centerX - 35, y: baseY - 220 },
|
||||||
{ x: centerX + 90, y: baseY - 260 },
|
{ x: centerX + 40, y: baseY - 230 },
|
||||||
{ x: centerX, y: baseY - 320 },
|
{ x: centerX, y: baseY - 250 },
|
||||||
{ x: centerX - 100, y: baseY - 150 },
|
{ x: centerX - 80, y: baseY - 150 },
|
||||||
{ x: centerX + 100, y: baseY - 180 },
|
{ x: centerX + 75, y: baseY - 160 },
|
||||||
|
{ x: centerX - 20, y: baseY - 200 },
|
||||||
|
{ x: centerX + 25, y: baseY - 210 },
|
||||||
],
|
],
|
||||||
sakura: [
|
sakura: [
|
||||||
{ x: centerX - 70, y: baseY - 180 },
|
{ x: centerX - 70, y: baseY - 180 },
|
||||||
{ x: centerX + 80, y: baseY - 200 },
|
{ x: centerX + 65, y: baseY - 190 },
|
||||||
{ x: centerX - 30, y: baseY - 250 },
|
{ x: centerX - 45, y: baseY - 220 },
|
||||||
{ x: centerX + 50, y: baseY - 280 },
|
{ x: centerX + 50, y: baseY - 230 },
|
||||||
{ x: centerX, y: baseY - 220 },
|
{ x: centerX, y: baseY - 250 },
|
||||||
|
{ x: centerX - 30, y: baseY - 200 },
|
||||||
|
{ x: centerX + 35, y: baseY - 210 },
|
||||||
],
|
],
|
||||||
bamboo: [
|
bamboo: [
|
||||||
{ x: centerX - 60, y: baseY - 150 },
|
{ x: centerX - 30, y: baseY - 160 },
|
||||||
{ x: centerX + 60, y: baseY - 180 },
|
{ x: centerX, y: baseY - 200 },
|
||||||
{ x: centerX - 30, y: baseY - 220 },
|
{ x: centerX + 30, y: baseY - 140 },
|
||||||
{ x: centerX + 30, y: baseY - 250 },
|
{ x: centerX - 25, y: baseY - 120 },
|
||||||
|
{ x: centerX + 25, y: baseY - 180 },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -393,45 +398,271 @@ Page({
|
|||||||
const centerX = width / 2;
|
const centerX = width / 2;
|
||||||
const baseY = height * 0.75;
|
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.beginPath();
|
||||||
ctx.moveTo(centerX - 25, baseY);
|
ctx.moveTo(centerX - 20, baseY);
|
||||||
ctx.lineTo(centerX - 15, baseY - 150);
|
// 左边缘曲线
|
||||||
ctx.lineTo(centerX + 15, baseY - 150);
|
ctx.bezierCurveTo(
|
||||||
ctx.lineTo(centerX + 25, baseY);
|
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.closePath();
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
// 树冠 - 多层
|
// 树皮纹理
|
||||||
const layers = [
|
ctx.setStrokeStyle('#2a1d0f');
|
||||||
{ radius: 120, y: baseY - 180, color: '#1e4d2b' },
|
ctx.setLineWidth(1);
|
||||||
{ radius: 100, y: baseY - 220, color: '#2d6a3e' },
|
for (let i = 0; i < 5; i++) {
|
||||||
{ radius: 80, y: baseY - 260, color: '#3d8b4f' },
|
const y = baseY - 20 - i * 30;
|
||||||
{ radius: 60, y: baseY - 300, color: '#4dac61' },
|
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.setFillStyle(layer.color);
|
||||||
|
|
||||||
|
// 绘制不规则椭圆形
|
||||||
ctx.beginPath();
|
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.fill();
|
||||||
|
ctx.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 装饰果实
|
// 松针细节
|
||||||
ctx.setFillStyle('#ffd700');
|
ctx.setStrokeStyle('#6dca6d');
|
||||||
for (let i = 0; i < 8; i++) {
|
ctx.setLineWidth(1.5);
|
||||||
const angle = (i / 8) * Math.PI * 2;
|
for (let i = 0; i < 30; i++) {
|
||||||
const r = 60 + Math.random() * 40;
|
const angle = (i / 30) * Math.PI * 2;
|
||||||
|
const r = 50 + Math.random() * 40;
|
||||||
const x = centerX + Math.cos(angle) * r;
|
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.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();
|
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) {
|
drawRibbons(ctx, windLevel) {
|
||||||
if (!this.ribbons) return;
|
if (!this.ribbons) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user