Масштаб браузера ломает компоновку SVG - PullRequest
0 голосов
/ 12 января 2020

(см. Фрагмент кода) Я пытаюсь нарисовать путь от нижней части одного элемента #item-A > div внутри foreignObject #item-A к вершине другого элемента #item-B > div внутри foreignObject #item-B. Высота div (s) является переменной.

Для другого уровня масштабирования браузера значение пути M ${item_A_div_Bottom - svgOffset.y} дает другой результат. Это нарушает макет.


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


window.onload = function() {
  var svgEl = document.querySelector('svg'),
    itemA = document.querySelector('#item-A'),
    itemB = document.querySelector('#item-B'),
    path = document.createElementNS("http://www.w3.org/2000/svg", 'path');

  const svgOffset = svgEl.getBoundingClientRect();
  const {
    a
  } = svgEl.getScreenCTM();

  const {
    x: item_A_BBoxX,
  } = itemA.getBBox();

  const {
    bottom: item_A_div_Bottom
  } = document.querySelector(`#item-A > div`).getBoundingClientRect();

  const {
    y: item_B_BBoxY,
  } = document.querySelector(`#item-B`).getBBox();

  const path_d = `
    M ${item_A_BBoxX},${(item_A_div_Bottom - svgOffset.y) * (1 / a)}
    V ${item_B_BBoxY}
  `;

  path.setAttribute('d', path_d);
  path.setAttribute('stroke', 'red');
  path.setAttribute('stroke-width', '2px');
  svgEl.appendChild(path);
}
#header {
  width: 100%;
  height: 30px;
  background-color: purple;
}

#svg-wrapper {
  display: flex;
  justify-content: center;
  height: 600px;
  width: 100%;
  background-color: grey;
}

svg {
  width: 80%;
  height: 100%;
}

foreignObject {
  background-color: black;
}
<div id="header">Header</div>
<div id="svg-wrapper">
  <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <style>
    div {
      color: white;
      font: 18px serif;
      height: 40px;
      overflow: auto;
      background-color: black;
    }
  </style>

  <foreignObject id="item-A" x="20" y="20" width="60" height="30">
    <div xmlns="http://www.w3.org/1999/xhtml">
      Item-A
    </div>
  </foreignObject>
  
  <foreignObject id="item-B" x="20" y="120" width="60" height="30">
    <div xmlns="http://www.w3.org/1999/xhtml">
      Item-B
    </div>
  </foreignObject>
</svg>
</div>

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

Как сохранить макет от взлома для разных уровней масштабирования?

1 Ответ

0 голосов
/ 14 января 2020

Значение атрибутов, возвращаемых element.getBoundingClientRect(), изменяется с увеличением браузера. Как указано в веб-документах MDN

Метод Element.getBoundingClientRect () возвращает размер элемента и его положение относительно области просмотра. ....

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

Вместо используя element.getBoundingClientRect(), используйте element.clientHeight с атрибутами object.getBBox() для расчета желаемой позиции.

window.onload = function() {
  var svgEl = document.querySelector('svg'),
    itemA = document.querySelector('#item-A'),
    itemB = document.querySelector('#item-B'),
    path = document.createElementNS("http://www.w3.org/2000/svg", 'path');

  const {
    clientTop: item_A_ClientTop,
    clientHeight: item_A_ClientHeight,
  } = itemA;

  const {
    x: item_A_BBoxX,
    y: item_A_BBoxY
  } = itemA.getBBox();

  const {
    clientHeight: item_A_div_ClientHeight
  } = document.querySelector(`#item-A > div`);

  const {
    y: item_B_BBoxY,
  } = document.querySelector(`#item-B`).getBBox();

  const path_d = `
    M ${item_A_BBoxX},${item_A_BBoxY + item_A_div_ClientHeight}
    V ${item_B_BBoxY}
  `;

  path.setAttribute('d', path_d);
  path.setAttribute('stroke', 'red');
  path.setAttribute('stroke-width', '2px');
  svgEl.appendChild(path);
}
#header {
  width: 100%;
  height: 30px;
  background-color: purple;
}

#svg-wrapper {
  display: flex;
  justify-content: center;
  height: 600px;
  width: 100%;
  background-color: grey;
}

svg {
  width: 80%;
  height: 100%;
}

foreignObject {
  background-color: black;
}
<div id="header">Header</div>
<div id="svg-wrapper">
  <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <style>
    div {
      color: white;
      font: 18px serif;
      height: 40px;
      overflow: auto;
      background-color: black;
    }
  </style>

  <foreignObject id="item-A" x="20" y="20" width="60" height="30">
    <div xmlns="http://www.w3.org/1999/xhtml">
      Item-A
    </div>
  </foreignObject>
  
  <foreignObject id="item-B" x="20" y="120" width="60" height="30">
    <div xmlns="http://www.w3.org/1999/xhtml">
      Item-B
    </div>
  </foreignObject>
</svg>
</div>
...