CoolFace
Apppublic

CVNSS/MapVN

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
script.js257 linesDownload Raw Back to root
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}