PerfectVector
Autor: Irene Kim

Pomijaj przezroczyste obszary SVG przy trafieniach Konva

Spraw, by przezroczyste obszary obrazu SVG nie przechwytywały kliknięć w Konva. Sprawdź pamięć podręczną, otwory, tła i przebudowę trafień po zmianie źródła.

11 min czytania
Na tej stronie

Aby przezroczyste obszary Konva.Image nie przechwytywały kliknięć, poczekaj na zdekodowanie obrazu źródłowego, wywołaj imageNode.cache(), następnie imageNode.drawHitFromCache(0) i przerysuj warstwę. Alfa buforowanej sceny, czyli nieprzezroczystość pikseli, określa wtedy, które piksele obrazu uczestniczą w wykrywaniu trafień.

Zwykły węzeł obrazu może odbierać zdarzenia na całym prostokącie, także w pustym otworze SVG. Samouczek Image Events Konva pokazuje alternatywę opartą na alfie. Artykuł testuje autorski pusty SVG nad oddzielnie klikalnym tłem, więc otwór nieprzechwytujący kliknięć obrazu ma rzeczywisty cel pod spodem.

Zmienia to obszar interakcji canvas. Nie tworzy edytowalnych węzłów Konva.Path z SVG ani nie zmienia reguł pointer-events SVG w DOM.

Oddziel obraz od jego obszaru trafienia

Obszar trafienia to obszar używany przez Konva do wyboru celu zdarzenia. Jego kształt może różnić się od widocznej grafiki. W niektórych aplikacjach szeroki prostokątny cel jest zamierzony; w innych przezroczyste otwory mają pozwalać wybrać dolny obiekt.

Zamierzona interakcjaPunkt wyjścia
Wybór całego obrazu z dowolnego punktu prostokątaDomyślne trafienia Image
Przepuszczanie przezroczystych pikseli do dolnego węzłaBuforuj obraz i zbuduj obszar trafienia z alfy
Edycja lub niezależne akcje poszczególnych obszarów wektorowychWybierz oddzielne ścieżki Konva i określ zachowanie
Sterowanie celami wskaźnika wewnątrz SVG inline w DOMUżyj oddzielnego poradnika SVG pointer-events

Aktualne API Shape przyjmuje całkowity alphaThreshold od 0 do 255; domyślnie 0. Podaj tutaj liczbę, nie callback. Jest to niezależne od wizualnego filtra Threshold. Przykład używa 0 z całkowicie nieprzezroczystymi wnętrzami i przezroczystymi otworami; nie ustala preferowanego progu dla miękkich cieni lub wygładzonych krawędzi.

1. Zapisz dwa kompletne wejściowe SVG

Utwórz pusty folder. Zapisz ten autorski pusty emblemat jako 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>

Wewnętrzna podścieżka tworzy otwór przy regule even-odd. Zapisz ten sam zewnętrzny kształt bez wewnętrznej podścieżki jako 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>

Drugi plik jest wariantem kontrolnym zmiany źródła. Maluje miejsce wcześniej puste. Żaden plik nie zawiera białego prostokąta tła ani osadzonej bitmapy.

2. Wczytaj, buforuj i zapisuj rzeczywiste cele

Przykład używa Konva 10.9.0. Zapisz trzy pliki obok obu SVG. HTML wczytuje przypięte wydanie przeglądarkowe; w kontrolowanym projekcie możesz udostępniać ten sam pakiet lokalnie.

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

Zapisz 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; }

Zapisz 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}`;

Udostępnij folder przez HTTP zamiast otwierać HTML jako lokalny plik:

python -m http.server 8000

Otwórz http://localhost:8000/reader.html i poczekaj na cztery sceny. SVG 240 × 180 jest umieszczony w pozycji (80,40) każdej sceny 420 × 260. Każde tło jest własnym nasłuchującym Konva.Rect pod obrazem. Nasłuch kliknięć sceny zgłasza event.target.id(), identyfikujący wybrany węzeł Konva, a nie sam element DOM canvas.

await source.decode() wykonuje się przed utworzeniem bufora. Buforuj po przygotowaniu rzeczywistego źródła, a nie podczas wczytywania obrazu. API Node Konva dokumentuje metody tworzenia i czyszczenia bufora.

3. Kliknij pełny obszar, otwór, narożnik i tło

Przetestuj cztery miejsca podane pod panelami. To współrzędne wewnątrz każdej sceny, a nie mierzone od okna przeglądarki.

MiejsceWspółrzędna scenyCo sprawdza
Pełna grafika125,110Nieprzezroczysty punkt wewnątrz emblematu
Oryginalny otwór195,130Przezroczyste wnętrze hollow.svg
Przezroczysty narożnik305,55Wewnątrz prostokąta obrazu, poza namalowaną sylwetką
Poza obrazem20,20Oddzielnie klikalne tło

Poniższa tabela zapisuje sprawdzony typ celu. Logi zawierają prefiks panelu, na przykład alpha-background.

KonfiguracjaPełny obszarOryginalny otwórPrzezroczysty narożnikPoza obrazem
Domyślny obrazObrazObrazObrazTło
Buforowany obszar trafienia z alfyObrazTłoTłoTło
Buforowany obraz z nieprzezroczystym białym wypełnieniemObrazObrazObrazTło
Zmienione źródło i przebudowany buforObrazObrazTłoTło
Rzeczywiste porównanie Konva w przeglądarce z domyślnymi trafieniami prostokątnymi, buforowanymi trafieniami alfy, nieprzezroczystym wypełnieniem i zmienionym źródłem oraz zapisanymi ID celów kliknięć
Rzeczywiste sceny Konva 10.9.0 z autorską grafiką SVG i zapisanymi celami kliknięć na komputerze. Pusty obraz oparty na alfie przepuszcza kliknięcia otworu i narożnika do oddzielnego tła. Nieprzezroczyste wypełnienie i zamalowane źródło zastępcze zmieniają wyniki.

Test przeglądarkowy użył rzeczywistych kliknięć wskaźnika we wszystkich 16 miejscach i zapisał zaufanie natywnego zdarzenia, współrzędne sceny i ID celów. Sprawdził też hasze źródeł, dekodowanie obrazów, wymiary scen i obrazów, publiczne wyniki przecięcia oraz natywne próbki alfy z renderowanego canvas każdego węzła obrazu.

W oryginalnym otworze alfa pustego węzła obrazu wynosiła 0. Warianty z pełnym wypełnieniem i zmienionym źródłem miały tam alfę 255. Oddzielne tło strony nie uczyniło samego pustego obrazu nieprzezroczystym: należało do innego węzła Konva pod spodem. To próbki wnętrz, z dala od wygładzonych granic.

Jest to jeden test przeglądarki komputerowej z prostymi autorskimi SVG i nieprzekształconymi scenami. Nie potwierdza działania dotyku, klawiatury, wielu przeglądarek, korzyści wydajnościowych ani progu dobrego dla każdego obrazu z miękkimi krawędziami. Powtórz test kliknięć przy rzeczywistym rozmiarze i transformacjach aplikacji.

4. Przebuduj bufor przy zmianie źródła

Czwarty panel zaczyna od pustego źródła i obszaru trafienia z alfy. Następnie czyści bufor, instaluje już zdekodowany obraz zastępczy, buforuje ponownie i przebudowuje obszar trafienia. Po tej sekwencji miejsce oryginalnego otworu zmienia cel z tła na obraz.

Bufor jest wyrenderowaną migawką. Po zmianie wpływającej na renderowane piksele odśwież razem bufor sceny i obszar trafienia z alfy. Nie zakładaj, że przypisanie innego obrazu zaktualizowało istniejący bufor trafień. Przy asynchronicznym zamienniku poczekaj na jego dekodowanie przed sekwencją z przykładu.

Trzeci panel izoluje inną przyczynę: artwork.fill('#ffffff') maluje nieprzezroczysty prostokąt za obrazem jako część tego samego węzła obrazu. Obszar trafienia z takiego bufora obejmuje namalowany obszar. Usuń niechciane wypełnienie węzła obrazu, a potem przebuduj bufor i obszar trafienia. Biało wyglądający obszar źródła też może być rzeczywistym nieprzezroczystym malowaniem; sprawdź przezroczystość przed obwinieniem wykrywania trafień.

Oddzielnie diagnozuj błędy odczytu i wyboru celu

ObjawCo sprawdzić najpierw
Otwór nadal zapisuje obrazPotwierdź buforowanie po dekodowaniu, przebudowę trafień i brak nieprzezroczystego wypełnienia lub tła źródła w otworze
Grafika się zmieniła, lecz kliknięcia trafiają w stary kształtWyczyść i przebuduj bufor sceny oraz obszar trafienia po zmianie źródła
Występuje błąd odczytu pikseli lub tainted-canvasSprawdź pochodzenie obrazu i nagłówki odpowiedzi przed interpretacją wyników
Cały obraz przestaje odbierać zdarzeniaSprawdź listening, widoczność, kolejność warstw i obecność innego węzła nad nim
Otwór trafia w scenę zamiast dolny obiektPotwierdź istnienie właściwego nasłuchującego obiektu pod punktem
Kliknięcia canvas działają, a klawiatura nieZaprojektuj kontrolki klawiatury i semantykę osobno; piksele trafień alfy ich nie zapewniają

Zestaw udostępnia oba SVG z tego samego pochodzenia co strona. Obrazy obcego pochodzenia wymagają czystego pochodzeniowo canvas do odczytu pikseli. Poradnik tainted-canvas Konva wyjaśnia użycie crossOrigin wraz z serwerem zezwalającym na CORS. Samo crossOrigin = 'anonymous' nie udzieli dostępu bez zgody serwera. Artykuł nie uruchamiał przypadku błędu obcego pochodzenia.

Zmiana CSS pointer-events na zewnętrznym canvas nie wybiera pikseli Konva.Image trafiających w wewnętrzne węzły. Oddziel wybór celu canvas od celów wskaźnika SVG w DOM. Zachowaj też główny SVG: buforowana mapa trafień jest pikselową informacją interakcji, a nie zamiennikiem dokumentu wektorowego.

Oddziel odzyskiwanie grafiki od obsługi zdarzeń

Jeśli aplikacja potrzebuje tylko umieszczania obrazu i trafień według alfy, odpowiedni przezroczysty raster może wystarczyć. Jeśli pozostał PNG lub JPG, a aplikacja potrzebuje też edytowalnych ścieżek, proces obrazu na wektor PerfectVector może pomóc odzyskać kandydata SVG. Sprawdź sylwetkę i otwory, potem wybierz reprezentację Konva i zaimplementuj zdarzenia. Przykład używał autorskich natywnych SVG i nie przeprowadzał konwersji PerfectVector.

Wektoryzacja nie instaluje nasłuchów, nie buduje dostępnych kontrolek, nie wybiera celów kliknięć ani nie synchronizuje buforów. Jeśli istnieje główny SVG, napraw bezpośrednio kod interakcji. Dla pliku niespodziewanie zawierającego pełny prostokątny obraz zastosuj kontrolę osadzonego rastra SVG, zanim uznasz zawartość za oddzielne edytowalne ścieżki.

FAQ

Dlaczego przezroczyste piksele obrazu Konva odbierają kliknięcia? Domyślny węzeł Image może używać prostokątnego obszaru trafień. Po przygotowaniu źródła buforuj go i użyj drawHitFromCache z właściwym liczbowym progiem alfy, a potem przerysuj i przetestuj rzeczywiste cele.

Jaki argument przyjmuje drawHitFromCache? Aktualne API przyjmuje opcjonalny całkowity próg alfy od 0 do 255, domyślnie 0. Przetestowany przykład podaje 0. Nie podaje callbacku i nie używa wizualnego filtra Threshold.

Dlaczego białe wypełnienie znów czyni otwór SVG klikalnym? Nieprzezroczyste wypełnienie węzła Image staje się częścią buforowanej sceny. Alfa bufora obejmuje wtedy namalowany obszar. Usuń niechciane wypełnienie i przebuduj bufor sceny oraz obszar trafienia.

Czy obszar trafienia z alfy pozwala niezależnie edytować ścieżki SVG? Nie. Grafika pozostaje jednym węzłem Image. Gdy potrzebujesz osobnych kontrolek obszarów wektorowych, użyj odpowiednich oddzielnych węzłów Path i jawnego modelu sceny.

Źródła

  1. Konva: Image Events — domyślne trafienia obrazu i generowanie obszaru trafienia z buforowanej grafiki.
  2. Konva: API Shape — aktualny liczbowy parametr alphaThreshold i jego zakres.
  3. Konva: API Node — tworzenie i czyszczenie bufora, cele zdarzeń i publiczne metody węzłów.
  4. Konva: poradnik tainted canvas — ograniczenia pochodzenia, CORS i błędy odczytu pikseli.
  5. Konva: SVG na canvas — renderowanie całego obrazu i oddzielnie konstruowane ścieżki wektorowe.

Jeśli brakuje edytowalnej geometrii, przygotuj kandydata SVG za pomocą PerfectVector, sprawdź otwory, a potem oddzielnie przetestuj cele kliknięć Konva i aktualizacje bufora.

Więcej z bloga

PerfectVector

Zacznij od czystszego SVG,
który łatwiej edytować

Bez karty kredytowej