futranbg commited on
Commit
a69f9f5
·
verified ·
1 Parent(s): e851965

Update static/js/solar_animation.js

Browse files
Files changed (1) hide show
  1. static/js/solar_animation.js +165 -54
static/js/solar_animation.js CHANGED
@@ -19,7 +19,8 @@ const colors = {
19
  battery: '#ADD8E6', // Xanh dương nhạt
20
  home: '#FF4500', // Đỏ cam
21
  flow: '#FFA500', // Cam cho dòng năng lượng
22
- text: '#333333'
 
23
  };
24
 
25
  // Hàm vẽ một hộp (thành phần)
@@ -36,46 +37,145 @@ function drawComponent(comp, label, color) {
36
  ctx.fillText(label, comp.x + comp.width / 2, comp.y + comp.height + 20);
37
  }
38
 
39
- // Hàm vẽ dòng năng lượng
40
- function drawFlow(startX, startY, endX, endY, direction = 'right', speed = 5) {
41
- ctx.strokeStyle = colors.flow;
42
- ctx.lineWidth = 4;
43
- ctx.lineCap = 'round'; // Để đầu mũi tên tròn hơn
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ctx.beginPath();
46
- ctx.moveTo(startX, startY);
47
- ctx.lineTo(endX, endY);
48
  ctx.stroke();
 
 
 
 
 
 
49
 
50
- // Vẽ hiệu ứng "dòng chảy"
51
- ctx.save(); // Lưu trạng thái canvas
52
- ctx.clip(); // Clip để chỉ vẽ trong đường đã vẽ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; // Màu sáng cho hiệu ứng dòng chảy
55
  ctx.lineWidth = 2;
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
- const numDots = 10;
58
- const lineLength = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
59
- const dotSpacing = lineLength / numDots;
 
 
 
60
 
61
- for (let i = 0; i < numDots; i++) {
62
- let currentPos = (energyFlowOffset + i * dotSpacing) % lineLength;
63
- if (currentPos < 0) currentPos += lineLength; // Xử lý khi offset âm
64
 
65
- let ratio = currentPos / lineLength;
66
- let x = startX + (endX - startX) * ratio;
67
- let y = startY + (endY - startY) * ratio;
 
 
68
 
69
- ctx.beginPath();
70
- if (direction === 'right' || direction === 'down') {
71
- ctx.arc(x, y, 3, 0, Math.PI * 2); // Vẽ chấm tròn
72
- } else { // Ví dụ: dòng đi lên hoặc sang trái
73
- ctx.arc(x, y, 3, 0, Math.PI * 2); // Vẽ chấm tròn
74
- }
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  ctx.fill();
 
 
 
77
  }
78
- ctx.restore(); // Khôi phục trạng thái canvas
79
  }
80
 
81
 
@@ -83,44 +183,55 @@ function drawFlow(startX, startY, endX, endY, direction = 'right', speed = 5) {
83
  function draw() {
84
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Xóa canvas
85
 
86
- // Vẽ các thành phần
87
- drawComponent(solarPanel, 'Tấm pin', colors.solar);
88
- drawComponent(inverter, 'Biến tần', colors.inverter);
89
- drawComponent(battery, 'Pin lưu trữ', colors.battery);
90
- drawComponent(homeLoad, 'Tải nhà', colors.home);
 
 
 
 
 
 
 
 
 
 
91
 
92
- // Vẽ luồng năng lượng
93
  // Pin -> Biến tần
94
- drawFlow(
95
- solarPanel.x + solarPanel.width / 2, solarPanel.y + solarPanel.height,
96
- inverter.x + inverter.width / 2, inverter.y,
97
- 'down'
98
  );
99
 
100
  // Biến tần -> Tải nhà
101
- drawFlow(
102
- inverter.x + inverter.width, inverter.y + inverter.height / 2,
103
- homeLoad.x, homeLoad.y + homeLoad.height / 2,
104
- 'right'
105
  );
106
 
107
- // Biến tần -> Pin lưu trữ
108
- drawFlow(
109
- inverter.x + inverter.width / 2, inverter.y + inverter.height,
110
- battery.x + battery.width / 2, battery.y,
111
- 'down'
 
112
  );
113
 
114
- // Pin lưu trữ -> Biến tần (khi năng lượng được lấy từ pin)
115
- drawFlow(
116
- battery.x + battery.width / 2, battery.y,
117
- inverter.x + inverter.width / 2, inverter.y + inverter.height,
118
- 'up', // Dòng đi lên
119
- -5 // Tốc độ ngược lại
120
  );
121
 
122
  // Cập nhật offset để tạo hoạt hình dòng chảy
123
- energyFlowOffset = (energyFlowOffset - 1) % 50; // Thay đổi tốc độ và độ dài dòng chảy
124
 
125
  animationFrameId = requestAnimationFrame(draw); // Lặp lại hoạt hình
126
  }
 
19
  battery: '#ADD8E6', // Xanh dương nhạt
20
  home: '#FF4500', // Đỏ cam
21
  flow: '#FFA500', // Cam cho dòng năng lượng
22
+ text: '#333333',
23
+ arrow: '#FFFFFF' // Màu trắng cho mũi tên
24
  };
25
 
26
  // Hàm vẽ một hộp (thành phần)
 
37
  ctx.fillText(label, comp.x + comp.width / 2, comp.y + comp.height + 20);
38
  }
39
 
40
+ // Hàm vẽ biểu tượng tấm pin mặt trời
41
+ function drawSolarPanelIcon(x, y, size) {
42
+ ctx.fillStyle = colors.solar;
43
+ ctx.beginPath();
44
+ // Hình chữ nhật bản
45
+ ctx.rect(x, y, size, size * 0.7);
46
+ ctx.fill();
47
+ ctx.strokeStyle = '#333';
48
+ ctx.lineWidth = 1;
49
+ ctx.stroke();
50
+
51
+ // Thêm các đường lưới nhỏ để trông giống pin hơn
52
+ ctx.strokeStyle = 'rgba(0,0,0,0.3)';
53
+ ctx.lineWidth = 0.5;
54
+ for (let i = 1; i < 4; i++) {
55
+ ctx.beginPath();
56
+ ctx.moveTo(x + i * (size / 4), y);
57
+ ctx.lineTo(x + i * (size / 4), y + size * 0.7);
58
+ ctx.stroke();
59
+ ctx.beginPath();
60
+ ctx.moveTo(x, y + i * (size * 0.7 / 3));
61
+ ctx.lineTo(x + size, y + i * (size * 0.7 / 3));
62
+ ctx.stroke();
63
+ }
64
+ }
65
 
66
+ // Hàm vẽ biểu tượng biến tần (inverter)
67
+ function drawInverterIcon(x, y, width, height) {
68
+ ctx.fillStyle = colors.inverter;
69
+ ctx.fillRect(x, y, width, height);
70
+ ctx.strokeStyle = '#333';
71
+ ctx.lineWidth = 2;
72
+ ctx.strokeRect(x, y, width, height);
73
+
74
+ // Vẽ mũi tên AC/DC
75
+ ctx.fillStyle = '#FFF';
76
+ ctx.font = 'bold 12px Arial';
77
+ ctx.textAlign = 'center';
78
+ ctx.textBaseline = 'middle';
79
+ ctx.fillText('DC', x + width / 4, y + height / 2);
80
+ ctx.fillText('AC', x + width * 3 / 4, y + height / 2);
81
  ctx.beginPath();
82
+ ctx.moveTo(x + width / 2 - 5, y + height / 2);
83
+ ctx.lineTo(x + width / 2 + 5, y + height / 2);
84
  ctx.stroke();
85
+ ctx.beginPath(); // Mũi tên nhỏ
86
+ ctx.moveTo(x + width / 2 + 5, y + height / 2 - 3);
87
+ ctx.lineTo(x + width / 2 + 10, y + height / 2);
88
+ ctx.lineTo(x + width / 2 + 5, y + height / 2 + 3);
89
+ ctx.fill();
90
+ }
91
 
92
+ // Hàm vẽ biểu tượng pin lưu trữ (battery)
93
+ function drawBatteryIcon(x, y, width, height) {
94
+ ctx.fillStyle = colors.battery;
95
+ ctx.fillRect(x, y, width, height);
96
+ ctx.strokeStyle = '#333';
97
+ ctx.lineWidth = 2;
98
+ ctx.strokeRect(x, y, width, height);
99
+
100
+ // Vẽ các vạch pin
101
+ ctx.fillStyle = '#FFF';
102
+ const barHeight = height / 3;
103
+ ctx.fillRect(x + width * 0.1, y + height * 0.1, width * 0.8, barHeight);
104
+ ctx.fillRect(x + width * 0.1, y + height * 0.4, width * 0.8, barHeight);
105
+ ctx.fillRect(x + width * 0.1, y + height * 0.7, width * 0.8, barHeight);
106
+
107
+ // Cực dương, cực âm
108
+ ctx.fillRect(x + width * 0.4, y - 5, width * 0.2, 5); // Cực dương
109
+ ctx.fillRect(x + width * 0.4, y + height, width * 0.2, 5); // Cực âm
110
+ }
111
+
112
+ // Hàm vẽ biểu tượng nhà (home load)
113
+ function drawHomeIcon(x, y, size) {
114
+ ctx.fillStyle = colors.home;
115
+ // Thân nhà
116
+ ctx.fillRect(x, y + size * 0.4, size, size * 0.6);
117
+ // Mái nhà
118
+ ctx.beginPath();
119
+ ctx.moveTo(x - 5, y + size * 0.4);
120
+ ctx.lineTo(x + size / 2, y);
121
+ ctx.lineTo(x + size + 5, y + size * 0.4);
122
+ ctx.closePath();
123
+ ctx.fill();
124
 
125
+ ctx.strokeStyle = '#333';
126
  ctx.lineWidth = 2;
127
+ ctx.strokeRect(x, y + size * 0.4, size, size * 0.6);
128
+ ctx.stroke();
129
+ }
130
+
131
+ // Hàm vẽ mũi tên di chuyển
132
+ function drawMovingArrow(p1, p2, offset, arrowSize = 8, speed = 1) {
133
+ const dx = p2.x - p1.x;
134
+ const dy = p2.y - p1.y;
135
+ const distance = Math.sqrt(dx * dx + dy * dy);
136
+
137
+ // Chỉ vẽ nếu có đủ không gian cho mũi tên
138
+ if (distance < arrowSize * 2) return;
139
 
140
+ ctx.strokeStyle = colors.flow;
141
+ ctx.lineWidth = 3;
142
+ ctx.beginPath();
143
+ ctx.moveTo(p1.x, p1.y);
144
+ ctx.lineTo(p2.x, p2.y);
145
+ ctx.stroke();
146
 
147
+ ctx.fillStyle = colors.arrow; // Màu trắng cho mũi tên
148
+ ctx.strokeStyle = '#333';
149
+ ctx.lineWidth = 1;
150
 
151
+ // Tính toán vị trí mũi tên
152
+ const numArrows = Math.floor(distance / (arrowSize * 3)); // Số lượng mũi tên trên đường
153
+ for (let i = 0; i < numArrows; i++) {
154
+ let currentPos = (offset + i * (arrowSize * 3)) % distance;
155
+ if (currentPos < 0) currentPos += distance; // Xử lý offset âm
156
 
157
+ const ratio = currentPos / distance;
158
+ const arrowX = p1.x + dx * ratio;
159
+ const arrowY = p1.y + dy * ratio;
 
 
 
160
 
161
+ // Tính góc của đường
162
+ const angle = Math.atan2(dy, dx);
163
+
164
+ ctx.save();
165
+ ctx.translate(arrowX, arrowY);
166
+ ctx.rotate(angle);
167
+
168
+ // Vẽ hình tam giác (mũi tên)
169
+ ctx.beginPath();
170
+ ctx.moveTo(-arrowSize, -arrowSize / 2);
171
+ ctx.lineTo(0, 0); // Đầu mũi tên
172
+ ctx.lineTo(-arrowSize, arrowSize / 2);
173
+ ctx.closePath();
174
  ctx.fill();
175
+ ctx.stroke();
176
+
177
+ ctx.restore();
178
  }
 
179
  }
180
 
181
 
 
183
  function draw() {
184
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Xóa canvas
185
 
186
+ // Vẽ các thành phần với biểu tượng trực quan
187
+ drawSolarPanelIcon(solarPanel.x, solarPanel.y, solarPanel.width);
188
+ drawInverterIcon(inverter.x, inverter.y, inverter.width, inverter.height);
189
+ drawBatteryIcon(battery.x, battery.y, battery.width, battery.height);
190
+ drawHomeIcon(homeLoad.x, homeLoad.y, homeLoad.width);
191
+
192
+ // Vẽ nhãn cho các thành phần (có thể đặt lại vị trí cho phù hợp với icon mới)
193
+ ctx.fillStyle = colors.text;
194
+ ctx.font = '14px Arial';
195
+ ctx.textAlign = 'center';
196
+ ctx.fillText('Tấm pin', solarPanel.x + solarPanel.width / 2, solarPanel.y + solarPanel.height + 20);
197
+ ctx.fillText('Biến tần', inverter.x + inverter.width / 2, inverter.y + inverter.height + 20);
198
+ ctx.fillText('Pin lưu trữ', battery.x + battery.width / 2, battery.y + battery.height + 20);
199
+ ctx.fillText('Tải nhà', homeLoad.x + homeLoad.width / 2, homeLoad.y + homeLoad.height + 20);
200
+
201
 
202
+ // Vẽ luồng năng lượng với mũi tên
203
  // Pin -> Biến tần
204
+ drawMovingArrow(
205
+ { x: solarPanel.x + solarPanel.width / 2, y: solarPanel.y + solarPanel.height },
206
+ { x: inverter.x + inverter.width / 2, y: inverter.y },
207
+ energyFlowOffset
208
  );
209
 
210
  // Biến tần -> Tải nhà
211
+ drawMovingArrow(
212
+ { x: inverter.x + inverter.width, y: inverter.y + inverter.height / 2 },
213
+ { x: homeLoad.x, y: homeLoad.y + homeLoad.height / 2 },
214
+ energyFlowOffset
215
  );
216
 
217
+ // Biến tần -> Pin lưu trữ (năng lượng dư thừa)
218
+ // Đường chéo xuống
219
+ drawMovingArrow(
220
+ { x: inverter.x + inverter.width / 2, y: inverter.y + inverter.height },
221
+ { x: battery.x + battery.width / 2, y: battery.y },
222
+ energyFlowOffset
223
  );
224
 
225
+ // Pin lưu trữ -> Biến tần (khi tải cần)
226
+ // Đường chéo lên
227
+ drawMovingArrow(
228
+ { x: battery.x + battery.width / 2, y: battery.y },
229
+ { x: inverter.x + inverter.width / 2, y: inverter.y + inverter.height },
230
+ -energyFlowOffset // Sử dụng offset ngược lại để tạo hiệu ứng mũi tên đi ngược
231
  );
232
 
233
  // Cập nhật offset để tạo hoạt hình dòng chảy
234
+ energyFlowOffset = (energyFlowOffset + 2) % 100; // Thay đổi tốc độ và khoảng cách mũi tên
235
 
236
  animationFrameId = requestAnimationFrame(draw); // Lặp lại hoạt hình
237
  }