PerfectVector
Par Irene Kim

Ignorer les zones SVG transparentes aux clics dans Konva

Fais ignorer les clics aux zones transparentes d’une image SVG dans Konva. Reproduis la détection par cache, teste trous et fonds et reconstruis les zones après un changement de source.

14 min de lecture
Sur cette page

Pour que les zones transparentes d’une Konva.Image ignorent les clics, attends le décodage de son image source, appelle imageNode.cache(), puis imageNode.drawHitFromCache(0) et redessine le calque. L’alpha de la scène en cache, c’est-à-dire l’opacité des pixels, détermine alors quels pixels de l’image participent à la détection des clics.

Un nœud d’image normal peut recevoir des événements dans tout son rectangle, y compris dans un trou SVG vide. Le tutoriel Image Events de Konva montre l’alternative fondée sur l’alpha. Cet article teste un SVG creux original au-dessus d’un fond cliquable distinct : un trou qui cesse de capter les clics d’image a donc une véritable cible sous-jacente pour les recevoir.

Cela change la zone d’interaction du canevas. Cela ne crée pas de nœuds Konva.Path modifiables à partir du SVG et ne change pas les règles pointer-events du SVG dans le DOM.

Sépare l’image de sa zone de détection

Une zone de détection est la surface utilisée par Konva pour choisir la cible d’un événement. Sa forme peut différer de l’illustration visible. Certaines applications souhaitent volontairement une large cible rectangulaire ; d’autres veulent que les trous transparents permettent de sélectionner un objet inférieur.

Interaction souhaitéePoint de départ
Sélectionner toute l’image depuis n’importe où dans son rectangleComportement de détection Image par défaut
Laisser les pixels transparents atteindre un nœud sous-jacentMettre l’image en cache et construire sa zone de détection à partir de l’alpha
Modifier des régions vectorielles individuelles ou leur attribuer des actions indépendantesChoisir des chemins Konva distincts et définir leur comportement
Contrôler les cibles du pointeur dans un SVG DOM en ligneUtiliser le guide distinct de pointer-events SVG

L’API Shape actuelle prend un entier alphaThreshold de 0 à 255 ; sa valeur par défaut est 0. Passe un nombre ici, pas une fonction de rappel. C’est distinct du filtre visuel Threshold. L’exemple utilise 0 avec des intérieurs entièrement opaques et des trous entièrement transparents ; il n’établit pas de seuil préféré pour les ombres douces ou les bords anticrénelés.

1. Enregistre deux entrées SVG complètes

Crée un dossier vide. Enregistre cet emblème creux original sous 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>

Le sous-chemin intérieur crée un trou avec la règle de remplissage pair-impair. Enregistre la même forme extérieure sans ce sous-chemin intérieur sous 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>

Le second fichier est le témoin de changement de source. Il peint l’emplacement auparavant vide. Aucun fichier ne contient de rectangle blanc de fond ni de bitmap incorporé.

2. Charge, mets en cache et consigne les véritables cibles

L’exemple utilise Konva 10.9.0. Enregistre ces trois fichiers à côté des deux SVG. Le HTML charge cette version de navigateur figée ; pour un projet contrôlé, tu peux plutôt servir la même version localement.

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

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

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

Sers ce dossier en HTTP au lieu d’ouvrir le HTML comme fichier local :

python -m http.server 8000

Ouvre http://localhost:8000/reader.html et attends l’apparition des quatre scènes. Le SVG de 240 × 180 est placé aux coordonnées de scène (80,40) dans chaque scène de 420 × 260. Chaque fond est son propre Konva.Rect à l’écoute des événements, sous l’image. L’écouteur de clic de la scène rapporte event.target.id(), qui identifie le nœud Konva choisi plutôt que le seul élément DOM canvas.

await source.decode() s’exécute avant la création du cache. Mets en cache lorsque la véritable source est prête, pas pendant que l’image charge encore. L’API Node de Konva documente les méthodes de création et de vidage du cache.

3. Clique sur la zone pleine, le trou, le coin et le fond

Teste les quatre emplacements indiqués sous les panneaux. Ce sont des coordonnées dans chaque scène, pas des coordonnées mesurées depuis la fenêtre du navigateur.

EmplacementCoordonnée de scèneCe qui est testé
Illustration pleine125,110Un point opaque bien à l’intérieur de l’emblème
Trou d’origine195,130Un intérieur transparent dans hollow.svg
Coin transparent305,55À l’intérieur du rectangle d’image, hors de sa silhouette peinte
Hors de l’image20,20Le fond cliquable distinct

Le tableau suivant consigne le type de cible testé. Les journaux incluent le préfixe de chaque panneau, comme alpha-background.

ConfigurationZone pleineTrou d’origineCoin transparentHors de l’image
Image par défautImageImageImageFond
Zone de détection alpha en cacheImageFondFondFond
Image en cache avec fond blanc opaqueImageImageImageFond
Source remplacée et cache reconstruitImageImageFondFond
Véritable comparaison Konva dans le navigateur avec détection rectangulaire par défaut, détection alpha en cache, fond d’image opaque et source remplacée, incluant les identifiants des cibles de clic enregistrés
Véritables scènes Konva 10.9.0 avec illustration SVG originale et cibles de clic de bureau enregistrées. L’image creuse fondée sur l’alpha laisse les clics du trou et du coin atteindre son fond distinct. Un fond d’image opaque et une source de remplacement peinte changent ces résultats.

Le test navigateur a utilisé de véritables clics de pointeur aux 16 emplacements et consigné l’état natif de confiance de l’événement, les coordonnées de scène et les identifiants de cible. Il a aussi vérifié les empreintes des fichiers source, le décodage des images, les dimensions des scènes et images, les résultats publics d’intersection et les échantillons alpha natifs du canevas rendu de chaque nœud d’image.

Au trou d’origine, l’alpha du nœud d’image creuse était de 0. Les témoins de fond opaque et de source remplacée avaient un alpha de 255 à cet endroit. Le fond de page distinct ne rendait pas l’image creuse elle-même opaque : il appartenait à un autre nœud Konva situé dessous. Ce sont des échantillons intérieurs, éloignés des limites anticrénelées.

Il s’agit d’un test dans un seul navigateur de bureau, avec des SVG originaux simples et des scènes non transformées. Il ne prouve ni comportement mobile ou tactile, ni interaction clavier, ni matrice multinavigateur, ni gains de performances, ni seuil adapté à toutes les images à bords doux. Répète le test de clic à la véritable taille affichée et avec les transformations de ton application.

4. Reconstruis le cache quand la source change

Le quatrième panneau commence avec la source creuse et sa zone de détection alpha. Il vide ensuite le cache, installe l’image de remplacement déjà décodée, remet en cache et reconstruit la zone de détection. L’emplacement du trou d’origine passe du fond à l’image après cette séquence.

Le cache est un instantané rendu. Après un changement qui affecte les pixels rendus, actualise ensemble la scène en cache et sa zone de détection fondée sur l’alpha. Ne suppose pas que l’attribution d’une autre image a mis à jour une zone de détection déjà en cache. Pour un remplacement chargé de manière asynchrone, attends son décodage avant la séquence de l’exemple.

Le troisième panneau isole une autre cause : artwork.fill('#ffffff') peint un rectangle opaque derrière l’image comme partie de ce même nœud d’image. Construire une zone de détection à partir de cette scène en cache inclut la zone peinte. Supprime un fond de nœud d’image indésirable, puis reconstruis le cache et la zone de détection. Une région d’apparence blanche dans le fichier source peut aussi être une véritable peinture opaque ; inspecte la transparence avant d’accuser la détection.

Diagnostique séparément les erreurs de lecture et de ciblage

SymptômePremier contrôle
Le trou consigne encore l’imageConfirme que la mise en cache suit le décodage, que la zone de détection a été reconstruite et qu’aucun fond opaque du nœud ou de la source ne couvre le trou
L’illustration a changé, mais les clics suivent encore une ancienne formeVide et reconstruis le cache de scène et la zone de détection alpha après le changement de source
Une erreur de lecture de pixels ou de canevas contaminé apparaîtVérifie l’origine de l’image et les en-têtes de réponse avant d’interpréter un résultat de détection
Toute l’image cesse de recevoir des événementsVérifie listening, la visibilité, l’ordre des calques et la présence d’un autre nœud au-dessus
Un trou atteint la scène au lieu d’un objet inférieurConfirme qu’un objet prévu à l’écoute existe réellement sous ce point
Les clics de canevas fonctionnent, mais pas le clavierConçois les commandes et la sémantique clavier séparément ; les pixels de détection alpha ne les fournissent pas

Cet exemple sert les deux SVG depuis la même origine que la page. Les images d’autres origines demandent un canevas non contaminé pour lire les pixels. Le guide de canevas contaminé de Konva explique l’utilisation de crossOrigin avec un serveur qui autorise la requête CORS. Définir seulement crossOrigin = 'anonymous' ne donne aucun accès si le serveur ne l’autorise pas. Cet article n’a pas exécuté de cas d’échec entre origines.

Changer pointer-events en CSS sur le canevas extérieur ne choisit pas quels pixels d’une Konva.Image atteignent ses nœuds internes. Sépare le ciblage du canevas du ciblage de pointeur SVG DOM. Garde aussi le maître SVG : une carte de détection en cache est une donnée d’interaction fondée sur des pixels, pas un document vectoriel de remplacement.

Sépare la récupération de l’illustration et les événements

Si ton application a seulement besoin de placer une image et d’utiliser une détection alpha, une image matricielle transparente adaptée peut déjà suffire. S’il ne reste qu’un PNG ou JPG et que l’application a aussi besoin de chemins modifiables, le processus image vers vecteur de PerfectVector peut aider à récupérer un SVG candidat. Inspecte la silhouette et les trous, puis choisis la représentation Konva et implémente ses événements. Cet exemple utilisait des SVG natifs originaux et n’a effectué aucune conversion PerfectVector.

La vectorisation n’installe pas d’écouteurs, ne construit pas de commandes accessibles, ne choisit pas les cibles de clic et ne synchronise pas les caches. Si un maître SVG existe déjà, corrige directement le code d’interaction. Pour un fichier contenant de manière inattendue une image rectangulaire complète, utilise le contrôle de contenu matriciel incorporé au SVG avant de supposer qu’il contient des chemins modifiables distincts.

Questions fréquentes

Pourquoi les pixels transparents de mon image Konva reçoivent-ils des clics ? Un nœud Image par défaut peut utiliser sa zone rectangulaire pour la détection. Lorsque la source est prête, mets-la en cache et utilise drawHitFromCache avec un seuil alpha numérique adapté, puis redessine et teste les véritables cibles.

Quel argument prend drawHitFromCache ? L’API actuelle accepte un seuil alpha entier facultatif de 0 à 255, par défaut 0. L’exemple testé passe 0. Il ne passe pas de fonction de rappel et n’utilise pas le filtre visuel Threshold.

Pourquoi un fond blanc rend-il mon trou SVG à nouveau cliquable ? Un fond opaque sur le nœud Image devient partie de sa scène en cache. L’alpha en cache inclut alors cette zone peinte. Supprime le fond indésirable et reconstruis le cache de scène et la zone de détection.

Une zone de détection alpha rend-elle les chemins SVG modifiables indépendamment ? Non. L’illustration reste un seul nœud Image. Utilise des nœuds Path distincts adaptés et un modèle de scène explicite si ton application demande des commandes par région vectorielle.

Sources

  1. Konva : Événements d’image — comportement de détection par défaut et génération d’une zone à partir de l’illustration en cache.
  2. Konva : API Shape — paramètre numérique alphaThreshold actuel et plage de valeurs.
  3. Konva : API Node — création et vidage du cache, cibles d’événement et méthodes publiques des nœuds.
  4. Konva : Guide du canevas contaminé — restrictions d’origine, CORS et échecs de lecture de pixels.
  5. Konva : SVG sur canvas — rendu de l’image entière ou chemins vectoriels construits séparément.

Lorsqu’il manque une géométrie modifiable, prépare un SVG candidat avec PerfectVector, inspecte ses trous, puis teste séparément les cibles de clic Konva prévues et les mises à jour du cache.

Plus depuis le blog

PerfectVector

Repartez d’un SVG plus net
et plus facile à modifier

Sans carte bancaire