> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

# Marker SVG Leaflet: căn đầu nhọn bằng iconAnchor

> Đặt marker SVG tùy chỉnh trong Leaflet bằng cách ánh xạ đầu nhọn nhìn thấy tới iconAnchor. Tính cả khoảng đệm viewBox, cách co vừa theo tỷ lệ và thay đổi kích thước biểu tượng.

Source: https://perfectvector.com/vi/blog/leaflet-svg-marker-anchor

Language: vi
Author: Irene Kim
Published: 2026-10-05T08:00:00+09:00
Updated: 2026-10-05T08:00:00+09:00

Với marker SVG tùy chỉnh trong Leaflet, đặt `iconSize` bằng kích thước ảnh hiển thị và `iconAnchor` bằng vị trí pixel của đầu nhọn nhìn thấy bên trong ảnh đó. Đo từ góc trên trái của ảnh. Lề SVG trong suốt được tính vào kích thước, nên nửa chiều rộng và toàn bộ chiều cao có thể không khớp với ghim bất đối xứng hoặc có khoảng đệm.

Hướng dẫn này dùng một ghim gốc có đầu nhọn nằm bên phải tâm thân. Chúng tôi so sánh hai khung nguồn ở hai kích thước biểu tượng trong bản đồ trình duyệt không có lớp ô bản đồ. Nếu đã có hình SVG sạch, hãy dùng trực tiếp; việc đặt điểm neo không cần vector hóa.

## Tách hình, hộp ảnh và điểm trên bản đồ

Ba hệ tọa độ gặp nhau ở đây. Đường dẫn SVG mô tả hình theo đơn vị nguồn. `iconSize` xác lập hộp ảnh hiển thị bằng pixel. Leaflet đặt điểm bên trong hộp được `iconAnchor` chỉ định tại vị trí địa lý của marker. [Tài liệu tham khảo Icon](https://leafletjs.com/reference.html#icon) định nghĩa các tùy chọn này và gốc điểm neo ở trên trái.

Ghim thông thường có thể chạm giữa cạnh dưới, nhưng hình của bạn có thể kết thúc ở chỗ khác. Giá trị mặc định được tài liệu Leaflet nêu là điểm neo ở giữa khi kích thước được chỉ định. Các giá trị giữa cạnh dưới trong thí nghiệm là giả định tường minh chúng tôi kiểm thử, không phải mặc định đó.

[Hướng dẫn biểu tượng tùy chỉnh chính thức](https://leafletjs.com/examples/custom-icons/) cho thấy cách truyền một thể hiện `L.icon` cho marker. Công việc thêm với hình đã nhập là tìm điểm bên trong ảnh cần chạm tọa độ bản đồ.

**Đầu nhọn nhìn thấy và điểm neo**

![So sánh khái niệm một ghim vị trí bất đối xứng trong vùng vẽ có khoảng đệm, với đầu nhọn lệch khỏi dấu chữ thập rồi chạm vào dấu đó](https://perfectvector.com/blog/leaflet-svg-marker-anchor/source-tip-anchor.webp)

*Minh họa: tính cả toàn bộ khung ảnh khi đặt đầu nhọn nhìn thấy. Thí nghiệm trình duyệt có đo lường bên dưới dùng các tệp SVG được tạo riêng.*

## Ánh xạ đầu nhọn nguồn sang pixel biểu tượng

Đầu nhọn ghim của chúng tôi ở tọa độ nguồn `(40, 80)`. Khung sát là `viewBox="0 0 60 80"`. Phiên bản có khoảng đệm chỉ đổi khung thành `viewBox="-20 -10 100 120"`; cả hai tệp giữ cùng đường dẫn và khoảng trống trong suốt.

[Tài liệu tham khảo `viewBox`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/viewBox) mô tả hình chữ nhật nguồn này. Khoảng trống bên trong đi cùng hình. [Hướng dẫn hộp bao SVG](https://perfectvector.com/vi/blog/svg-bounding-box) giải thích vì sao vùng vẽ và hình được tô có thể có phạm vi khác nhau.

Cả hai phiên bản dùng `preserveAspectRatio="xMidYMid meet"` tường minh. Thiết lập này co vừa khung một cách đồng đều trong khung nhìn và căn giữa phần trống còn lại. Xem [tài liệu tham khảo preserveAspectRatio của MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/preserveAspectRatio) cho các thiết lập đó.

Với chế độ co vừa này, gọi `W` và `H` là kích thước biểu tượng, `(minX, minY, vbW, vbH)` là viewBox và `(tipX, tipY)` là đầu nhọn nguồn:

```text
s = min(W / vbW, H / vbH)
offsetX = (W - vbW * s) / 2
offsetY = (H - vbH * s) / 2
anchorX = (tipX - minX) * s + offsetX
anchorY = (tipY - minY) * s + offsetY
```

Với biểu tượng 60×80 có khoảng đệm, `s` là `0.6`. Khung nguồn 100×120 chiếm 60×72 pixel, để lại bốn pixel ở trên và dưới. Đầu nhọn trở thành `[36, 58]`, gồm độ lệch dọc đó. Chỉ lấy tỷ lệ từng chiều riêng sẽ bỏ qua bước co vừa đồng đều.

| Khung nguồn                    | Kích thước biểu tượng | Điểm neo đầu nhọn đã tính |
| ------------------------------ | --------------------- | ------------------------- |
| Sát, 0 0 60 80                 | 60×80                 | \[40, 80]                 |
| Sát, 0 0 60 80                 | 90×120                | \[60, 120]                |
| Có khoảng đệm, -20 -10 100 120 | 60×80                 | \[36, 58]                 |
| Có khoảng đệm, -20 -10 100 120 | 90×120                | \[54, 87]                 |

Các phép tính này áp dụng cho ảnh không biến đổi và thiết lập `meet` căn giữa tường minh của chúng tôi. Căn chỉnh khác, `slice` hoặc `none` cần phép ánh xạ tọa độ tương ứng. [Hướng dẫn tỷ lệ khung hình](https://perfectvector.com/vi/blog/svg-preserveaspectratio) trình bày các lựa chọn đó. Các biến đổi CSS bổ sung trên ảnh marker cũng cần kiểm tra riêng.

## Kiểm tra vị trí trong bản đồ không có lớp ô

Chúng tôi dùng Leaflet 1.9.4, bản phát hành ổn định được xác định trên [trang tải chính thức](https://leafletjs.com/download.html). Thí nghiệm tải JavaScript và CSS cố định phiên bản cục bộ và không thêm lớp ô bản đồ. Mỗi bản đồ chiếu tọa độ `[0, 0]` lên dấu chữ thập xanh; nó hiển thị một ảnh SVG cạnh điểm tham chiếu đó.

Kiểm tra nguồn thấy một đường dẫn phức hợp giống hệt và không có ảnh raster nhúng trong mỗi SVG. Kích thước ảnh trong trình duyệt là 60×80 và 90×120 pixel như yêu cầu. Dùng hình chữ nhật quan sát được của từng ảnh, chúng tôi so sánh vị trí đầu nhọn được ánh xạ với điểm địa lý đã chiếu:

| Nguồn         | Kích thước biểu tượng | Sai số khi giả định điểm neo giữa cạnh dưới | Sai số với điểm neo đầu nhọn đã tính |
| ------------- | --------------------- | ------------------------------------------- | ------------------------------------ |
| Sát           | 60×80                 | 10 px sang phải                             | 0 px                                 |
| Sát           | 90×120                | 15 px sang phải                             | 0 px                                 |
| Có khoảng đệm | 60×80                 | 6 px sang phải, 22 px lên trên              | 0 px                                 |
| Có khoảng đệm | 90×120                | 9 px sang phải, 33 px lên trên              | 0 px                                 |

Dấu chữ thập cũng chạm các đầu ghim đã sửa một cách rõ ràng. Đây là quan sát của mẫu tự tạo trong một trình duyệt Chrome, không phải bảo đảm cho mọi SVG, trình duyệt hay cách tích hợp bản đồ. Ghim có khoảng đệm vẫn nhỏ hơn bên trong cùng hộp ảnh sau khi sửa điểm neo. Vị trí và kích thước hình là những quyết định riêng.

## Sao chép mã khởi đầu marker chạy được

Lưu HTML hoàn chỉnh này thành `marker.html` và phục vụ từ máy chủ web cục bộ. Nó tải các tệp Leaflet cố định phiên bản từ CDN được liên kết trên trang tải Leaflet. SVG được mã hóa thành URL ảnh, nên ví dụ không cần tệp biểu tượng hay dịch vụ ô bản đồ. Cần truy cập internet cho hai tệp thư viện đó.

```html
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Leaflet SVG tip anchor</title>
<link rel="stylesheet"
      href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<style>
  #map { width: 360px; height: 300px; background: #edf3ef; }
  .cross { position: absolute; z-index: 1000; pointer-events: none;
    width: 24px; height: 24px; transform: translate(-50%, -50%); }
  .cross::before, .cross::after { content: ""; position: absolute;
    background: #006cbe; }
  .cross::before { top: 12px; width: 24px; height: 1px; }
  .cross::after { left: 12px; width: 1px; height: 24px; }
</style>
<div id="map"></div>
<pre id="status">Loading marker…</pre>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="60" height="80" viewBox="-20 -10 100 120" preserveAspectRatio="xMidYMid meet"><path fill="#bf593c" fill-rule="evenodd" d="M30 0 C46 0 60 12 60 28 C60 46 46 54 40 80 C30 62 0 46 0 28 C0 12 14 0 30 0 Z M40 26 A10 10 0 1 0 20 26 A10 10 0 1 0 40 26 Z"/></svg>`;
const size = [60, 80];
const viewBox = [-20, -10, 100, 120];
const sourceTip = [40, 80];
const scale = Math.min(size[0] / viewBox[2], size[1] / viewBox[3]);
const anchor = [
  (sourceTip[0] - viewBox[0]) * scale
    + (size[0] - viewBox[2] * scale) / 2,
  (sourceTip[1] - viewBox[1]) * scale
    + (size[1] - viewBox[3] * scale) / 2
];
const map = L.map("map", {
  zoomControl: false, attributionControl: false,
  dragging: false, scrollWheelZoom: false, doubleClickZoom: false,
  boxZoom: false, keyboard: false, touchZoom: false
}).setView([0, 0], 2);
const position = [0, 0];
const icon = L.icon({
  iconUrl: "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg),
  iconSize: size,
  iconAnchor: anchor
});
const marker = L.marker(position, { icon }).addTo(map);
const container = document.querySelector("#map");
const cross = document.createElement("span");
cross.className = "cross";
container.append(cross);
const point = map.latLngToContainerPoint(position);
cross.style.left = point.x + "px";
cross.style.top = point.y + "px";
(async () => {
  const image = marker.getElement();
  await image.decode();
  const ir = image.getBoundingClientRect();
  const mr = container.getBoundingClientRect();
  const displacement = [
    ir.left + anchor[0] - mr.left - point.x,
    ir.top + anchor[1] - mr.top - point.y
  ];
  document.querySelector("#status").textContent = JSON.stringify({
    version: L.version, size, anchor,
    observedImageSize: [ir.width, ir.height], displacement
  }, null, 2);
})();
</script>
</html>
```

Trạng thái hiển thị phải báo `anchor: [36, 58]`, kích thước ảnh `[60, 80]` và độ dịch chuyển bằng 0. Đổi `size` thành `[90, 120]`; cùng phép tính cho `[54, 87]`. Để dùng khung sát thay thế, đổi cả `viewBox` của SVG và mảng `viewBox` JavaScript thành `0 0 60 80`. Giữ chúng nhất quán.

Đường dẫn có đường bao trong hình tròn với `fill-rule="evenodd"`, tạo khoảng trống thực sự. [Tài liệu tham khảo fill-rule của MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/fill-rule) giải thích quy tắc này. Ví dụ không thêm viền hay biến đổi cho vùng chứa bản đồ; những thứ đó đòi hỏi chú ý khi so sánh hình chữ nhật khung nhìn với tọa độ vùng chứa.

## Kiểm tra đích trước khi đổi hình

Nếu marker vẫn lệch tọa độ, kiểm tra theo thứ tự:

1. Xác nhận ảnh SVG đã tải và phần tử marker có chiều rộng cùng chiều cao pixel dự kiến.
2. Xác định đầu nhọn nguồn thực tế, gồm mọi biến đổi bên trong SVG.
3. Kiểm tra toàn bộ viewBox, tọa độ tối thiểu và chế độ co vừa.
4. Tính lại điểm neo bất cứ khi nào đổi khung hoặc kích thước biểu tượng.
5. Gỡ các quy tắc CSS ghi đè không liên quan khi thử, rồi kiểm tra lại kiểu cuối cùng và các kích thước hiển thị dự kiến.

Giữ tọa độ địa lý cố định trong quá trình này. Di chuyển tọa độ để bù khoảng đệm ảnh sẽ che giấu lỗi đặt hình.

Đây là quy trình marker trình duyệt. [Marker SVG QGIS](https://perfectvector.com/vi/blog/qgis-svg-markers) dùng các điều khiển đích và cú pháp màu tô có tham số riêng; giữ bản chính SVG thông thường để bàn giao cho trình duyệt nói chung.

Nếu biểu tượng chỉ có ở dạng PNG hoặc JPG và bạn cần đường bao có thể chỉnh sửa, hãy [chuyển hình bằng PerfectVector](https://perfectvector.com/vi/image-to-vector), kiểm tra khoảng trống và đầu nhọn, rồi lưu bản chính SVG sạch. Sau đó tính điểm neo Leaflet từ khung nguồn cuối. Vector hóa chuẩn bị hình; mã bản đồ cung cấp vị trí và cách đặt. Nếu vấn đề duy nhất là độ lệch trong SVG có sẵn, sửa trực tiếp khung hoặc điểm neo. [Hướng dẫn vector hóa ảnh](https://perfectvector.com/vi/blog/what-is-image-vectorization) giải thích khi nào chuyển nguồn raster là hữu ích.

## Câu hỏi thường gặp

**iconAnchor có phải vĩ độ và kinh độ không?**
Không. Đó là vị trí pixel bên trong ảnh biểu tượng hiển thị, đo từ góc trên trái. Vị trí địa lý của marker được cung cấp riêng.

**Tại sao SVG có khoảng đệm vẫn nhỏ sau khi sửa điểm neo?**
Khung nguồn trống vẫn chiếm một phần hộp ảnh. Sửa điểm neo đặt đầu nhọn đúng vị trí; nó không xóa khoảng trắng hay phóng to hình được tô.

**Tôi có nên luôn dùng nửa chiều rộng và toàn bộ chiều cao không?**
Chỉ dùng cặp đó khi điểm gắn nhìn thấy thực sự nằm ở đó. Ghim bất đối xứng và nguồn có khoảng đệm của chúng tôi cần giá trị khác, dù cả hai đều là hình marker vị trí.

**Chuyển PNG thành SVG có sửa vị trí marker không?**
Chuyển đổi có thể cung cấp hình có thể chỉnh sửa. Khung nguồn cuối, kích thước hiển thị, điểm neo và tọa độ bản đồ vẫn quyết định vị trí marker.

## Nguồn tham khảo

1. [Leaflet — Tài liệu tham khảo Icon](https://leafletjs.com/reference.html#icon) — Kích thước pixel, gốc điểm neo, căn giữa mặc định và căn chỉnh địa lý.
2. [Leaflet — Marker với biểu tượng tùy chỉnh](https://leafletjs.com/examples/custom-icons/) — Tạo biểu tượng và gán cho marker.
3. [Leaflet — Tải xuống](https://leafletjs.com/download.html) — Bản phát hành ổn định và các liên kết JavaScript, CSS cố định phiên bản.
4. [MDN — viewBox](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/viewBox) — Hình chữ nhật tọa độ nguồn dùng trong hai khung.
5. [MDN — preserveAspectRatio](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/preserveAspectRatio) — Co vừa đồng đều và căn giữa khoảng trống còn lại cho xMidYMid meet.
6. [MDN — fill-rule](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/fill-rule) — Quy tắc chẵn-lẻ dùng cho khoảng trống trong suốt.

## Explore

- [Read the HTML article](https://perfectvector.com/vi/blog/leaflet-svg-marker-anchor)
- [Ảnh SVG cho nút Cytoscape.js: độ vừa và khoảng đệm](https://perfectvector.com/vi/blog/cytoscape-svg-node-artwork.md)
- [Đo độ dài đường dẫn SVG mà không nhầm tỷ lệ](https://perfectvector.com/vi/blog/measure-svg-path-length.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
