Как сделать зум (масштабирование) изображения в зависимости от положения курсора мыши?

Я хочу реализовать зум по курсу мышки, используя аффинные трансформации. Я написал следующий код: <br/> <br/> <pre><code class="javascript">const screen = document.getElementById("screen");
const world = document.getElementById("world");

const m = new DOMMatrix([1, 0, 0, 1, 0, 0]);
const p = new DOMPoint();

const OnZoom = function(e) {
  const zoom = 1 + (0.02 * Math.sign(e.deltaY));
  const rect = world.getBoundingClientRect();
  
  p.x = e.clientX - rect.x;
  p.y = e.clientY - rect.y;
  
  //m.scaleSelf(zoom, zoom, 1, p1.x, p1.y);
  
  m.translateSelf(p.x, p.y);
  m.scaleSelf(zoom, zoom);
  m.translateSelf(-p.x, -p.y);
  
  world.style.transform = m.toString();
};

screen.addEventListener("mousewheel", OnZoom);</code></pre> <br/> <br/> Код работает, но если курсор находиться в правом нижнем углу, зум начинает вести себя неправильно. У блока с <code>id="world"</code> в CSS указано свойство <code>transform-origin: 0 0;</code>. Что именно я упускаю в этой реализации? <br/> <br/> Вот ссылка на <a href="https://codepen.io/eheeiurh-the-flexboxer/pen/BaNjwz" rel="nofollow">CodePen</a> и на <a href="https://jsfiddle.net/ejhntk8b/" rel="nofollow">JSFiddle</a>.
  • 16 октября 2024 г. 13:33
Ответы на вопрос 1
Ваша реализация зума по курсу мыши использует аффинные преобразования, и в целом она выглядит почти правильно. Однако, поведение зума в углах экрана может быть проблематичным из-за того, как осуществляется трансформация.

Когда вы используете `translateSelf` для центрирования точки зума, стоит учитывать, что вы изменяете систему координат, и, как следствие, это может привести к нежелательному смещению объекта при больших масштабах или при нахождении курсора в пределах блока. 

Вот улучшенная версия вашего кода, которая должна исправить проблемы с масштабированием:

```javascript
const screen = document.getElementById("screen");
const world = document.getElementById("world");

const m = new DOMMatrix([1, 0, 0, 1, 0, 0]);
const p = new DOMPoint();

const OnZoom = function(e) {
  e.preventDefault(); // предотвратить стандартное поведение прокрутки
  const zoom = 1 + (0.02 * Math.sign(e.deltaY));
  const rect = world.getBoundingClientRect();

  // Изменяем координаты курсора относительно блока (world)
  p.x = e.clientX - rect.left;
  p.y = e.clientY - rect.top;

  // Модернизируем матрицу трансформаций
  const scaleX = m.a * zoom;
  const scaleY = m.d * zoom;

  m.translateSelf(p.x, p.y); // Перемещаем к курсору
  m.scaleSelf(zoom, zoom); // Масштабируем
  m.translateSelf(-p.x, -p.y); // Возвращаем обратно

  world.style.transform = m.toString();
};

screen.addEventListener("wheel", OnZoom); // Используем 'wheel' вместо 'mousewheel'
```

### Вот некоторые пояснения по улучшенной версии:

1. **Использование `e.preventDefault()`**: Это предотвратит прокрутку страницы при использовании зума.

2. **Использование `wheel` события**: Событие `wheel` является более современным и предпочтительным для работы с прокруткой.

3. **Корректное изменение позиции**: Мы перемещаем центр зума к координатам курсора, а затем применяем масштабирование. Этот порядок действий позволяет уменьшить сдвиг объекта относительно курсора, даже если он находится в углу.

4. **Изменение `m.a` и `m.d` для расчета при масштабировании**: Это изменение обеспечивает более адекватное масштабирование на основе текущих значений.

Попробуйте использовать эту версию и проверьте, будет ли она работать лучше при зуме в различных частях изображения. 
Похожие вопросы