CVNSS/MapVN
0
1const canvas = document.getElementById('mapCanvas');
2const ctx = canvas.getContext('2d');
3
4const img = document.querySelector('.map-container img');
5canvas.width = img.clientWidth;
6canvas.height = img.clientHeight;
7
8const mapElement = document.getElementById('vietnam-map');
9
10let areas = [];
11let locationsData = [];
12
13fetch('provinces.json')
14 .then(response => {
15 if (!response.ok) {
16 throw new Error('Network response was not ok');
17 }
18 return response.json();
19 })
20 .then(data => {
21 locationsData = data;
22 areas = locationsData.map(location => {
23 if (!location.coords) {
24 console.error('Missing coords for location:', location);
25 return { coords: [], shape: 'poly' };
26 }
27 const coords = location.coords.split(',').map(Number);
28 const shape = 'poly';
29 return { coords, shape };
30 });
31 createMapAreas();
32 })
33 .catch(error => {
34 console.error('Error loading the JSON file:', error);
35 });
36
37function updateCanvasSize() {
38 // Cập nhật kích thước canvas theo kích thước thực của ảnh
39 canvas.width = img.offsetWidth;
40 canvas.height = img.offsetHeight;
41 canvas.style.width = `${img.offsetWidth}px`;
42 canvas.style.height = `${img.offsetHeight}px`;
43}
44
45// Sửa lại event listener resize
46window.addEventListener('resize', () => {
47 updateCanvasSize();
48 createMapAreas();
49});
50
51// Thêm vào sau khi ảnh đã load
52img.addEventListener('load', () => {
53 updateCanvasSize();
54 createMapAreas();
55});
56
57// Sửa lại hàm createMapAreas
58function createMapAreas() {
59 mapElement.innerHTML = '';
60 const scaleX = img.offsetWidth / img.naturalWidth;
61 const scaleY = img.offsetHeight / img.naturalHeight;
62
63 areas.forEach((area) => {
64 const scaledCoords = area.coords.map((val, index) => {
65 return index % 2 === 0
66 ? Math.round(val * scaleX)
67 : Math.round(val * scaleY);
68 });
69
70 const areaElement = document.createElement('area');
71 areaElement.setAttribute('shape', area.shape);
72 areaElement.setAttribute('coords', scaledCoords.join(','));
73 areaElement.setAttribute('href', 'javascript:void(0)');
74 mapElement.appendChild(areaElement);
75 });
76
77 setupEventListeners();
78}
79
80const drawOutline = (coords) => {
81 ctx.clearRect(0, 0, canvas.width, canvas.height);
82 ctx.beginPath();
83 const scaleX = canvas.width / img.naturalWidth;
84 const scaleY = canvas.height / img.naturalHeight;
85
86 ctx.moveTo(coords[0] * scaleX, coords[1] * scaleY);
87 for (let i = 2; i < coords.length; i += 2) {
88 ctx.lineTo(coords[i] * scaleX, coords[i + 1] * scaleY);
89 }
90
91 ctx.closePath();
92
93 // Thêm fill màu da cam nhạt
94 ctx.fillStyle = 'rgba(255, 165, 0, 0.3)'; // Màu da cam với độ trong suốt 0.3
95 ctx.fill();
96
97 // Vẽ viền đỏ
98 ctx.strokeStyle = 'red';
99 ctx.lineWidth = 2;
100 ctx.stroke();
101};
102
103const clearOutline = () => {
104 ctx.clearRect(0, 0, canvas.width, canvas.height);
105};
106
107// Tooltip functionality
108const tooltip = document.querySelector('.tooltip-box');
109
110const showTooltip = (data, event) => {
111 tooltip.style.display = 'block';
112
113 let nameText = '';
114 let areaText = '';
115 let areaTotal = 0;
116 let popText = '';
117 let popTotal = 0;
118
119 if (typeof data.name === 'object' && Object.keys(data.name).length > 1) {
120 // Gộp tên
121 nameText = Object.values(data.name).join(' + ');
122
123 // Diện tích
124 for (const key in data.naturalArea) {
125 const value = parseFloat(data.naturalArea[key].replace('.', '').replace(',', '.'));
126 areaText += `<p>${data.name[key]}: ${data.naturalArea[key]}</p>`;
127 areaTotal += value;
128 }
129
130 // Dân số
131 for (const key in data.population) {
132 const value = parseFloat(data.population[key].replace('.', '').replace(',', '.'));
133 popText += `<p>${data.name[key]}: ${data.population[key]}</p>`;
134 popTotal += value;
135 }
136
137 tooltip.innerHTML = `
138 <h2>${nameText}</h2>
139 <hr>
140 <h3>DIỆN TÍCH TỰ NHIÊN (KM²)</h3>
141 ${areaText}
142 <p><strong>Tổng diện tích:</strong> ${areaTotal.toLocaleString('vi-VN')}</p>
143 <hr>
144 <h3>QUY MÔ DÂN SỐ (NGHÌN NGƯỜI)</h3>
145 ${popText}
146 <p><strong>Tổng dân số:</strong> ${popTotal.toLocaleString('vi-VN')}</p>
147 <hr>
148 <p>Tên tỉnh, thành sau sáp nhập: <span class="highlight">${data.mergedName}</span></p>
149 <p>Trung tâm chính trị - hành chính: <span class="highlight">${data.center}</span></p>
150 `;
151 } else {
152 // Trường hợp chỉ có 1 tỉnh
153 tooltip.innerHTML = `
154 <h2>${data.name}</h2>
155 <hr>
156 <h3>DIỆN TÍCH TỰ NHIÊN (KM²)</h3>
157 <p>${data.name}: ${data.naturalArea}</p>
158 <p><strong>Tổng diện tích:</strong> ${data.naturalArea}</p>
159 <hr>
160 <h3>QUY MÔ DÂN SỐ (NGHÌN NGƯỜI)</h3>
161 <p>${data.name}: ${data.population}</p>
162 <p><strong>Tổng dân số:</strong> ${data.population}</p>
163 <hr>
164 <!-- <p>Tên tỉnh, thành sau sáp nhập: <span class="highlight">${data.mergedName}</span></p>
165 <p>Trung tâm chính trị - hành chính: <span class="highlight">${data.center}</span></p>
166 -->
167 `;
168 }
169
170 // Hiện tooltip trước để lấy kích thước thực
171 tooltip.style.visibility = 'hidden';
172 tooltip.style.display = 'block';
173
174 const tooltipWidth = tooltip.offsetWidth;
175 const tooltipHeight = tooltip.offsetHeight;
176
177 // Lấy kích thước và vị trí của viewport
178 const viewportWidth = window.innerWidth;
179 const viewportHeight = window.innerHeight;
180 const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
181 const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
182
183 // Tính toán vị trí tốt nhất cho tooltip
184 let left = event.pageX + 10;
185 let top = event.pageY + 10;
186
187 // Kiểm tra và điều chỉnh vị trí ngang
188 if (left + tooltipWidth > viewportWidth + scrollLeft) {
189 left = event.pageX - tooltipWidth - 10;
190 }
191
192 // Kiểm tra và điều chỉnh vị trí dọc
193 if (top + tooltipHeight > viewportHeight + scrollTop) {
194 top = event.pageY - tooltipHeight - 10;
195
196 // Nếu vẫn vượt quá phía trên viewport
197 if (top < scrollTop) {
198 // Đặt tooltip ở giữa màn hình theo chiều dọc
199 top = scrollTop + (viewportHeight - tooltipHeight) / 2;
200 }
201 }
202
203 // Đảm bảo tooltip không bị cắt ở các cạnh
204 left = Math.max(scrollLeft + 10, Math.min(left, viewportWidth + scrollLeft - tooltipWidth - 10));
205 top = Math.max(scrollTop + 10, Math.min(top, viewportHeight + scrollTop - tooltipHeight - 10));
206
207 // Áp dụng vị trí và hiện tooltip
208 tooltip.style.left = `${left}px`;
209 tooltip.style.top = `${top}px`;
210 tooltip.style.visibility = 'visible';
211 tooltip.classList.add('active');
212};
213
214// Thêm event listener cho viewport resize
215window.addEventListener('resize', () => {
216 if (tooltip.classList.contains('active')) {
217 tooltip.classList.remove('active');
218 }
219});
220
221const hideTooltip = () => {
222 tooltip.classList.remove('active');
223};
224
225function setupEventListeners() {
226 document.querySelectorAll('area').forEach((area, index) => {
227 area.addEventListener('mouseenter', () => {
228 drawOutline(areas[index].coords);
229 });
230 area.addEventListener('mouseleave', clearOutline);
231
232 area.addEventListener('mouseenter', (event) => {
233 showTooltip(locationsData[index], event);
234 });
235 area.addEventListener('mousemove', (event) => {
236 // Chỉ cập nhật vị trí nếu tooltip không bị overflow
237 const tooltipRect = tooltip.getBoundingClientRect();
238 const viewportHeight = window.innerHeight;
239 const viewportWidth = window.innerWidth;
240
241 // Chỉ di chuyển tooltip theo chuột khi có đủ không gian
242 if (tooltipRect.width + event.clientX < viewportWidth &&
243 tooltipRect.height + event.clientY < viewportHeight) {
244 tooltip.style.left = `${event.pageX + 10}px`;
245 tooltip.style.top = `${event.pageY + 10}px`;
246 }
247 });
248 area.addEventListener('mouseleave', hideTooltip);
249 });
250
251 document.addEventListener('mousemove', (event) => {
252 if (!event.target.closest('area') && !tooltip.contains(event.target)) {
253 tooltip.style.display = 'none';
254 tooltip.innerHTML = '';
255 }
256 });
257}