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

# หมุด SVG ใน Leaflet: จัดปลายด้วย iconAnchor

> วางหมุด SVG ใน Leaflet โดยแมปปลายที่เห็นกับ iconAnchor คำนึงถึงขอบว่าง viewBox การปรับสัดส่วน และการเปลี่ยนขนาดไอคอน

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

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

สำหรับหมุด SVG กำหนดเองใน Leaflet ตั้ง `iconSize` เป็นขนาดภาพที่แสดง และ `iconAnchor` เป็นตำแหน่งพิกเซลของปลายที่เห็นภายในภาพ วัดจากมุมบนซ้ายของภาพ ขอบโปร่งใส SVG นับรวมในขนาดด้วย ดังนั้นครึ่งความกว้างกับความสูงเต็มอาจไม่ตรงปลายหมุดที่ไม่สมมาตรหรือมีขอบว่าง

คู่มือนี้ใช้หมุดต้นฉบับที่ปลายอยู่ขวาจากกลางตัว เราเทียบกรอบต้นฉบับสองแบบที่ขนาดไอคอนสองขนาดในแผนที่เบราว์เซอร์ไม่มีไทล์ หากมีภาพ SVG สะอาดแล้ว ให้ใช้โดยตรง การวางจุดยึดไม่ต้องแปลงเวกเตอร์

## แยกภาพ กรอบภาพ และจุดแผนที่

มีระบบพิกัดสามชุดมาพบกัน เส้น SVG อธิบายภาพในหน่วยต้นฉบับ `iconSize` กำหนดกรอบภาพที่แสดงเป็นพิกเซล Leaflet วางจุดในกรอบที่ `iconAnchor` ระบุไว้ตรงตำแหน่งภูมิศาสตร์ของหมุด [เอกสาร Icon](https://leafletjs.com/reference.html#icon) นิยามตัวเลือกเหล่านี้และจุดเริ่มยึดที่มุมบนซ้าย

หมุดทั่วไปอาจไปถึงกลางขอบล่าง แต่ภาพของคุณอาจสิ้นสุดที่อื่น ค่าเริ่มต้นตามเอกสาร Leaflet คือจุดยึดกลางเมื่อระบุขนาด ค่ากลางล่างในทดลองของเราเป็นสมมติฐานชัดเจนที่ทดสอบ ไม่ใช่ค่าเริ่มต้นนั้น

[บทเรียนไอคอนกำหนดเองทางการ](https://leafletjs.com/examples/custom-icons/) แสดงวิธีส่งอินสแตนซ์ `L.icon` ให้หมุด งานเพิ่มสำหรับภาพนำเข้าคือหาจุดภายในภาพที่ควรแตะพิกัดแผนที่

**ปลายที่เห็นและจุดยึด**

![การเทียบเชิงแนวคิดของหมุดตำแหน่งไม่สมมาตรในพื้นที่มีขอบว่าง โดยปลายห่างจากกากบาทก่อน แล้วมาพบกากบาท](https://perfectvector.com/blog/leaflet-svg-marker-anchor/source-tip-anchor.webp)

*ภาพอธิบาย: คำนึงถึงกรอบภาพทั้งหมดเมื่อวางปลายที่เห็น การทดลองเบราว์เซอร์ที่วัดด้านล่างใช้ SVG ที่สร้างแยก*

## แมปปลายต้นฉบับเป็นพิกเซลไอคอน

ปลายหมุดของเราอยู่พิกัดต้นฉบับ `(40, 80)` กรอบกระชับคือ `viewBox="0 0 60 80"` เวอร์ชันเผื่อพื้นที่เปลี่ยนเฉพาะกรอบเป็น `viewBox="-20 -10 100 120"` ทั้งสองเก็บเส้นเดิมและช่องโปร่งใส

[เอกสาร `viewBox`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/viewBox) อธิบายสี่เหลี่ยมต้นทางนี้ พื้นที่ว่างภายในเดินไปกับภาพ [คู่มือกรอบขอบเขต SVG](https://perfectvector.com/th/blog/svg-bounding-box) อธิบายว่าทำไมพื้นที่งานกับรูปที่ระบายมีขอบเขตต่างกันได้

ทั้งสองระบุ `preserveAspectRatio="xMidYMid meet"` ชัดเจน เพื่อปรับกรอบสม่ำเสมอภายใน viewport และจัดกลางพื้นที่เหลือ ดู[เอกสาร preserveAspectRatio ของ MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/preserveAspectRatio) สำหรับค่านี้

ในโหมดนี้ ให้ `W` และ `H` เป็นขนาดไอคอน `(minX, minY, vbW, vbH)` เป็น viewBox และ `(tipX, tipY)` เป็นปลายต้นฉบับ:

```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
```

สำหรับไอคอนเผื่อพื้นที่ 60×80 ค่า `s` คือ `0.6` กรอบต้นฉบับ 100×120 กินพื้นที่ 60×72 พิกเซล เหลือบนล่างด้านละสี่ ปลายกลายเป็น `[36, 58]` รวมระยะเยื้องแนวตั้งนั้น การใช้เพียงสัดส่วนแต่ละมิติแยกกันจะพลาดขั้นปรับสม่ำเสมอ

| กรอบต้นฉบับ                  | ขนาดไอคอน | จุดยึดปลายที่คำนวณ |
| ---------------------------- | --------- | ------------------ |
| กระชับ 0 0 60 80             | 60×80     | \[40, 80]          |
| กระชับ 0 0 60 80             | 90×120    | \[60, 120]         |
| เผื่อพื้นที่ -20 -10 100 120 | 60×80     | \[36, 58]          |
| เผื่อพื้นที่ -20 -10 100 120 | 90×120    | \[54, 87]          |

สูตรนี้ใช้กับภาพที่ไม่แปลงรูปของเราและค่า `meet` จัดกลางชัดเจน การจัดแนวอื่น `slice` หรือ `none` ต้องใช้การแมปพิกัดตรงกัน [คู่มือสัดส่วน](https://perfectvector.com/th/blog/svg-preserveaspectratio) ครอบคลุมตัวเลือก การแปลง CSS เพิ่มบนภาพหมุดต้องตรวจแยกด้วย

## ตรวจตำแหน่งในแผนที่ไม่มีไทล์

เราใช้ Leaflet 1.9.4 รุ่นเสถียรที่ระบุใน[หน้าดาวน์โหลดทางการ](https://leafletjs.com/download.html) การทดลองโหลด JavaScript และ CSS ที่ตรึงรุ่นในเครื่อง และไม่เพิ่มเลเยอร์ไทล์ แผนที่แต่ละอันฉายพิกัด `[0, 0]` เป็นกากบาทน้ำเงิน และแสดงภาพ SVG หนึ่งภาพข้างจุดอ้างอิง

การตรวจต้นฉบับพบเส้นประกอบเดียวกันหนึ่งเส้นและไม่มีแรสเตอร์ฝังตัวในแต่ละ SVG ขนาดภาพในเบราว์เซอร์เป็น 60×80 กับ 90×120 พิกเซลตามขอ เราใช้กรอบภาพที่สังเกตเทียบตำแหน่งปลายหลังแมปกับจุดภูมิศาสตร์ที่ฉาย:

| ต้นฉบับ      | ขนาดไอคอน | คลาดเคลื่อนเมื่อสมมติยึดกลางล่าง | คลาดเคลื่อนเมื่อคำนวณจุดยึดปลาย |
| ------------ | --------- | -------------------------------- | ------------------------------- |
| กระชับ       | 60×80     | ขวา 10 px                        | 0 px                            |
| กระชับ       | 90×120    | ขวา 15 px                        | 0 px                            |
| เผื่อพื้นที่ | 60×80     | ขวา 6 px ขึ้น 22 px              | 0 px                            |
| เผื่อพื้นที่ | 90×120    | ขวา 9 px ขึ้น 33 px              | 0 px                            |

กากบาทยังเห็นตรงปลายหมุดที่แก้แล้วด้วย นี่เป็นข้อสังเกตไฟล์ทดสอบที่สร้างใน Chrome หนึ่งเบราว์เซอร์ ไม่ใช่รับประกันทุก SVG เบราว์เซอร์ หรือการรวมแผนที่ หมุดเผื่อพื้นที่ยังเล็กกว่าในกรอบภาพเดียวกันหลังแก้จุดยึด ตำแหน่งกับขนาดภาพเป็นคนละการตัดสินใจ

## คัดลอกตัวอย่างหมุดที่ทำงานได้

บันทึก HTML ครบนี้เป็น `marker.html` และให้บริการจากเว็บเซิร์ฟเวอร์ในเครื่อง มันโหลดไฟล์ Leaflet ที่ตรึงรุ่นจาก CDN ที่หน้าดาวน์โหลด Leaflet เชื่อมไว้ SVG เข้ารหัสเป็น URL ภาพ จึงไม่ต้องมีไฟล์ไอคอนหรือบริการไทล์ ต้องใช้อินเทอร์เน็ตสำหรับไฟล์ไลบรารีสองไฟล์นั้น

```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>
```

สถานะที่แสดงควรรายงาน `anchor: [36, 58]` ขนาดภาพ `[60, 80]` และระยะคลาดศูนย์ เปลี่ยน `size` เป็น `[90, 120]` การคำนวณเดิมให้ `[54, 87]` หากใช้กรอบกระชับ ให้เปลี่ยนทั้ง `viewBox` ใน SVG และอาร์เรย์ JavaScript `viewBox` เป็น `0 0 60 80` คงสองจุดให้ตรงกัน

เส้นมีขอบวงกลมด้านในกับ `fill-rule="evenodd"` สร้างช่องจริง [เอกสาร fill-rule ของ MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/fill-rule) อธิบายกฎ ตัวอย่างไม่เพิ่มขอบหรือการแปลงให้ตัวครอบแผนที่ หากเพิ่มต้องระวังเมื่อเทียบกรอบ viewport กับพิกัดตัวครอบ

## ตรวจปลายทางก่อนเปลี่ยนภาพ

หากหมุดยังไม่ตรงพิกัด ให้ตรวจตามลำดับ:

1. ยืนยันว่า SVG โหลดและองค์ประกอบหมุดมีความกว้างสูงพิกเซลที่ต้องการ
2. หาปลายต้นฉบับจริง รวมการแปลงภายใน SVG
3. ตรวจ viewBox เต็ม พิกัดต่ำสุด และโหมดปรับภาพ
4. คำนวณจุดยึดใหม่ทุกครั้งที่เปลี่ยนกรอบหรือขนาดไอคอน
5. ลบ CSS ที่เขียนทับและไม่เกี่ยวข้องขณะทดสอบ แล้วตรวจสไตล์สุดท้ายกับขนาดแสดงเป้าหมายอีกครั้ง

คงพิกัดภูมิศาสตร์ไว้ระหว่างตรวจ การย้ายพิกัดชดเชยขอบภาพจะซ่อนข้อผิดพลาดวางภาพ

นี่เป็นขั้นตอนหมุดเบราว์เซอร์ [หมุด SVG ใน QGIS](https://perfectvector.com/th/blog/qgis-svg-markers) ใช้ตัวควบคุมปลายทางและไวยากรณ์สีพารามิเตอร์ของตน เก็บต้นฉบับ SVG ทั่วไปสำหรับส่งเบราว์เซอร์ทั่วไป

หากไอคอนมีเพียง PNG หรือ JPG และต้องการเส้นขอบแก้ไขได้ ให้[แปลงภาพด้วย PerfectVector](https://perfectvector.com/th/image-to-vector) ตรวจช่องและปลาย แล้วบันทึกต้นฉบับ SVG สะอาด จากนั้นคำนวณจุดยึด Leaflet จากกรอบต้นฉบับสุดท้าย การแปลงเวกเตอร์เตรียมภาพ ส่วนโค้ดแผนที่ให้ตำแหน่งและการวาง หากปัญหามีแค่ระยะเยื้องใน SVG เดิม ให้แก้กรอบหรือจุดยึดโดยตรง [คู่มือแปลงภาพเป็นเวกเตอร์](https://perfectvector.com/th/blog/what-is-image-vectorization) อธิบายว่าเมื่อใดการแปลงแรสเตอร์มีประโยชน์

## คำถามที่พบบ่อย

**iconAnchor คือพิกัดละติจูดลองจิจูดหรือไม่?**
ไม่ เป็นตำแหน่งพิกเซลภายในภาพไอคอนที่แสดง วัดจากมุมบนซ้าย ตำแหน่งภูมิศาสตร์หมุดให้แยกกัน

**ทำไม SVG เผื่อพื้นที่ยังเล็กหลังแก้จุดยึด?**
กรอบว่างต้นฉบับยังครอบครองส่วนหนึ่งของกรอบภาพ การแก้จุดยึดวางปลาย ไม่ได้ลบพื้นที่ว่างหรือขยายรูปที่ระบาย

**ควรใช้ครึ่งความกว้างกับความสูงเต็มเสมอหรือไม่?**
ใช้คู่นั้นเฉพาะเมื่อจุดแตะที่เห็นอยู่ตรงนั้นจริง หมุดไม่สมมาตรกับต้นฉบับเผื่อพื้นที่ของเราต้องการค่าต่างกัน แม้ทั้งคู่เป็นภาพหมุดตำแหน่ง

**แปลง PNG เป็น SVG แก้ตำแหน่งหมุดหรือไม่?**
การแปลงให้ภาพแก้ไขได้ แต่กรอบต้นฉบับสุดท้าย ขนาดแสดง จุดยึด และพิกัดแผนที่ยังเป็นตัวกำหนดตำแหน่ง

## แหล่งข้อมูล

1. [Leaflet — เอกสาร Icon](https://leafletjs.com/reference.html#icon) — ขนาดพิกเซล จุดเริ่มยึด การจัดกลางเริ่มต้น และการจัดแนวภูมิศาสตร์
2. [Leaflet — หมุดไอคอนกำหนดเอง](https://leafletjs.com/examples/custom-icons/) — สร้างไอคอนและกำหนดให้หมุด
3. [Leaflet — ดาวน์โหลด](https://leafletjs.com/download.html) — รุ่นเสถียรและลิงก์ JavaScript กับ CSS ที่ตรึงรุ่น
4. [MDN — viewBox](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/viewBox) — สี่เหลี่ยมพิกัดต้นทางที่ใช้ในสองกรอบ
5. [MDN — preserveAspectRatio](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/preserveAspectRatio) — การปรับสม่ำเสมอและพื้นที่เหลือจัดกลางสำหรับ xMidYMid meet
6. [MDN — fill-rule](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/fill-rule) — กฎคู่คี่สำหรับช่องโปร่งใส

## Explore

- [Read the HTML article](https://perfectvector.com/th/blog/leaflet-svg-marker-anchor)
- [ภาพ SVG ในโหนด Cytoscape.js: แก้ขนาดและช่องว่าง](https://perfectvector.com/th/blog/cytoscape-svg-node-artwork.md)
- [วัดความยาวพาธ SVG โดยไม่สับสนสเกล](https://perfectvector.com/th/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)
