Как получить постоянную ширину хода независимо от местоположения? - PullRequest
1 голос
/ 03 февраля 2020

Суть проблемы в том, что когда я перемещаю элемент SVG, ширина линий меняется.

Я работаю в среде браузера и хочу четкие линии (без сглаживания) , так что рендеринг формы установлен в crispEdges. Например, у вас может быть

"d M 0 0 L 10 0 L 10 10 L 0 10 z"

для определения квадрата со стилем

' заполнить: нет; ход: черный; ширина хода: 1 пиксель; рендеринг формы: crispEdges '

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

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

Я пробовал возиться с отношением размера viewBox к размеру области рисования, округляя сумму перевода до целого числа или принимать целое число формы +0,50, но ни одно из подобных решений, похоже, не сработает, поскольку оно сводится к уровню зума в браузере. векторный эффект: не масштабирующий штрих тоже не исправляет это.

Есть ли способ заставить SVG рассматривать все пути как фигуры 1d геометрии c и использовать отдельное перо для всех их? Другими словами, я хочу, чтобы перо рисовало все в «масштабе с масштабированием», а не с масштабом линий после того, как они были нарисованы.

Пожалуйста, только ваниль JS.

Вы можете проверить, что я имею в виду, запустив код ниже. Нажмите на квадрат и перетащите его (без сенсорных устройств). Обратите внимание, что на некоторых уровнях масштабирования браузера он может вести себя хорошо и не делать то, что я описал.

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

Продолжение ... Проблема не в CTM экрана. Я проверил, что обратное действительно является обратным; то есть CTM x CTM ^ {- 1} - это идентификатор для каждого значения, которое я пробовал. А значения, которые предоставляет matrixTransform (getScreenCTM (). Inverse ()), являются линейными по входам (или настолько линейными, насколько позволяют числа с плавающей запятой).

<html>
<body>

<svg xmlns="http://www.w3.org/2000/svg" id="svgtest" 
    style = "border: 1px solid; display: block; margin-left: auto; margin-right: auto; shape-rendering: crispEdges;" 
    width = "400" height = "400" viewBox = "0 0 100 100" >
</svg>

<script>
var theSquare = {
  x : 10,
  y : 10
};

var initialSquare = {
  x : 10,
  y : 10
};

var SideLength = 10;
var initialX = 0;
var initialY = 0;
var draggingSquare = 0;

function pointInRect(p, r) {
  return p.x > r.xLeft && p.x < r.xRight && p.y > r.yTop && p.y < r.yBottom;
}

function mouseToLocal(theEvent) {
  var screenPt = svgArea.createSVGPoint();
  screenPt.x = theEvent.clientX;
  screenPt.y = theEvent.clientY;
  var svgPt = screenPt.matrixTransform(svgArea.getScreenCTM().inverse());
  return {
    x : svgPt.x,
    y : svgPt.y
  };
}
  
function doMouseDown(event) {
  var localCoord = mouseToLocal(event);
  initialX = localCoord.x;
  initialY = localCoord.y;

  var pt =  {x: initialX, y: initialY};
  var rect = {xLeft: theSquare.x, xRight: theSquare.x + SideLength,
              yTop: theSquare.y, yBottom: theSquare.y + SideLength};
  if (pointInRect(pt,rect))
    {
      draggingSquare = 1;
      initialSquare.x = theSquare.x;
      initialSquare.y = theSquare.y;
    }
  else
    draggingSquare = 0;
}

function doMouseUp(event) {
  draggingSquare = 0;
}

function doMouseMove(event) {
  if (draggingSquare == 0) 
    return;
    
  var localCoord = mouseToLocal(event);
  zeroTranslate.setTranslate(initialSquare.x + localCoord.x - initialX,
    +initialSquare.y + localCoord.y - initialY);
  
  theSquare.x = initialSquare.x + localCoord.x - initialX;
  theSquare.y = initialSquare.y + localCoord.y - initialY;
}

var svgArea = document.getElementById('svgtest');

var theSVGSquare = document.createElementNS("http://www.w3.org/2000/svg", 'path' ); 
theSVGSquare.setAttributeNS(null, "d", "M 0 0" +
  " L " + SideLength +  " 0" + 
  " L " + SideLength + " " + SideLength +
  " L 0 " +SideLength +
  " z");
theSVGSquare.setAttributeNS(null, 'style', 'fill: none; stroke: black; stroke-width: 0.5px; shape-rendering: crispEdges' );

var tforms = theSVGSquare.transform;
var zeroTranslate = svgArea.createSVGTransform();
zeroTranslate.setTranslate(initialSquare.x,initialSquare.y);
theSVGSquare.transform.baseVal.insertItemBefore(zeroTranslate,0);

svgArea.appendChild(theSVGSquare);

svgArea.addEventListener("mousedown",doMouseDown,false);
svgArea.addEventListener("mouseup",doMouseUp,false); 
svgArea.addEventListener("mousemove",doMouseMove,false);
</script>
</body>
</html>

1 Ответ

0 голосов
/ 06 февраля 2020

Я публикую этот «ответ» как способ официально закрыть вопрос и поблагодарить Роберта Лонгсона за его внимание.

Короче говоря, то, что я хочу сделать, не может быть сделано в браузер, что удивительно, так как все, что я хочу сделать, это нарисовать линию. Использование тега HTML canvas создает размытое изображение из-за сглаживания, а SVG создает дрожащие линии. Это сводится к тому, что браузер не предоставляет информацию, необходимую для работы с точностью до пикселя устройства.

Я разместил обсуждение этой проблемы с примерами на моем личном веб-сайте:

Рисование на холсте размыто и SVG дрожит

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