PerfectVector
โดย Irene Kim

ให้พื้นที่ SVG โปร่งใสไม่รับคลิกใน Konva

ให้ส่วนโปร่งใสของภาพ SVG ไม่รับคลิกใน Konva ทำซ้ำการตรวจ hit จากแคช ทดสอบช่องและพื้นหลัง และสร้าง hit ใหม่เมื่อเปลี่ยนต้นทาง

อ่าน 7 นาที
ในหน้านี้

หากต้องการให้พื้นที่โปร่งใสของ 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 อัลฟาจากแคชภาพพื้นหลังพื้นหลังพื้นหลัง
ภาพแคชมีสีเติมขาวทึบภาพภาพภาพพื้นหลัง
เปลี่ยนต้นทางและสร้างแคชใหม่ภาพภาพพื้นหลังพื้นหลัง
การเปรียบเทียบ Konva จริงในเบราว์เซอร์ มี hit สี่เหลี่ยมเริ่มต้น hit อัลฟาจากแคช สีเติมภาพทึบ และต้นทางใหม่ พร้อม ID เป้าคลิกที่บันทึก
stage Konva 10.9.0 จริงกับ SVG ต้นฉบับและเป้าคลิกเดสก์ท็อปที่บันทึก ภาพกลวงแบบอัลฟาส่งคลิกช่องและมุมไปพื้นหลังแยก สีเติมภาพทึบและต้นทางใหม่ที่ระบายเปลี่ยนผลนั้น

การทดสอบเบราว์เซอร์ใช้คลิกตัวชี้จริงทั้ง 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 แยกที่เหมาะสมและโมเดลฉากชัดเมื่อแอปต้องควบคุมแต่ละพื้นที่เวกเตอร์

แหล่งข้อมูล

  1. Konva: Image Events — hit ภาพเริ่มต้นและการสร้างพื้นที่ hit จากภาพแคช
  2. Konva: Shape API — พารามิเตอร์ alphaThreshold ตัวเลขปัจจุบันและช่วงค่า
  3. Konva: Node API — สร้างแคช ล้างแคช เป้าเหตุการณ์ และเมธอดสาธารณะ
  4. Konva: คู่มือ Tainted canvas — ข้อจำกัด origin, CORS และปัญหาอ่านพิกเซล
  5. Konva: SVG บน canvas — เรนเดอร์ภาพทั้งชิ้นเทียบกับสร้างพาธเวกเตอร์แยก

หากไม่มีรูปทรงแก้ได้ ให้เตรียม SVG ตัวเลือกด้วย PerfectVector ตรวจช่อง แล้วทดสอบเป้าคลิก Konva และการอัปเดตแคชแยกกัน

เพิ่มเติมจากบล็อก

PerfectVector

เริ่มต้นด้วย SVG ที่เรียบร้อยขึ้น
และแก้ไขได้ง่ายกว่า

ไม่ต้องใช้บัตรเครดิต