Как создать миникарту SVG с использованием D3 v5 и показать текущие размеры области просмотра? - PullRequest
0 голосов
/ 20 сентября 2018

Я импортирую внешний SVG, используя приведенный ниже код, и я прикрепил к нему поведение zoom and pan, которое работает нормально.

Я также создал тип миникарты, который показывает тот же SVG,но только в уменьшенной версии.

Я хотел бы показать прямоугольник на мини-карте, который представляет текущий видовой экран, отображаемый на экране.Однако у меня возникают проблемы с правильным масштабированием и позиционированием.

JS CODE:

let url = 'https://gist.githubusercontent.com/ivanbacher/11cd328411c74b2bc2ec789291852544/raw/bb39be70f71e4fb52ae5101150f5fffde4b66272/map.svg';


d3.svg(url).then( (xml)=> {

   let width = parseInt( d3.select('body').style('width') );
   let height = parseInt( d3.select('body').style('height') );

   document.querySelector('#map').appendChild(xml.documentElement.cloneNode(true));
   document.querySelector('#minimap').appendChild(xml.documentElement.cloneNode(true));


   let map = d3.select('#map').select('svg')
   let minimap = d3.select('#minimap').select('svg')
                    .attr('width', 200);

   let transform = d3.zoomIdentity.translate(0, 0).scale(1);

   let zoom = d3.zoom()
      .scaleExtent([1, 3])
      .on('zoom', zoomed);

   map.call(zoom)
      .call(zoom.transform, transform);

   function zoomed() {
      let mapMainContainer = map.select('#main_container')
         .attr('transform', d3.event.transform);

      minimap.select('#minimapRect').remove();

      let mapWidth = parseFloat( d3.select('#map').style('width') );
      let mapHeight = parseFloat( d3.select('#map').style('height') );


      let minimapWidth = parseFloat( d3.select('#minimap').style('width') );
      let minimapHeight = parseFloat( d3.select('#minimap').style('height') );

      let minimapScale = minimapWidth / mapWidth ;   // size of big map times this = size of minimap
      let minimapRect = minimap.append('rect')
          .attr('id', 'minimapRect')
          .attr('width', minimapWidth / minimapScale) //HERE?
          .attr('height', minimapHeight / minimapScale ) //HERE?
          .attr('stroke', 'red')
          .attr('fill', 'black')
          .attr('transform', `translate(${-d3.event.transform.x},${-d3.event.transform.y}) scale(${d3.event.transform.k})`);
   }
})

Здесь - это рабочая версия кода + поведение панорамирования и масштабирования.

enter image description here

ОБНОВЛЕНИЕ

Рабочая версия кода ручки

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Я нашел другой способ достижения тех же результатов, используя d3.zoomIdentity.scale. Рабочий пример

function zoomed() {
      let transform = d3.event.transform;
      let modifiedTransform = d3.zoomIdentity.scale( 1/transform.k ).translate( -transform.x, -transform.y );

      let mapMainContainer = map.select('#main_container')
         .attr('transform', transform);


      minimapRect
          .attr('width', mapMainContainer.node().getBBox().width )
          .attr('height', mapMainContainer.node().getBBox().height )
          .attr('stroke', 'red')
          .attr('stroke-width', 10/modifiedTransform.k )
          .attr('stroke-dasharray', 10/modifiedTransform.k )
          .attr('fill', 'none')
          .attr('transform', modifiedTransform);
   }
0 голосов
/ 20 сентября 2018

В настоящее время вы ограничиваете размер основной карты до 600 пикселей с помощью включающего элемента div, а мини-карту - до 200 пикселей.Обратите внимание, что система координат, которую используют оба, будет одинаковой.Ваши расчеты должны учитывать разницу между размером SVG viewBox и размером основной карты, т.е.

let mapWidth = parseFloat( d3.select('#map').style('width') );
let mapHeight = parseFloat( d3.select('#map').style('height') );
let factor = mapWidth / d3.select('#map svg').attr('viewBox').split(' ')[2]

Размеры rect на мини-карте должны быть установлены с использованием этого коэффициента масштабирования:

  let minimapRect = minimap.append('rect')
      .attr('id', 'minimapRect')
      .attr('width', mapWidth / factor )
      .attr('height', mapHeight / factor )

Когда происходят события масштабирования, у вас уже есть значения x и y и коэффициент масштабирования d3.event.transform.k, поэтому вам просто нужно разделить все на коэффициент масштабирования:

  let dx = d3.event.transform.x / d3.event.transform.k;
  let dy = d3.event.transform.y / d3.event.transform.k;
  let minimapRect = minimap.append('rect')
      .attr('id', 'minimapRect')
      .attr('width', mapWidth / factor / d3.event.transform.k )
      .attr('height', mapHeight / factor / d3.event.transform.k )
      .attr('stroke', 'red')
      .attr('fill', 'black')
      .attr('transform', `translate(${-dx},${-dy})`);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...