Как перевести расстояния перемещения мыши в координатное пространство SVG? - PullRequest
2 голосов
/ 08 апреля 2019

У меня есть SVG-визуализация распределения ключевых слов цвета CSS4 в пространстве HSL здесь: https://meyerweb.com/eric/css/colors/hsl-dist.html

Недавно я добавил масштабирование с помощью колесика мыши и панорамирование с помощью мыши. Я могу преобразовать точку из экранного пространства в координатное пространство SVG, используя matrixTransform, .getScreenCTM() и .inverse() благодаря примеру кода, который я нашел в Интернете, но как мне преобразовать движения мыши во время перетаскивания? Сейчас я просто сдвигаю viewBox координаты на значения X и Y с event, что означает, что при увеличении изображение перетаскивается быстрее, чем движение мыши.

В качестве примера, предположим, что я увеличил изображение и перетаскиваю его на панорамирование, и я дергаю мышь влево и немного вниз. event.movementX возвращает -37 и event.movementY возвращает 6. Как определить, как далеко это соответствует координатам SVG, чтобы координаты viewBox правильно смещались?

( Примечание: Я знаю, что есть библиотеки для такого рода вещей, но я намеренно пишу ванильный код JS, чтобы узнать больше о SVG и JS. пожалуйста, не публикуйте «LOL, просто используйте библиотеку X» и оставьте все как есть. Спасибо!)

Отредактировано, чтобы добавить: Меня попросили отправить код. Публикация всего JS кажется слишком длинной, но эта функция запускает события mousemove:

function dragger(event) {
    var target = document.getElementById('color-wheel');
    var coords = parseViewBox(target);
    coords.x -= event.movementX;
    coords.y -= event.movementY;
    changeViewBox(target,coords);
}

Если нужно больше, тогда просмотрите источник на связанной странице; все JS вверху страницы. Ничто не является внешним, за исключением файла, который просто содержит все значения HSL и названия цветов для визуализации.

Ответы [ 2 ]

6 голосов
/ 08 апреля 2019

Моя рекомендация: не беспокойтесь о свойствах movementX / Y в событии.Просто позаботьтесь о том, где мышь появилась и где она находится сейчас.

(Это дает дополнительное преимущество, заключающееся в том, что вы получаете тот же результат, даже если пропустите некоторые события: возможно, из-за того, что мышь вышла из окна, или, может быть,потому что вы хотите сгруппировать события, чтобы вы запускали код только один раз для каждого кадра анимации.)

Для места, где была запущена мышь, вы измеряете это по событию mousedown.Преобразуйте его в позицию в координатах SVG, используя метод, который вы использовали, с .getScreenCTM().inverse() и .matrixTransform().После этого преобразования вам все равно, где на экране находится этот пункт.Вы заботитесь только о том, где это на картинке.Это точка на рисунке, которую вы всегда будете перемещать, чтобы находиться под мышью.

В событиях mousemove вы используете тот же метод преобразования, чтобы узнать, где в данный момент находится мышь в пределах текущей координаты SVG.система.Затем вы выясните, как далеко это находится от точки (опять же, в координатах SVG), которую вы хотите под мышью.Это сумма, которую вы используете для преобразования графики.Я последовал вашему примеру и выполняю преобразование, сдвигая x и y части viewBox:

function move(e) {
  var targetPoint = svgCoords(event, svg);
  shiftViewBox(anchorPoint.x - targetPoint.x,
               anchorPoint.y - targetPoint.y);
}

Вы также можете переместить изображение с помощью transform нагруппа (<g> элемент) в SVG;просто используйте тот же самый групповой элемент для вызова getScreenCTM(), который конвертируется из координат события clientX / Y.

Полная демонстрация для перетаскивания на панорамирование.Я пропустил весь ваш код рисования и эффект масштабирования.Но масштаб все равно должен работать, потому что единственная позиция, которую вы сохраняете в глобальных значениях, уже преобразована в координаты SVG.

var svg = document.querySelector("svg");
var anchorPoint;

function shiftViewBox(deltaX, deltaY) {
	svg.viewBox.baseVal.x += deltaX;
	svg.viewBox.baseVal.y += deltaY;
}

function svgCoords(event,elem) {
	var ctm = elem.getScreenCTM();
	var pt = svg.createSVGPoint();
    // Note: rest of method could work with another element,
    // if you don't want to listen to drags on the entire svg.
    // But createSVGPoint only exists on <svg> elements.
	pt.x = event.clientX;
	pt.y = event.clientY;
	return pt.matrixTransform(ctm.inverse());
}

svg.addEventListener("mousedown", function(e) {
  anchorPoint = svgCoords(event, svg);
  window.addEventListener("mousemove", move);
  window.addEventListener("mouseup", cancelMove);
});

function cancelMove(e) {
  window.removeEventListener("mousemove", move);
  window.removeEventListener("mouseup", cancelMove);
  anchorPoint = undefined;
}

function move(e) {
  var targetPoint = svgCoords(event, svg);
  shiftViewBox(anchorPoint.x - targetPoint.x,
               anchorPoint.y - targetPoint.y);
}
body {
  display: grid;
  margin: 0;
  min-height: 100vh;
}

svg {
  margin: auto;
  width: 70vmin;
  height: 70vmin;
  border: thin solid gray;
  cursor: move;
}
<svg viewBox="-40 -40 80 80">
  <polygon fill="skyBlue"
           points="0 -40, 40 0, 0 40 -40 0" />
</svg>
2 голосов
/ 08 апреля 2019

Так что сценарию нужно что-то, чтобы векторы, перемещаемые SVG, были скоординированы с векторами, перемещенными мышью на экране.Несмотря на то, что событие находится на вашей цели, ваш SVG, свойства MouseEvent относятся только к вашему экрану.

Свойство MoveX только для чтения интерфейса MouseEvent обеспечивает разницу в координате X указателя мыши.между данным событием и предыдущим событием перемещения мыши.Другими словами, значение свойства вычисляется следующим образом: currentEvent.movementX = currentEvent.screenX - previousEvent.screenX.

From https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX

ScreenXСвойство только для чтения интерфейса MouseEvent обеспечивает горизонтальную координату (смещение) указателя мыши в глобальных (экранных) координатах.

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

Вначале я думал, что вы сможетеотработайте масштабирование объекта SVG, используя некоторую комбинацию его окна просмотра и его фактической ширины на экране. Естественно, то, что изначально кажется разумным, не является. Этот подход не сработает, если кажется, что он будет чисто случайным.

Но оказывается, что решение по существу заключается в использованиитот же тип кода, который вы использовали при масштабировании, когда вы приближаетесь к движениям мыши.Функции .getScreenCTM() и .inverse() - это именно то, что вам нужно снова.Но вместо того, чтобы пытаться найти единственную точку в SVG для работы, вам нужно выяснить, на что выводится экранное расстояние в SVG, сравнив вместо этого две точки в SVG.

То, что я привожу здесь, не обязательно является наиболее оптимальным решением, но, надеюсь, поможет объяснить и даст вам кое-что для дальнейшей работы ...

function dragger(event) {
    var target = document.getElementById('color-wheel');
    var coords = parseViewBox(target);


    //Get an initial point in the SVG to start measuring from
    var start_pt = target.createSVGPoint();

    start_pt.x = 0;
    start_pt.y = 0;

    var svgcoord = start_pt.matrixTransform(target.getScreenCTM().inverse());


    //Create a point within the same SVG that is equivalent to 
    //the px movement by the pointer
    var comparison_pt = target.createSVGPoint();

    comparison_pt.x = event.movementX;
    comparison_pt.y = event.movementY;

    var svgcoord_plus_movement = comparison_pt.matrixTransform(target.getScreenCTM().inverse());


    //Use the two SVG points created from screen position values to determine 
    //the in-SVG distance to change coordinates
    coords.x -=  (svgcoord_plus_movement.x - svgcoord.x);


    //Repeat the above, but for the Y axis
    coords.y -= (svgcoord_plus_movement.y - svgcoord.y);


    //Deliver the changes to the SVG to update the view
    changeViewBox(target,coords);
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...