0%

P5js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
let flowerImg;
let plants = [];
const NUM_PLANTS = 45; // 花朵总数

function preload() {
// 关键:在这里加载你的手绘花朵模板图片
// 确保图片背景是透明的 (PNG格式)
flowerImg = loadImage('./flower.png');
}

function setup() {
createCanvas(600, 800);
imageMode(CENTER); // 让图片以中心点为基准绘制

// 初始化所有植物数据
for (let i = 0; i < NUM_PLANTS; i++) {
plants.push(new Plant());
}
}

function draw() {
// 绘制蓝色渐变背景 (模拟视频里的背景)
setGradient(0, 0, width, height, color(40, 60, 90), color(120, 160, 200));

// 绘制花盆 (用简单图形模拟,你也可以替换为 loadImage)
drawPot();

// 遍历并绘制每根茎和花朵
for (let i = 0; i < plants.length; i++) {
// 利用索引 i 让每根茎的生长开始时间错开,产生层次感
if (frameCount > i * 4) {
plants[i].grow();
plants[i].display();
}
}
}

// 定义植物类
class Plant {
constructor() {
// 生长起点统一在花盆口中心
this.startX = width / 2;
this.startY = height - 150;

// 关键点1:参差不齐与向外散开
// 终点的 X 随机向左右散开,Y 随机高度
this.endX = width / 2 + random(-250, 250);
this.endY = height - random(300, 650);

// 生成贝塞尔曲线的两个控制点,让茎有自然的弯曲弧度
this.cp1X = this.startX + random(-50, 50);
this.cp1Y = this.startY - random(50, 100);
this.cp2X = this.endX + random(-100, 100);
this.cp2Y = this.endY + random(100, 200);

// t 代表生长进度,从 0 生长到 1
this.t = 0;
this.speed = random(0.005, 0.015); // 每根茎生长速度不同,增加参差感

this.flowerScale = 0;
this.maxFlowerScale = random(0.03, 0.08); // 花朵最终大小随机

// 关键点2:随机的花朵颜色深浅
// 使用 tint() 可以在不改变原图结构的情况下叠加颜色
// 这里生成随机深浅的色调 (以偏黄色调为例,你可以调整 RGB 值)
let shade = random(100, 255);
this.flowerTint = color(shade, shade, shade); // 比如让黄色呈现不同深浅
}

grow() {
// 1. 茎先生长
if (this.t < 1) {
this.t += this.speed*2;
}
// 2. 茎长到 100% 后,花朵开始膨胀长出
else if (this.flowerScale < this.maxFlowerScale) {
this.flowerScale += 0.01;
}
}

display() {
noFill();
stroke(45, 140, 45); // 茎的绿色
strokeWeight(4);

// 关键点3:绘制生长的茎
// 不是直接画一条线,而是计算从起点到当前进度 t 的贝塞尔曲线点
beginShape();
for (let i = 0; i <= this.t; i += 0.02) {
let px = bezierPoint(this.startX, this.cp1X, this.cp2X, this.endX, i);
let py = bezierPoint(this.startY, this.cp1Y, this.cp2Y, this.endY, i);
vertex(px, py);
}
// 补上当前尖端的精确位置,防止锯齿断层
let currentX = bezierPoint(this.startX, this.cp1X, this.cp2X, this.endX, this.t);
let currentY = bezierPoint(this.startY, this.cp1Y, this.cp2Y, this.endY, this.t);
vertex(currentX, currentY);
endShape();

// 当茎长完后,在终点绘制花朵
if (this.t >= 1 && this.flowerScale > 0) {
push();
translate(this.endX, this.endY); // 移动到茎的顶端
scale(this.flowerScale); // 花朵缩放动画
tint(this.flowerTint); // 应用随机深浅颜色滤镜

// 绘制加载的手绘模板图片
if (flowerImg) {
image(flowerImg, 0, 0);
} else {
// 如果图片加载失败的备用绘制 (粉色圆圈)
fill(255, 100, 150);
noStroke();
ellipse(0, 0, 50, 50);
}
pop();
}
}
}

// --- 辅助函数:绘制渐变背景 ---
function setGradient(x, y, w, h, c1, c2) {
noFill();
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1);
let c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}

// --- 辅助函数:绘制简单的花盆 ---
function drawPot() {
fill(250, 245, 235); // 陶瓷白
noStroke();

// 盆体
beginShape();
vertex(width/2 - 70, height);
vertex(width/2 + 70, height);
vertex(width/2 + 90, height - 150);
vertex(width/2 - 90, height - 150);
endShape(CLOSE);

// 盆口边缘
rectMode(CENTER);
rect(width/2, height - 150, 190, 20, 10);
}