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

Update static/js/solar_animation.js

Browse files
Files changed (1) hide show
  1. static/js/solar_animation.js +162 -109
static/js/solar_animation.js CHANGED
@@ -4,154 +4,219 @@ const canvas = document.getElementById('solarCanvas');
4
  const ctx = canvas.getContext('2d');
5
 
6
  let animationFrameId;
7
- let energyFlowOffset = 0; // Dịch chuyển để tạo hiệu ứng dòng chảy
8
 
9
  // Kích thước và vị trí các thành phần
10
- const solarPanel = { x: 50, y: 50, width: 100, height: 80 };
11
- const inverter = { x: 250, y: 150, width: 80, height: 60 };
12
- const battery = { x: 450, y: 250, width: 70, height: 100 };
13
  const homeLoad = { x: 650, y: 150, width: 80, height: 70 };
14
 
15
  // Định nghĩa màu sắc và thuộc tính
16
  const colors = {
17
- solar: '#FFD700', // Vàng năng lượng mặt trời
18
- inverter: '#4CAF50', // Xanh cây
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)
27
- function drawComponent(comp, label, color) {
28
- ctx.fillStyle = color;
29
- ctx.fillRect(comp.x, comp.y, comp.width, comp.height);
30
- ctx.strokeStyle = '#333';
31
- ctx.lineWidth = 2;
32
- ctx.strokeRect(comp.x, comp.y, comp.width, comp.height);
33
-
34
- ctx.fillStyle = colors.text;
35
- ctx.font = '14px Arial';
36
- ctx.textAlign = 'center';
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 đủ 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;
@@ -179,51 +244,39 @@ function drawMovingArrow(p1, p2, offset, arrowSize = 8, speed = 1) {
179
  }
180
 
181
 
182
- // Hàm chính để vẽ tất cả
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 },
 
4
  const ctx = canvas.getContext('2d');
5
 
6
  let animationFrameId;
7
+ let energyFlowOffset = 0; // Dịch chuyển để tạo hiệu ứng dòng chảy của mũi tên
8
 
9
  // Kích thước và vị trí các thành phần
10
+ const solarPanel = { x: 50, y: 50, width: 120, height: 90 };
11
+ const inverter = { x: 280, y: 150, width: 90, height: 70 };
12
+ const battery = { x: 480, y: 250, width: 80, height: 110 };
13
  const homeLoad = { x: 650, y: 150, width: 80, height: 70 };
14
 
15
  // Định nghĩa màu sắc và thuộc tính
16
  const colors = {
17
+ solarDark: '#4CAF50', // Xanh đậm cho tấm pin
18
+ solarLight: '#8BC34A', // Xanh nhạt cho tấm pin
19
+ inverterBody: '#607D8B', // Xám xanh cho thân biến tần
20
+ inverterAccent: '#FFC107', // Vàng cam cho điểm nhấn biến tần
21
+ batteryBody: '#2196F3', // Xanh dương cho thân pin
22
+ batteryAccent: '#BBDEFB', // Xanh dương nhạt cho điểm nhấn pin
23
+ homeRoof: '#E91E63', // Hồng cho mái nhà
24
+ homeBody: '#FFEB3B', // Vàng cho thân nhà
25
+ flowPrimary: '#FF9800', // Cam sáng cho dòng chảy chính
26
+ flowSecondary: 'rgba(255, 255, 255, 0.7)', // Trắng trong suốt cho hiệu ứng dòng chảy
27
+ text: '#212121', // Đen gần cho văn bản
28
+ shadow: 'rgba(0, 0, 0, 0.2)' // Màu bóng đổ
29
  };
30
 
31
+ // --- Hàm vẽ các biểu tượng chi tiết hơn ---
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ // Vẽ biểu tượng tấm pin mặt trời
34
+ function drawSolarPanelIcon(x, y, width, height) {
35
+ // Nền tấm pin (hơi nghiêng)
36
+ ctx.fillStyle = colors.solarDark;
37
  ctx.beginPath();
38
+ ctx.moveTo(x, y + height * 0.1);
39
+ ctx.lineTo(x + width, y);
40
+ ctx.lineTo(x + width, y + height);
41
+ ctx.lineTo(x, y + height * 0.9);
42
+ ctx.closePath();
43
  ctx.fill();
 
 
 
44
 
45
+ // Các ô pin
46
+ ctx.strokeStyle = colors.solarLight;
47
+ ctx.lineWidth = 1.5;
48
+ const cellWidth = width / 4;
49
+ const cellHeight = height / 3;
50
+ for (let i = 0; i < 4; i++) {
51
+ for (let j = 0; j < 3; j++) {
52
+ ctx.strokeRect(x + i * cellWidth, y + j * cellHeight + (j * cellHeight * 0.1), cellWidth, cellHeight);
53
+ }
 
 
 
54
  }
55
+
56
+ // Hiệu ứng phản chiếu
57
+ let gradient = ctx.createLinearGradient(x, y, x + width, y + height);
58
+ gradient.addColorStop(0, 'rgba(255, 255, 255, 0.3)');
59
+ gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
60
+ ctx.fillStyle = gradient;
61
+ ctx.beginPath();
62
+ ctx.moveTo(x, y + height * 0.1);
63
+ ctx.lineTo(x + width * 0.3, y);
64
+ ctx.lineTo(x + width * 0.5, y + height * 0.3);
65
+ ctx.lineTo(x, y + height * 0.5);
66
+ ctx.closePath();
67
+ ctx.fill();
68
+
69
+ ctx.fillStyle = colors.text;
70
+ ctx.font = '14px Arial';
71
+ ctx.textAlign = 'center';
72
+ ctx.fillText('Tấm pin', x + width / 2, y + height + 20);
73
  }
74
 
75
+ // Vẽ biểu tượng biến tần (inverter)
76
  function drawInverterIcon(x, y, width, height) {
77
+ // Thân biến tần
78
+ ctx.fillStyle = colors.inverterBody;
79
  ctx.fillRect(x, y, width, height);
80
+
81
+ // Viền và bóng đổ
82
  ctx.strokeStyle = '#333';
83
  ctx.lineWidth = 2;
84
  ctx.strokeRect(x, y, width, height);
85
 
86
+ ctx.shadowColor = colors.shadow;
87
+ ctx.shadowBlur = 8;
88
+ ctx.shadowOffsetX = 3;
89
+ ctx.shadowOffsetY = 3;
90
+ ctx.fillRect(x, y, width, height); // Vẽ lại để áp dụng bóng đổ
91
+ ctx.shadowBlur = 0; // Đặt lại bóng đổ
92
+
93
+ // Các cổng kết nối
94
+ ctx.fillStyle = colors.inverterAccent;
95
+ ctx.fillRect(x - 5, y + height * 0.2, 5, 10); // Cổng vào DC
96
+ ctx.fillRect(x + width, y + height * 0.7, 5, 10); // Cổng ra AC
97
+
98
+ // Ký hiệu DC/AC
99
  ctx.fillStyle = '#FFF';
100
  ctx.font = 'bold 12px Arial';
101
  ctx.textAlign = 'center';
102
  ctx.textBaseline = 'middle';
103
+ ctx.fillText('DC', x + width * 0.25, y + height / 2);
104
+ ctx.fillText('AC', x + width * 0.75, y + height / 2);
105
+
106
+ ctx.fillStyle = colors.text;
107
+ ctx.fillText('Biến tần', x + width / 2, y + height + 20);
 
 
 
 
 
 
108
  }
109
 
110
+ // Vẽ biểu tượng pin lưu trữ (battery)
111
  function drawBatteryIcon(x, y, width, height) {
112
+ // Thân pin
113
+ ctx.fillStyle = colors.batteryBody;
114
  ctx.fillRect(x, y, width, height);
115
+
116
+ // Viền và bóng đổ
117
  ctx.strokeStyle = '#333';
118
  ctx.lineWidth = 2;
119
  ctx.strokeRect(x, y, width, height);
120
 
121
+ ctx.shadowColor = colors.shadow;
122
+ ctx.shadowBlur = 8;
123
+ ctx.shadowOffsetX = 3;
124
+ ctx.shadowOffsetY = 3;
125
+ ctx.fillRect(x, y, width, height); // Vẽ lại để áp dụng bóng đổ
126
+ ctx.shadowBlur = 0; // Đặt lại bóng đổ
127
+
128
+ // Các cực
129
+ ctx.fillStyle = colors.batteryAccent;
130
+ ctx.fillRect(x + width * 0.35, y - 10, width * 0.3, 10); // Cực dương
131
+ ctx.fillRect(x + width * 0.35, y + height, width * 0.3, 10); // Cực âm
132
+ ctx.beginPath();
133
+ ctx.arc(x + width * 0.5, y - 5, 5, 0, Math.PI * 2); ctx.fill(); // Đầu cực dương
134
+ ctx.beginPath();
135
+ ctx.arc(x + width * 0.5, y + height + 5, 5, 0, Math.PI * 2); ctx.fill(); // Đầu cực âm
136
+
137
+ // Vạch năng lượng (ví dụ 3/4 pin)
138
+ ctx.fillStyle = 'rgba(255,255,255,0.7)';
139
+ ctx.fillRect(x + width * 0.1, y + height * 0.1, width * 0.8, height * 0.75); // Mức năng lượng
140
+
141
+ ctx.fillStyle = colors.text;
142
+ ctx.fillText('Pin lưu trữ', x + width / 2, y + height + 20);
143
  }
144
 
145
+ // Vẽ biểu tượng nhà (home load)
146
+ function drawHomeIcon(x, y, width, height) {
 
147
  // Thân nhà
148
+ ctx.fillStyle = colors.homeBody;
149
+ ctx.fillRect(x, y + height * 0.4, width, height * 0.6);
150
+
151
  // Mái nhà
152
+ ctx.fillStyle = colors.homeRoof;
153
  ctx.beginPath();
154
+ ctx.moveTo(x - 10, y + height * 0.4);
155
+ ctx.lineTo(x + width / 2, y);
156
+ ctx.lineTo(x + width + 10, y + height * 0.4);
157
  ctx.closePath();
158
  ctx.fill();
159
 
160
+ // Viền và bóng đổ
161
  ctx.strokeStyle = '#333';
162
  ctx.lineWidth = 2;
163
+ ctx.strokeRect(x, y + height * 0.4, width, height * 0.6);
164
+ ctx.beginPath();
165
+ ctx.moveTo(x - 10, y + height * 0.4);
166
+ ctx.lineTo(x + width / 2, y);
167
+ ctx.lineTo(x + width + 10, y + height * 0.4);
168
+ ctx.closePath();
169
  ctx.stroke();
170
+
171
+ ctx.shadowColor = colors.shadow;
172
+ ctx.shadowBlur = 8;
173
+ ctx.shadowOffsetX = 3;
174
+ ctx.shadowOffsetY = 3;
175
+ ctx.fillRect(x, y + height * 0.4, width, height * 0.6);
176
+ ctx.beginPath();
177
+ ctx.moveTo(x - 10, y + height * 0.4);
178
+ ctx.lineTo(x + width / 2, y);
179
+ ctx.lineTo(x + width + 10, y + height * 0.4);
180
+ ctx.closePath();
181
+ ctx.fill();
182
+ ctx.shadowBlur = 0; // Đặt lại bóng đổ
183
+
184
+ // Cửa sổ
185
+ ctx.fillStyle = 'rgba(173, 216, 230, 0.8)'; // Xanh da trời nhạt
186
+ ctx.fillRect(x + width * 0.2, y + height * 0.5, width * 0.25, height * 0.25);
187
+ ctx.fillRect(x + width * 0.55, y + height * 0.5, width * 0.25, height * 0.25);
188
+
189
+ ctx.fillStyle = colors.text;
190
+ ctx.fillText('Tải nhà', x + width / 2, y + height + 20);
191
  }
192
 
193
  // Hàm vẽ mũi tên di chuyển
194
+ function drawMovingArrow(p1, p2, offset, arrowSize = 10) {
195
  const dx = p2.x - p1.x;
196
  const dy = p2.y - p1.y;
197
  const distance = Math.sqrt(dx * dx + dy * dy);
198
 
199
+ // Không vẽ nếu đường quá ngắn
200
  if (distance < arrowSize * 2) return;
201
 
202
+ // Đường dẫn chính của dòng chảy
203
+ ctx.strokeStyle = colors.flowPrimary;
204
+ ctx.lineWidth = 4;
205
  ctx.beginPath();
206
  ctx.moveTo(p1.x, p1.y);
207
  ctx.lineTo(p2.x, p2.y);
208
  ctx.stroke();
209
 
210
+ ctx.fillStyle = colors.flowSecondary; // Màu trắng cho mũi tên
211
  ctx.strokeStyle = '#333';
212
  ctx.lineWidth = 1;
213
 
214
+ // Tính toán số lượng mũi tên và vị trí
215
+ const numArrows = Math.floor(distance / (arrowSize * 2.5)); // Số lượng mũi tên trên đường
216
+ const arrowSpacing = distance / numArrows;
217
+
218
  for (let i = 0; i < numArrows; i++) {
219
+ let currentPos = (offset + i * arrowSpacing) % distance;
220
  if (currentPos < 0) currentPos += distance; // Xử lý offset âm
221
 
222
  const ratio = currentPos / distance;
 
244
  }
245
 
246
 
247
+ // --- Hàm chính để vẽ tất cả ---
248
  function draw() {
249
+ ctx.clearRect(0, 0, canvas.width, canvas.height); // Xóa canvas cho khung hình mới
250
 
251
+ // Vẽ các thành phần với biểu tượng trực quan chi tiết
252
+ drawSolarPanelIcon(solarPanel.x, solarPanel.y, solarPanel.width, solarPanel.height);
253
  drawInverterIcon(inverter.x, inverter.y, inverter.width, inverter.height);
254
  drawBatteryIcon(battery.x, battery.y, battery.width, battery.height);
255
+ drawHomeIcon(homeLoad.x, homeLoad.y, homeLoad.width, homeLoad.height);
 
 
 
 
 
 
 
 
 
 
256
 
257
  // Vẽ luồng năng lượng với mũi tên
258
+ // Pin -> Biến tần (DC)
259
  drawMovingArrow(
260
  { x: solarPanel.x + solarPanel.width / 2, y: solarPanel.y + solarPanel.height },
261
+ { x: inverter.x + inverter.width * 0.25, y: inverter.y + inverter.height / 2 },
262
  energyFlowOffset
263
  );
264
 
265
+ // Biến tần -> Tải nhà (AC)
266
  drawMovingArrow(
267
+ { x: inverter.x + inverter.width * 0.75, y: inverter.y + inverter.height / 2 },
268
  { x: homeLoad.x, y: homeLoad.y + homeLoad.height / 2 },
269
  energyFlowOffset
270
  );
271
 
272
+ // Biến tần -> Pin lưu trữ (năng lượng dư thừa, AC sang DC)
 
273
  drawMovingArrow(
274
  { x: inverter.x + inverter.width / 2, y: inverter.y + inverter.height },
275
  { x: battery.x + battery.width / 2, y: battery.y },
276
  energyFlowOffset
277
  );
278
 
279
+ // Pin lưu trữ -> Biến tần (khi tải cần, DC sang AC)
 
280
  drawMovingArrow(
281
  { x: battery.x + battery.width / 2, y: battery.y },
282
  { x: inverter.x + inverter.width / 2, y: inverter.y + inverter.height },