SVG.js предоставляет мощные возможности для обработки событий,
включая поддержку сенсорных взаимодействий на мобильных устройствах.
События касания (touchstart, touchmove,
touchend, touchcancel) позволяют создавать
интерактивные графические элементы, реагирующие на прикосновения.
В SVG.js события касания обрабатываются через метод
.on(), аналогично стандартным DOM-событиям. Пример базовой
привязки события:
const draw = SVG().addTo('body').size(300, 300);
const circle = draw.circle(100).fill('#f06');
circle.on('touchstart', function(event) {
this.fill({ color: '#0f6' });
});
В этом примере при касании круга цвет изменяется. Объект
event содержит стандартные свойства TouchEvent, включая
touches, targetTouches и
changedTouches, что позволяет точно определять координаты и
идентификаторы касаний.
touchstart — возникает при начале
касания.touchmove — срабатывает при
перемещении пальца по элементу.touchend — фиксируется при отпускании
пальца.touchcancel — вызывается, если касание
прерывается системой (например, при входящем звонке или выходе за
пределы экрана).Координаты касания можно получить через объект
event.touches. Для первого пальца это выглядит так:
circle.on('touchmove', function(event) {
const touch = event.touches[0];
const x = touch.clientX;
const y = touch.clientY;
this.center(x, y);
});
Метод center(x, y) позволяет перемещать SVG-элемент к
позиции касания, создавая эффект перетаскивания.
Особенности:
clientX и clientY дают координаты
относительно окна браузера.point(x, y) объекта SVG, чтобы преобразовать
координаты в локальные для SVG-контейнера.const point = draw.point(touch.clientX, touch.clientY);
this.center(point.x, point.y);
SVG.js позволяет отслеживать несколько касаний одновременно, что полезно для жестов масштабирования и вращения:
circle.on('touchmove', function(event) {
for (let i = 0; i < event.touches.length; i++) {
const touch = event.touches[i];
console.log(`Касание ${i}: x=${touch.clientX}, y=${touch.clientY}`);
}
});
Каждое касание имеет уникальный identifier, позволяющий
различать пальцы при сложных жестах.
Для реализации pinch-to-zoom и rotation gestures используется
комбинация событий touchstart, touchmove и
touchend. Пример вычисления масштаба при двух касаниях:
let initialDistance = null;
circle.on('touchstart', function(event) {
if (event.touches.length === 2) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
initialDistance = Math.sqrt(dx*dx + dy*dy);
}
});
circle.on('touchmove', function(event) {
if (event.touches.length === 2 && initialDistance) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
const newDistance = Math.sqrt(dx*dx + dy*dy);
const scale = newDistance / initialDistance;
this.scale(scale);
}
});
На мобильных устройствах браузер по умолчанию может интерпретировать
касания как прокрутку. Для предотвращения этого используется
event.preventDefault():
circle.on('touchmove', function(event) {
event.preventDefault();
const touch = event.touches[0];
const point = draw.point(touch.clientX, touch.clientY);
this.center(point.x, point.y);
});
Это гарантирует, что движение пальца изменяет только SVG-элемент, а не страницу.
Для кроссплатформенной совместимости часто комбинируют события
mousedown/mousemove/mouseup с
touchstart/touchmove/touchend:
function startDrag(event) {
const e = event.touches ? event.touches[0] : event;
this.data('offset', { x: e.clientX - this.cx(), y: e.clientY - this.cy() });
}
function moveDrag(event) {
event.preventDefault();
const e = event.touches ? event.touches[0] : event;
const offset = this.data('offset');
this.center(e.clientX - offset.x, e.clientY - offset.y);
}
circle.on('mousedown touchstart', startDrag)
.on('mousemove touchmove', moveDrag);
Такой подход обеспечивает одинаковое поведение для настольных и мобильных устройств, сохраняя удобство работы с интерактивными SVG-элементами.
touchmove — при быстром
движении пальца события срабатывают часто, что может снижать FPS.
Использование throttle или requestAnimationFrame повышает
производительность.cx,
cy, transform), вместо полного пересоздания
элементов.Комбинация касания, перетаскивания и масштабирования:
const rect = draw.rect(150, 100).fill('#09f').move(50, 50);
let initialDistance = null;
let startPos = null;
rect.on('touchstart', function(event) {
if (event.touches.length === 1) {
const touch = event.touches[0];
startPos = { x: touch.clientX, y: touch.clientY };
} else if (event.touches.length === 2) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
initialDistance = Math.sqrt(dx*dx + dy*dy);
}
});
rect.on('touchmove', function(event) {
event.preventDefault();
if (event.touches.length === 1 && startPos) {
const touch = event.touches[0];
const dx = touch.clientX - startPos.x;
const dy = touch.clientY - startPos.y;
this.dmove(dx, dy);
startPos = { x: touch.clientX, y: touch.clientY };
} else if (event.touches.length === 2 && initialDistance) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
const newDistance = Math.sqrt(dx*dx + dy*dy);
const scale = newDistance / initialDistance;
this.scale(scale);
initialDistance = newDistance;
}
});
Этот подход демонстрирует управление элементом с одного пальца для перемещения и двумя пальцами для масштабирования, полностью реализуя стандартное сенсорное взаимодействие на мобильных устройствах.