Прослушивание события жеста изменения масштабирования на тачпаде в браузере

Тачпады некоторых современных ноутбуков поддерживают разные жесты. Один из таких жестов отвечает за изменение масштабирования. Недавно я заметил, что те же Google-карты успешно используют эти жесты при сёрфинге карт через любой популярный браузер. Я проверил Edge, Chrome, Firefox, Safari.

Жест изменения масштабирования

А карты Apple поддерживают не только жест масштабирования, но и жест вращения. При этом, все элементы страницы остаются на своих местах с неизменными габаритами. То есть, это не какое-нибудь растягивание HTML-странички, как по комбинации клавиш Ctrl++. Это именно прослушивание этих жестов.

Жест масштабирования и вращения на примере карт Apple

Я хотел бы использовать эти жесты в своих приложениях. Например, масштабировать или вращать изображение. Чтобы не оставлять вопрос без кода, накидал демку с большой картинкой, оригинальный размер которой 4000x4000. Если кто знает как отслеживать эти жесты, поделитесь, пожалуйста, хотя бы ссылкой на материал.

document.querySelector('.canvas').onclick = (event) => event.target.classList.toggle('original');

window.onwheel = (event)=>{
  let canvas = document.querySelector('.canvas');
  let rect = canvas.getBoundingClientRect();
  let { wheelDeltaX, wheelDeltaY } = event;
  if(Math.round(rect.left) == window.innerWidth - 50 && wheelDeltaX > 0 ) wheelDeltaX = 0;
  else if(rect.left > window.innerWidth - 50) { wheelDeltaX = - 50 - rect.left + window.innerWidth; }
  if(Math.round(rect.top) == window.innerHeight - 50 && wheelDeltaY > 0 ) wheelDeltaY = 0;
  else if(rect.top > window.innerHeight - 50) { wheelDeltaY = - 50 - rect.top + window.innerHeight; }
  if(Math.round(rect.right) == 50 && wheelDeltaX < 0 ) wheelDeltaX = 0;
  else if(rect.right < 50) { wheelDeltaX = 50 - rect.right; }
  if(Math.round(rect.bottom) == 50 && wheelDeltaY < 0 ) wheelDeltaY = 0;
  else if(rect.bottom < 50) { wheelDeltaY = 50 - rect.bottom; }
  canvas.style.transform = `${getComputedStyle(canvas).transform} translate(${wheelDeltaX}px, ${wheelDeltaY}px)`;
  //console.log(event.wheelDeltaX,event.wheelDeltaY);
};
body {
  min-height: 100vh;
  margin: 0;
}

.canvas {
  position: fixed;
  left: 50vw;
  top: 50vh;
  width: calc(100vw - 4rem);
  height: calc(100vh - 4rem);
  max-width: calc(100vh - 4rem);
  max-height: calc(100vw - 4rem);
  transform: translate(-50%, -50%);
}

.canvas.original{
  transform: translate(0%, 0%);
  left: calc(-2000px + 50vw);
  top: calc(-2000px + 50vh);
  width: 4000px;
  height: 4000px;
  max-width: 4000px;
  max-height: 4000px;
}

img {
  width: 100%;
  height: 100%;
  pointer-events:none;
}

.surface {
  width: 100vw;
  height: 100vh;
  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="gray" stroke="none"><rect x="0" y="0" width="8" height="8"/><rect x="8" y="8" width="8" height="8"/></svg>') 16px/16px repeat;
}

.controls {
  background: cyan;
  position: fixed;
  height: 3rem;
  width: 5rem;
}

.top {
  top: 0;
}

.bottom {
  bottom: 0;
}

.left {
  left: 0;
}

.right {
  right: 0;
}
<div class="surface">
  <div class="canvas">
    <img src="https://images.unsplash.com/photo-1620421680906-275860f61e27?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=4000&q=80" />
  </div>
</div>
<div class="controls top left"></div>
<div class="controls top right"></div>
<div class="controls bottom left"></div>
<div class="controls bottom right"></div>

Немного юмористического сарказма. На второй картинке страница с примерами карт Apple. На картинке виден горизонтальный скролл. Он остается при любом размере окна браузера, но проявляется только в Safari. Что-то подсказывает мне, что никто из web-мастеров Apple по картам не пользуется Safari по причинам скудного набора функционала панели разработчиков. В противном случае они бы давно заметили этот баг.


Ответы (1 шт):

Автор решения: DiD

Все оказалось вон оно как просто. Те события называются gesturestart, gestureend и gesturechange.

var node;
var rotation = 0;
var gestureStartRotation = 0;
var gestureStartScale = 0;
var scale = 1;
var posX = 0;
var posY = 0;
var startX;
var startY;
var node = document.querySelector('.canvas');
var render = () => {
    window.requestAnimationFrame(() => {
        var val = `translate3D(${posX}px, ${posY}px, 0px) rotate(${rotation}deg) scale(${scale})`;
        node.style.transform = val;
    });
};
window.addEventListener('wheel', (e) => {
    e.preventDefault();
    if (e.ctrlKey) {
        scale -= e.deltaY * 0.01;
    }
    else {
        posX -= e.deltaX * 2;
        posY -= e.deltaY * 2;
    }
    render();
});
window.addEventListener("gesturestart", function (e) {
    e.preventDefault();
    startX = e.pageX - posX;
    startY = e.pageY - posY;
    gestureStartRotation = rotation;
    gestureStartScale = scale;
});
window.addEventListener("gesturechange", function (e) {
    e.preventDefault();
    rotation = gestureStartRotation + e.rotation;
    scale = gestureStartScale * e.scale;
    posX = e.pageX - startX;
    posY = e.pageY - startY;
    render();
});
window.addEventListener("gestureend", function (e) {
    e.preventDefault();
 });
body {
  min-height: 100vh;
  margin: 0;
}

.canvas {
  position: fixed;
  left: calc(50vw - 50vmin + 2rem);
  top: calc(50vh - 50vmin + 2rem);
  width: calc(100vw - 4rem);
  height: calc(100vh - 4rem);
  max-width: calc(100vh - 4rem);
  max-height: calc(100vw - 4rem);
/* transform: translate(-50%, -50%);*/
}

.canvas.original{
  transform: translate(0%, 0%);
  left: calc(-2000px + 50vw);
  top: calc(-2000px + 50vh);
  width: 4000px;
  height: 4000px;
  max-width: 4000px;
  max-height: 4000px;
}

img {
  width: 100%;
  height: 100%;
  pointer-events:none;
}

.surface {
  width: 100vw;
  height: 100vh;
  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="gray" stroke="none"><rect x="0" y="0" width="8" height="8"/><rect x="8" y="8" width="8" height="8"/></svg>') 16px/16px repeat;
}

.controls {
  background: cyan;
  position: fixed;
  height: 3rem;
  width: 5rem;
}

.top {
  top: 0;
}

.bottom {
  bottom: 0;
}

.left {
  left: 0;
}

.right {
  right: 0;
}
<div class="surface">
  <div class="canvas">
    <img src="https://images.unsplash.com/photo-1620421680906-275860f61e27?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=4000&q=80" />
  </div>
</div>
<div class="controls top left"></div>
<div class="controls top right"></div>
<div class="controls bottom left"></div>
<div class="controls bottom right"></div>

→ Ссылка