Как исключить прозрачные области SVG из кликов Konva
Исключите прозрачные области SVG из кликов Konva. Повторите проверку попаданий через кеш, испытайте отверстия и фон и обновите карту после замены источника.
На этой странице
- Отделите изображение от области попаданий
- 1. Сохраните два полных исходника SVG
- 2. Загрузите, кешируйте и записывайте настоящие цели
- 3. Нажмите на сплошную область, отверстие, угол и фон
- 4. Перестройте кеш при изменении источника
- Раздельно проверяйте ошибки чтения пикселей и выбора целей
- Отделяйте восстановление рисунка от подключения событий
- Частые вопросы
- Источники
Чтобы прозрачные области Konva.Image игнорировали клики, дождитесь декодирования исходного изображения, вызовите imageNode.cache(), затем imageNode.drawHitFromCache(0) и перерисуйте слой. После этого альфа-канал кешированной сцены, то есть непрозрачность пикселей, определяет, какие пиксели участвуют в проверке попаданий.
Обычный узел изображения может получать события по всему прямоугольнику, включая пустое отверстие SVG. Учебник Konva Image Events показывает альтернативу по альфа-каналу. Здесь мы проверяем собственный SVG с отверстием над отдельно кликабельным фоном: у отверстия, переставшего перехватывать клики изображения, есть настоящий нижний объект, который их получит.
Это меняет область взаимодействия на canvas. Способ не создаёт из SVG редактируемые узлы Konva.Path и не меняет правила DOM SVG pointer-events.
Отделите изображение от области попаданий
Область попаданий — область, по которой Konva выбирает цель события. Её форма может отличаться от видимого рисунка. В одних приложениях широкая прямоугольная цель намеренна, в других прозрачные отверстия должны позволять выбрать нижний объект.
| Нужное взаимодействие | Отправная точка |
|---|---|
| Выбирать целое изображение из любой точки прямоугольника | Стандартная обработка попаданий Image |
| Пропускать прозрачные пиксели к нижнему узлу | Кешировать изображение и строить область попаданий по альфа-каналу |
| Редактировать отдельные векторные области или назначать им независимые действия | Выбрать отдельные контуры Konva и определить поведение |
| Управлять целями указателя внутри встроенного DOM SVG | Использовать отдельное руководство по SVG pointer-events |
Актуальный Shape API принимает целое alphaThreshold от 0 до 255, по умолчанию 0. Передавайте число, а не функцию обратного вызова. Это отличается от визуального фильтра 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 и дождитесь четырёх сцен. SVG 240 × 180 размещён в координатах (80,40) каждой сцены 420 × 260. Каждый фон — отдельный принимающий события Konva.Rect под изображением. Обработчик клика сцены сообщает event.target.id(): это выбранный узел Konva, а не просто DOM-элемент canvas.
await source.decode() выполняется до создания кеша. Кешируйте после готовности настоящего источника, а не во время загрузки изображения. Node API Konva описывает создание и очистку кеша.
3. Нажмите на сплошную область, отверстие, угол и фон
Проверьте четыре точки под панелями. Это координаты внутри каждой сцены, а не относительно окна браузера.
| Место | Координата сцены | Что проверяется |
|---|---|---|
| Сплошной рисунок | 125,110 | Непрозрачная точка глубоко внутри эмблемы |
| Исходное отверстие | 195,130 | Прозрачная внутренняя область hollow.svg |
| Прозрачный угол | 305,55 | Внутри прямоугольника изображения, но вне закрашенного силуэта |
| Вне изображения | 20,20 | Отдельно кликабельный фон |
Следующая таблица фиксирует проверенный тип цели. Журналы включают префикс панели, например alpha-background.
| Конфигурация | Сплошная область | Исходное отверстие | Прозрачный угол | Вне изображения |
|---|---|---|---|---|
| Стандартное изображение | Изображение | Изображение | Изображение | Фон |
| Кешированная область по альфа-каналу | Изображение | Фон | Фон | Фон |
| Кешированное изображение с непрозрачной белой заливкой | Изображение | Изображение | Изображение | Фон |
| Заменённый источник и перестроенный кеш | Изображение | Изображение | Фон | Фон |

Браузерный тест использовал настоящие клики указателя во всех 16 точках и записывал нативный признак доверенного события, координаты сцены и ID целей. Также проверялись хеши исходных файлов, декодирование, размеры сцены и изображения, публичные результаты пересечений и нативные альфа-образцы из отрисованного canvas каждого узла изображения.
В исходном отверстии альфа узла с отверстием была 0. У контроля с непрозрачной заливкой и заменённым источником там было 255. Отдельный фон страницы не делал само изображение непрозрачным: он принадлежал другому узлу Konva ниже. Точки находятся внутри областей, вдали от сглаженных границ.
Это один тест настольного браузера с простыми собственными SVG и сценами без преобразований. Он не подтверждает мобильное и сенсорное поведение, клавиатурное взаимодействие, матрицу браузеров, прирост производительности или порог для любых мягких краёв. Повторите клики с настоящими размерами отображения и преобразованиями приложения.
4. Перестройте кеш при изменении источника
Четвёртая панель начинается с источника с отверстием и его области по альфа-каналу. Затем очищает кеш, устанавливает уже декодированную замену, снова кеширует и перестраивает область попаданий. После этой последовательности цель в исходном отверстии меняется с фона на изображение.
Кеш — снимок отрисованного состояния. После изменения отрисованных пикселей обновляйте вместе кеш сцены и область попаданий по альфа-каналу. Не считайте, что назначение другого изображения обновило существующую кешированную область. Для асинхронной замены дождитесь её декодирования до выполнения показанной последовательности.
Третья панель выделяет другую причину: artwork.fill('#ffffff') рисует непрозрачный прямоугольник позади изображения как часть того же узла. Область попаданий, созданная из кеша сцены, включает эту краску. Удалите ненужную заливку узла, затем перестройте кеш и область. Белая на вид область исходного файла тоже может быть настоящей непрозрачной краской: проверьте прозрачность до обвинения проверки попаданий.
Раздельно проверяйте ошибки чтения пикселей и выбора целей
| Симптом | Что проверить сначала |
|---|---|
| Отверстие всё ещё записывает изображение | Кеш создан после декодирования, область перестроена, непрозрачная заливка или фон источника не закрывают отверстие |
| Рисунок изменился, а клики следуют старой форме | Очистить и перестроить кеш сцены и область по альфа-каналу после изменения источника |
| Ошибка чтения пикселей или загрязнённого canvas | Источник изображения и заголовки ответа до интерпретации результатов попадания |
| Всё изображение перестало получать события | listening, видимость, порядок слоёв и наличие верхнего узла |
| Отверстие ведёт к сцене, а не нижнему объекту | Под точкой действительно есть нужный принимающий события объект |
| Клики canvas работают, клавиатура — нет | Клавиатурное управление и семантика проектируются отдельно; альфа-пиксели попаданий их не предоставляют |
Образец раздаёт оба SVG с того же источника, что и страницу. Для чтения пикселей изображений другого источника нужен незагрязнённый canvas. Руководство Konva по загрязнённому canvas объясняет использование crossOrigin вместе с сервером, разрешающим запрос CORS. Одно crossOrigin = 'anonymous' не даёт доступа без разрешения сервера. В статье не запускался случай ошибки другого источника.
Изменение CSS pointer-events внешнего canvas не выбирает пиксели внутри Konva.Image, попадающие в его внутренние узлы. Отделяйте цели canvas от целей указателя DOM SVG. Сохраните также оригинал SVG: кешированная карта — пиксельные данные взаимодействия, а не замена векторного документа.
Отделяйте восстановление рисунка от подключения событий
Если приложению нужны лишь размещение изображения и попадания по альфа-каналу, подходящего прозрачного растра может быть достаточно. Если остался только PNG или JPG и нужна также редактируемая геометрия, процесс изображения в вектор PerfectVector поможет восстановить кандидат SVG. Проверьте силуэт и отверстия, затем выберите представление Konva и реализуйте события. Здесь использованы собственные исходные SVG без преобразования PerfectVector.
Векторизация не устанавливает обработчики, не создаёт доступные элементы управления, не выбирает цели кликов и не синхронизирует кеши. Если оригинал SVG уже есть, исправляйте код взаимодействия напрямую. Если внутри файла неожиданно обнаруживается полное прямоугольное изображение, выполните проверку встроенного растра SVG, прежде чем считать содержимое отдельными редактируемыми контурами.
Частые вопросы
Почему прозрачные пиксели изображения Konva получают клики? Стандартный узел Image может использовать прямоугольник для попаданий. После готовности источника кешируйте его, примените drawHitFromCache с подходящим числовым порогом, перерисуйте и проверьте настоящие цели.
Какой аргумент принимает drawHitFromCache? Текущий API принимает необязательный целочисленный порог альфа-канала от 0 до 255, по умолчанию 0. Проверенный пример передаёт 0. Он не передаёт функцию обратного вызова и не использует визуальный фильтр Threshold.
Почему белая заливка снова делает отверстие SVG кликабельным? Непрозрачная заливка узла Image входит в кешированную сцену. Альфа кеша включает эту краску. Удалите ненужную заливку и перестройте кеш сцены и область попаданий.
Делает ли область по альфа-каналу контуры SVG независимо редактируемыми? Нет. Рисунок остаётся одним узлом Image. Если нужны отдельные векторные области, используйте подходящие отдельные узлы Path и явную модель сцены.
Источники
- Konva: Image Events — Стандартные попадания изображения и создание области из кешированного рисунка.
- Konva: Shape API — Текущий числовой alphaThreshold и его диапазон.
- Konva: Node API — Создание и очистка кеша, цели событий и публичные методы узлов.
- Konva: Руководство по загрязнённому canvas — Ограничения источников, CORS и ошибки чтения пикселей.
- Konva: SVG на canvas — Отрисовка целого изображения и отдельно построенные векторные контуры.
Если редактируемой геометрии нет, подготовьте кандидат SVG в PerfectVector, проверьте отверстия, затем отдельно испытайте нужные цели кликов Konva и обновления кеша.
Ещё из блога

Маркеры SVG в Matplotlib scatter: проверьте контур
Используйте простой контур SVG как маркер scatter в Matplotlib. Повторите проверки центра, ориентации, отверстий и размера в реальных экспортах SVG и PDF.

Как добавить водяной знак SVG в готовый PDF через Python
Добавьте водяной знак SVG в готовый PDF с svglib, ReportLab и pypdf. Проверьте прозрачность, обрезку, поворот, сохранение текста и порядок наложения.
