ให้พื้นที่ SVG โปร่งใสไม่รับคลิกใน Konva
ให้ส่วนโปร่งใสของภาพ SVG ไม่รับคลิกใน Konva ทำซ้ำการตรวจ hit จากแคช ทดสอบช่องและพื้นหลัง และสร้าง hit ใหม่เมื่อเปลี่ยนต้นทาง
ในหน้านี้
หากต้องการให้พื้นที่โปร่งใสของ Konva.Image ไม่รับคลิก ให้รอภาพต้นทางถอดรหัส เรียก imageNode.cache() ตามด้วย imageNode.drawHitFromCache(0) แล้ววาดเลเยอร์ใหม่ ค่าอัลฟาของฉากแคชหรือความทึบพิกเซลจะกำหนดว่าพิกเซลภาพใดร่วมตรวจ hit
โหนดภาพปกติรับเหตุการณ์ได้ทั่วสี่เหลี่ยม รวมถึงช่อง SVG ว่าง บทเรียน Image Events ของ Konva สาธิตทางเลือกตามอัลฟา บทความนี้ทดสอบ SVG กลวงต้นฉบับเหนือพื้นหลังที่คลิกได้แยก เพื่อให้คลิกผ่านช่องที่ไม่รับคลิกภาพไปหาเป้าหมายจริงด้านล่าง
นี่เปลี่ยนพื้นที่โต้ตอบใน canvas ไม่ได้สร้างโหนด Konva.Path ที่แก้ไขได้จาก SVG หรือเปลี่ยนกฎ pointer-events ของ DOM SVG
แยกภาพออกจากพื้นที่รับคลิก
พื้นที่ hit คือบริเวณที่ Konva ใช้เลือกเป้าหมายเหตุการณ์ รูปร่างอาจต่างจากภาพที่เห็น บางแอปตั้งใจใช้เป้าสี่เหลี่ยมกว้าง ส่วนบางแอปต้องให้ช่องใสเลือกวัตถุด้านล่างได้
| การโต้ตอบที่ต้องการ | จุดเริ่มต้น |
|---|---|
| เลือกภาพทั้งชิ้นจากทุกจุดในสี่เหลี่ยม | พฤติกรรม hit เริ่มต้นของ Image |
| ให้พิกเซลโปร่งใสส่งถึงโหนดล่าง | แคชภาพและสร้างพื้นที่ hit จากอัลฟา |
| แก้หรือใส่คำสั่งแยกให้พื้นที่เวกเตอร์ | เลือกพาธ Konva แยก และกำหนดพฤติกรรม |
| ควบคุมเป้าตัวชี้ใน DOM SVG แบบ inline | ใช้คู่มือ SVG pointer-events แยกต่างหาก |
Shape API ปัจจุบัน รับจำนวนเต็ม alphaThreshold ตั้งแต่ 0 ถึง 255 ค่าเริ่มต้น 0 ส่งตัวเลข ไม่ใช่ callback และแยกจากฟิลเตอร์ภาพ Threshold ตัวอย่างใช้ 0 กับภายในทึบเต็มและช่องโปร่งใสเต็ม ไม่ได้กำหนดเกณฑ์แนะนำสำหรับเงานุ่มหรือขอบลดรอยหยัก
1. บันทึก SVG อินพุตครบสองไฟล์
สร้างโฟลเดอร์ว่าง บันทึกตรากลวงต้นฉบับนี้เป็น hollow.svg:
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="180" viewBox="0 0 240 180">
<path fill="#315d6f" fill-rule="evenodd"
d="M20 30 L180 20 L215 60 L195 150 L35 160 L10 110 Z
M80 65 L145 55 L160 110 L95 125 Z"/>
</svg>พาธย่อยภายในทำช่องด้วยกฎเติม even-odd บันทึกรูปนอกเดิมโดยไม่มีพาธย่อยในเป็น filled.svg:
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="180" viewBox="0 0 240 180">
<path fill="#315d6f" d="M20 30 L180 20 L215 60 L195 150 L35 160 L10 110 Z"/>
</svg>ไฟล์ที่สองเป็นชุดควบคุมเปลี่ยนต้นทาง ระบายตำแหน่งที่เคยว่าง ทั้งสองไม่มีสี่เหลี่ยมพื้นหลังขาวหรือบิตแมปฝัง
2. โหลด แคช และบันทึกเป้าหมายจริง
ตัวอย่างใช้ Konva 10.9.0 บันทึกสามไฟล์นี้ข้าง SVG ทั้งสอง HTML โหลดรุ่นเบราว์เซอร์ที่ตรึงไว้ สำหรับโปรเจกต์ควบคุม คุณให้บริการรุ่นเดียวกันจากเครื่องแทนได้
บันทึก reader.html:
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Konva transparent image hit comparison</title>
<link rel="stylesheet" href="reader.css">
<main>
<h1>Transparent artwork: which node gets the click?</h1>
<p class="banner"><strong id="version">Loading Konva…</strong>. Original same-origin SVGs, actual Konva.Image nodes, and a separately clickable background. These are canvas hit pixels, not editable SVG path nodes. Desktop click test only; no mobile or accessibility audit.</p>
<div class="grid">
<section class="panel"><h2>1. Default rectangular hit</h2><div class="stage" id="default"></div><p>The hollow image still catches its transparent center and corner.</p><pre class="log" id="default-log"></pre></section>
<section class="panel"><h2>2. Cached alpha hit region</h2><div class="stage" id="alpha"></div><p>cache() then drawHitFromCache(0): the center and corner reach the background.</p><pre class="log" id="alpha-log"></pre></section>
<section class="panel"><h2>3. Opaque image fill</h2><div class="stage" id="fill"></div><p>A white fill behind this image is part of its cached scene. Its formerly empty pixels receive image hits.</p><pre class="log" id="fill-log"></pre></section>
<section class="panel"><h2>4. Replaced source, rebuilt cache</h2><div class="stage" id="updated"></div><p>The original hole is painted in filled.svg. Clear, replace, cache, and rebuild the hit region.</p><pre class="log" id="updated-log"></pre></section>
</div>
<p>Click these stage coordinates in each panel: solid (125,110), hole (195,130), transparent corner (305,55), and outside the image (20,20). The corner is inside the image viewport but outside its painted shape. Logs report actual click targets.</p>
</main>
<script src="https://unpkg.com/konva@10.9.0/konva.min.js"></script>
<script type="module" src="reader.js"></script>
</html>บันทึก reader.css:
body { margin: 32px; font: 16px/1.55 Arial, sans-serif; color: #183a42; background: #edf2ef; }
main { max-width: 1080px; margin: auto; }
h1 { font-size: 32px; margin-bottom: 12px; }
h2 { margin: 0 0 12px; font-size: 22px; }
p { margin: 10px 0; }
.banner { background: #dce8e4; padding: 18px; border-left: 5px solid #315d6f; }
.grid { display: grid; grid-template-columns: repeat(2, minmax(460px, 1fr)); gap: 20px; margin: 24px 0; }
.panel { background: white; padding: 20px; border: 1px solid #d1dbd5; border-radius: 12px; }
.stage { width: 420px; height: 260px; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; background: #f8faf7; padding: 12px; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; }
th, td { border-bottom: 1px solid #c9d5ce; padding: 10px; text-align: left; }
.log { min-height: 88px; margin-bottom: 0; font-size: 13px; }บันทึก reader.js:
const source = new Image();
source.src = './hollow.svg';
await source.decode();
const replacement = new Image();
replacement.src = './filled.svg';
await replacement.decode();
const clicks = [];
const scenes = [];
function makeScene(id, mode) {
const stage = new Konva.Stage({ container: id, width: 420, height: 260 });
const layer = new Konva.Layer();
stage.add(layer);
const background = new Konva.Rect({
id: `${id}-background`, width: 420, height: 260, fill: '#eadfcd'
});
const artwork = new Konva.Image({
id: `${id}-image`, image: source, x: 80, y: 40, width: 240, height: 180
});
if (mode === 'fill') artwork.fill('#ffffff');
layer.add(background, artwork);
if (mode !== 'default') {
artwork.cache();
artwork.drawHitFromCache(0);
}
layer.draw();
const beforeUpdate = stage.getIntersection({ x: 195, y: 130 }).id();
if (mode === 'update') {
artwork.clearCache();
artwork.image(replacement);
artwork.cache();
artwork.drawHitFromCache(0);
layer.draw();
}
stage.on('click', (event) => {
const point = stage.getPointerPosition();
const record = {
scene: id, x: point.x, y: point.y,
target: event.target.id(), trusted: event.evt.isTrusted
};
clicks.push(record);
document.getElementById(`${id}-log`).textContent +=
`(${Math.round(point.x)}, ${Math.round(point.y)}) → ${record.target}\n`;
document.dispatchEvent(new CustomEvent('fixture-click-recorded'));
});
const scene = { id, stage, artwork, beforeUpdate };
scenes.push(scene);
return scene;
}
makeScene('default', 'default');
makeScene('alpha', 'alpha');
makeScene('fill', 'fill');
makeScene('updated', 'update');
document.getElementById('version').textContent = `Konva ${Konva.version}`;ให้บริการโฟลเดอร์ผ่าน HTTP แทนเปิด HTML เป็นไฟล์ในเครื่อง:
python -m http.server 8000เปิด http://localhost:8000/reader.html และรอสี่ stage ปรากฏ SVG 240 × 180 วางที่พิกัด stage (80,40) ในแต่ละ stage 420 × 260 พื้นหลังแต่ละอันเป็น Konva.Rect ที่รับเหตุการณ์ได้อยู่ใต้ภาพ ตัวรับ click ของ stage รายงาน event.target.id() ซึ่งระบุโหนด Konva ที่เลือก ไม่ใช่เพียงองค์ประกอบ canvas ใน DOM
await source.decode() ทำก่อนสร้างแคช แคชเมื่อแหล่งภาพจริงพร้อม ไม่ใช่ยังโหลดอยู่ Node API ของ Konva อธิบายเมธอดสร้างและล้างแคช
3. คลิกส่วนทึบ ช่อง มุม และพื้นหลัง
ทดสอบสี่ตำแหน่งที่ระบุใต้แผง เป็นพิกัดภายในแต่ละ stage ไม่ใช่พิกัดจากหน้าต่างเบราว์เซอร์
| ตำแหน่ง | พิกัด stage | สิ่งที่ทดสอบ |
|---|---|---|
| ภาพทึบ | 125,110 | จุดทึบด้านในตรา |
| ช่องเดิม | 195,130 | ภายในโปร่งใสของ hollow.svg |
| มุมโปร่งใส | 305,55 | ในสี่เหลี่ยมภาพ แต่นอกรูปเงาที่ระบาย |
| นอกภาพ | 20,20 | พื้นหลังที่คลิกแยกได้ |
ตารางนี้บันทึกชนิดเป้าที่ทดสอบ ล็อกมีคำนำหน้าแผง เช่น alpha-background
| การตั้งค่า | ส่วนทึบ | ช่องเดิม | มุมโปร่งใส | นอกภาพ |
|---|---|---|---|---|
| ภาพเริ่มต้น | ภาพ | ภาพ | ภาพ | พื้นหลัง |
| พื้นที่ hit อัลฟาจากแคช | ภาพ | พื้นหลัง | พื้นหลัง | พื้นหลัง |
| ภาพแคชมีสีเติมขาวทึบ | ภาพ | ภาพ | ภาพ | พื้นหลัง |
| เปลี่ยนต้นทางและสร้างแคชใหม่ | ภาพ | ภาพ | พื้นหลัง | พื้นหลัง |

การทดสอบเบราว์เซอร์ใช้คลิกตัวชี้จริงทั้ง 16 ตำแหน่ง บันทึกสถานะความเชื่อถือเหตุการณ์ พิกัด stage และ ID เป้า ยังตรวจแฮชไฟล์ต้นทาง การถอดรหัสภาพ ขนาด stage และภาพ ผล intersection สาธารณะ และตัวอย่างอัลฟาจาก canvas ที่เรนเดอร์ของแต่ละโหนดภาพ
ที่ช่องเดิม อัลฟาโหนดภาพกลวงเป็น 0 ชุดควบคุมเติมทึบและเปลี่ยนต้นทางมีอัลฟา 255 พื้นหลังหน้าแยกไม่ได้ทำให้ภาพกลวงเองทึบ เพราะเป็นโหนด Konva อีกอันใต้ภาพ ตัวอย่างวัดเป็นจุดภายในห่างขอบลดรอยหยัก
นี่เป็นการทดสอบเบราว์เซอร์เดสก์ท็อปหนึ่งแบบกับ SVG ต้นฉบับเรียบง่ายและ stage ไม่แปลงรูป ไม่ได้ยืนยันมือถือ/สัมผัส คีย์บอร์ด หลายเบราว์เซอร์ ประสิทธิภาพดีขึ้น หรือเกณฑ์สำหรับทุกภาพขอบนุ่ม ทดสอบคลิกซ้ำที่ขนาดแสดงและการแปลงจริงของแอป
4. สร้างแคชใหม่เมื่อเปลี่ยนต้นทาง
แผงที่สี่เริ่มด้วยต้นทางกลวงและพื้นที่ hit อัลฟา จากนั้นล้างแคช ใส่ภาพแทนที่ถอดรหัสแล้ว แคชอีกครั้งและสร้างพื้นที่ hit ใหม่ ตำแหน่งช่องเดิมเปลี่ยนจากพื้นหลังเป็นภาพหลังลำดับนี้
แคชเป็นภาพฉาก ณ ขณะหนึ่ง หลังเปลี่ยนสิ่งที่มีผลต่อพิกเซล ให้รีเฟรชแคชฉากกับพื้นที่ hit อัลฟาพร้อมกัน อย่าคิดว่าใส่อีกภาพแล้วพื้นที่ hit แคชเดิมอัปเดต หากภาพแทนโหลดแบบ asynchronous ให้รอถอดรหัสก่อนทำลำดับตัวอย่าง
แผงที่สามแยกอีกสาเหตุ artwork.fill('#ffffff') ระบายสี่เหลี่ยมทึบหลังภาพเป็นส่วนของโหนดภาพเดียวกัน พื้นที่ hit จากฉากแคชนั้นจึงรวมส่วนระบาย เอาสีเติมโหนดที่ไม่ต้องการออก แล้วสร้างแคชและพื้นที่ hit ใหม่ บริเวณที่ดูขาวในต้นทางอาจเป็นสีทึบจริง ตรวจความโปร่งใสก่อนโทษการตรวจ hit
แยกปัญหาอ่านพิกเซลและเลือกเป้า
| อาการ | สิ่งที่ตรวจก่อน |
|---|---|
| ช่องยังบันทึกว่าเป็นภาพ | ยืนยันแคชหลังถอดรหัส สร้างพื้นที่ hit ใหม่ และไม่มีสีเติมทึบหรือพื้นต้นทางปิดช่อง |
| ภาพเปลี่ยนแต่คลิกยังตามรูปเก่า | ล้างและสร้างแคชฉากกับพื้นที่ hit อัลฟาใหม่หลังเปลี่ยนต้นทาง |
| เกิดข้อผิดพลาดอ่านพิกเซลหรือ tainted-canvas | ตรวจ origin ภาพและ header คำตอบก่อนตีความผล hit |
| ทั้งภาพหยุดรับเหตุการณ์ | ตรวจ listening การมองเห็น ลำดับเลเยอร์ และโหนดที่ทับอยู่ |
| ช่องถึง stage แทนวัตถุข้างล่าง | ยืนยันว่ามีวัตถุรับเหตุการณ์ที่ตั้งใจอยู่ใต้จุดนั้นจริง |
| คลิก canvas ได้แต่คีย์บอร์ดไม่ได้ | ออกแบบการควบคุมคีย์บอร์ดและความหมายแยก พิกเซล hit อัลฟาไม่ได้ให้สิ่งนั้น |
ชุดนี้ให้ SVG ทั้งคู่จาก origin เดียวกับหน้า ภาพข้าม origin ต้องใช้ canvas ที่สะอาดด้าน origin เพื่ออ่านพิกเซล คู่มือ tainted-canvas ของ Konva อธิบายใช้ crossOrigin คู่กับเซิร์ฟเวอร์ที่อนุญาต CORS การตั้ง crossOrigin = 'anonymous' อย่างเดียวให้สิทธิ์ไม่ได้หากเซิร์ฟเวอร์ไม่ยอม บทความไม่ได้รันกรณีข้าม origin ล้มเหลว
การเปลี่ยน CSS pointer-events บน canvas นอกไม่ได้เลือกว่าพิกเซลใดใน Konva.Image จะ hit โหนดภายใน แยกเป้าหมาย canvas จากเป้าตัวชี้ DOM SVG และเก็บไฟล์หลัก SVG เพราะแผนที่ hit แคชเป็นข้อมูลโต้ตอบแบบพิกเซล ไม่ใช่เอกสารเวกเตอร์แทน
แยกการกู้ภาพจากการต่อเหตุการณ์
หากแอปต้องการแค่วางภาพและ hit ตามอัลฟา ราสเตอร์โปร่งใสที่เหมาะสมอาจพอ หากเหลือ PNG หรือ JPG และต้องมีพาธแก้ไขได้ด้วย กระบวนการภาพเป็นเวกเตอร์ของ PerfectVector ช่วยกู้ SVG ตัวเลือกได้ ตรวจรูปเงาและช่อง แล้วเลือกรูปแบบ Konva และทำเหตุการณ์ ตัวอย่างนี้ใช้ SVG เนทีฟต้นฉบับ ไม่ได้แปลง PerfectVector
การแปลงเวกเตอร์ไม่ได้ติด listener สร้างส่วนควบคุมเข้าถึงได้ เลือกเป้าคลิก หรือทำแคชให้ตรงกัน หากมีไฟล์หลัก SVG ให้แก้โค้ดโต้ตอบโดยตรง หากไฟล์มีภาพสี่เหลี่ยมเต็มโดยไม่คาด ให้ใช้การตรวจราสเตอร์ฝังใน SVG ก่อนคิดว่าเป็นพาธแยกที่แก้ได้
คำถามที่พบบ่อย
ทำไมพิกเซลโปร่งใสในภาพ Konva รับคลิก? โหนด Image เริ่มต้นใช้สี่เหลี่ยมเป็นพื้นที่ hit ได้ หลังต้นทางพร้อม ให้แคชและใช้ drawHitFromCache กับเกณฑ์อัลฟาตัวเลขที่เหมาะ วาดใหม่และทดสอบเป้าจริง
drawHitFromCache รับอาร์กิวเมนต์อะไร? API ปัจจุบันรับเกณฑ์อัลฟาจำนวนเต็ม 0 ถึง 255 แบบไม่บังคับ ค่าเริ่มต้น 0 ตัวอย่างส่ง 0 ไม่ส่ง callback หรือใช้ฟิลเตอร์ภาพ Threshold
ทำไมเติมขาวแล้วช่อง SVG กลับรับคลิก? สีเติมทึบบน Image กลายเป็นส่วนฉากแคช อัลฟาแคชจึงรวมพื้นที่นั้น เอาสีเติมที่ไม่ต้องการออกแล้วสร้างแคชฉากและพื้นที่ hit ใหม่
พื้นที่ hit อัลฟาทำให้พาธ SVG แก้แยกได้หรือไม่? ไม่ ภาพยังเป็น Image โหนดเดียว ใช้ Path แยกที่เหมาะสมและโมเดลฉากชัดเมื่อแอปต้องควบคุมแต่ละพื้นที่เวกเตอร์
แหล่งข้อมูล
- Konva: Image Events — hit ภาพเริ่มต้นและการสร้างพื้นที่ hit จากภาพแคช
- Konva: Shape API — พารามิเตอร์ alphaThreshold ตัวเลขปัจจุบันและช่วงค่า
- Konva: Node API — สร้างแคช ล้างแคช เป้าเหตุการณ์ และเมธอดสาธารณะ
- Konva: คู่มือ Tainted canvas — ข้อจำกัด origin, CORS และปัญหาอ่านพิกเซล
- Konva: SVG บน canvas — เรนเดอร์ภาพทั้งชิ้นเทียบกับสร้างพาธเวกเตอร์แยก
หากไม่มีรูปทรงแก้ได้ ให้เตรียม SVG ตัวเลือกด้วย PerfectVector ตรวจช่อง แล้วทดสอบเป้าคลิก Konva และการอัปเดตแคชแยกกัน
เพิ่มเติมจากบล็อก

มาร์กเกอร์ SVG ใน Matplotlib scatter: ตรวจพาธ
ใช้พาธ SVG เรียบง่ายเป็นมาร์กเกอร์ Matplotlib scatter ทำซ้ำการตรวจศูนย์กลาง ทิศทาง ช่อง และขนาดในไฟล์ SVG และ PDF ที่ส่งออกจริง

เพิ่มลายน้ำ SVG ใน PDF เดิมด้วย Python
รวมลายน้ำ SVG ใน PDF เดิมด้วย svglib, ReportLab และ pypdf ตรวจความทึบ หน้าที่ครอป การหมุน ข้อความที่เก็บไว้ และลำดับภาพซ้อน
