D3 SVG Переместите масштаб, чтобы прямоугольник внутри <g> имел другой угол

У меня есть <svg> с фоновыми изображениями и другим прямоугольником на этом svg, которые находятся в правильном положении.
Мои прямоугольники сгруппированы по <g> со значениями перевода, и каждый прямоугольник имеет определенные значения поворота. Теперь я хочу, чтобы мой SVG фокусировался (показывался в центре страницы) на определенном прямоугольнике, не нарушая фоновое изображение, и svg переводил туда. Основная проблема заключается в том, что эти прямоугольники тоже имеют разные углы. Так что перевод не работает должным образом....

SVG выглядит так, как показано ниже.
Может ли кто-нибудь дать мне рекомендации?

<svg width = "4500" height = "3610" style = "background: url(&quot;/images/001.jpg&quot;) no-repeat;">   
<g id = "228" transform = "translate(2001,620)rotate(216) translate(0 -216)">
 <g>
<rect x = "0" y = "0" height = "72" width = "36" stroke = "black" style = "fill: transparent;">
</rect><text x = "0" y = "36" text-anchor = "start" style = "fill: black;">228</text>
</g>
 <g>
  <rect x = "36" y = "0" height = "72" width = "36" stroke = "black" style = "fill: transparent;">
  </rect><text x = "36" y = "36" text-anchor = "start" style = "fill: black;">229</text>
   </g>
 </g>
 </svg>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
0
230
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это должно быть проще в SVG, и может быть, но вот мое решение:

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

// Bounds in local coordinates space:
var bounds = rectElement.getBBox();

Во-вторых, мы получаем четыре угла из этой ограничивающей рамки:

 // Corners of rect in local coordinate space:
 var corners = [
     {x: bounds.x,y: bounds.y},
     {x: bounds.x + bounds.width, y: bounds.y},
     {x: bounds.x + bounds.width, y: bounds.y + bounds.height },
     {x: bounds.x, y: bounds.y + bounds.height }
 ];

В-третьих, мы получаем матрицу преобразования между прямоугольником и SVG:

 // relevant transform:
 var t = rectElement.getCTM();

Теперь мы можем преобразовать углы, которые у нас есть, в глобальные единицы SVG:

  // Convert the points to global SVG coordainte space:
  for(var i = 0; i < corners.length; i++) {
      var point = svg.node().createSVGPoint();
      point.x = corners[i].x;
      point.y = corners[i].y;
      corners[i] = point.matrixTransform(t);
   }

Хорошо, на данный момент у нас есть углы повернутого прямоугольника в пригодных для использования координатах. Мы хотим найти ширину и высоту этих координат:

 // get extents for x,y in global SVG space:
 var x = d3.extent(corners, function(d) { return d.x; });
 var y = d3.extent(corners, function(d) { return d.y; });
 var w = x[1] - x[0];
 var h = y[1] - y[0];    

Это дает нам повернутую ширину и высоту, которые мы теперь можем использовать для масштабирования SVG:. Здесь я использую небольшой запас в моей функции масштабирования (10% запаса с каждой стороны):

// For getting scale factor:
var scale = function(elementWidth,elementHeight,SVGWidth,SVGHeight) {
  return Math.min(SVGWidth/elementWidth*0.8,SVGHeight/elementHeight*0.8);
}

Который я использую так:

var k = scale(w,h,SVGwidth,SVGheight);

В этот момент я могу приблизить прямоугольник с переводом -x[0]*k, -y[0]*k и масштабом k. Однако это приведет к форме в верхнем левом углу SVG, я хочу учесть поля и все, что ширина/высота прямоугольника меньше относительно ширины/высоты SVG. Для этого я вычисляю смещение для каждого из x, y:

// Offset to center feature:
var ox = (SVGwidth - w*k)/2;
var oy = (SVGheight- h*k)/2;

И теперь я могу создать свое преобразование для центрирования заданного прямоугольника в SVG:

var newTransform = "translate("+[-x[0]*k+ox,-y[0]*k+oy]+")scale("+k+")";

Эти фрагменты используют родителя g для хранения всего в SVG, что упрощает управление преобразованиями. Кроме того, у этого g его преобразование стирается при масштабировании до прямоугольника, потому что нам нужно не относительное преобразование из текущего состояния/преобразования масштабирования, а одно в исходное состояние/преобразование масштабирования, иначе мы увидим странное поведение.

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

var svg = d3.select("svg");
var contents = svg.html();
svg.selectAll("*").remove();
var g = svg.append("g")
   .attr("transform", "translate(0,0)scale(1)")
   .html(contents);

var SVGwidth = 400;
var SVGheight = 180;

// For getting scale factor:
var scale = function(elementWidth,elementHeight,SVGWidth,SVGHeight) {
  return Math.min(SVGWidth/elementWidth*0.8,SVGHeight/elementHeight*0.8);
}

var zoom = d3.zoom()
  .on("zoom", function(d) {
    g.attr("transform",d3.event.transform);
  })

svg.call(zoom);

function zoomToRect(rect) {

        // Bounds in local coordinates space:
        var bounds = rect.getBBox();
        
        // Corners of rect in local coordinate space:
        var corners = [
          {x: bounds.x,y: bounds.y},
          {x: bounds.x + bounds.width, y: bounds.y},
          {x: bounds.x + bounds.width, y: bounds.y + bounds.height },
          {x: bounds.x, y: bounds.y + bounds.height }
        ];

        // Reset transform:
        g.attr("transform","");
     
        // relevant transform:
        var t = rect.getCTM();
        
        // Convert the points to global SVG coordainte space:
        for(var i = 0; i < corners.length; i++) {
          var point = svg.node().createSVGPoint();
          point.x = corners[i].x;
          point.y = corners[i].y;
          corners[i] = point.matrixTransform(t);
        }
        
        // get extents for x,y in global SVG space:
        var x = d3.extent(corners, function(d) { return d.x; });
        var y = d3.extent(corners, function(d) { return d.y; });
        var w = x[1] - x[0];
        var h = y[1] - y[0]; 
        var k = scale(w,h,SVGwidth,SVGheight);
        // Offset to center feature:
        var ox = (SVGwidth - w*k)/2;
        var oy = (SVGheight- h*k)/2;
         
        var newTransform = d3.zoomIdentity
          .translate(-x[0]*k+ox,-y[0]*k+oy)
          .scale(k);
          
        svg.call(zoom.transform,newTransform);              
        
  }
  
  
d3.select("button").on("click", function() { zoomToRect(d3.select("rect").node()) });
svg {
  border: 1px dotted black;
}
div {
  position: absolute;
  top: 0;
  left: 0;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width = "400" height = "180" style = "background: url(&quot;/images/001.jpg&quot;) no-repeat;">   
<g id = "228" transform = "translate(2001,620)rotate(216) translate(0 -216)">
  <g>
    <rect x = "0" y = "0" height = "72" width = "36" stroke = "black" style = "fill: transparent;"></rect>
    <text x = "0" y = "36" text-anchor = "start" style = "fill: black;">228</text>
  </g>
  <g>
     <rect x = "36" y = "0" height = "72" width = "36" stroke = "black" style = "fill: transparent;">
     </rect><text x = "36" y = "36" text-anchor = "start" style = "fill: black;">229</text>
  </g>
</g>
<g id = "228" transform = "translate(1801,550)rotate(65) translate(0 0)">
   <g>
    <rect x = "0" y = "0" height = "100" width = "20" stroke = "black" style = "fill: transparent;"></rect>
    <text x = "0" y = "36" text-anchor = "start" style = "fill: black;">454</text>
  </g>
  <g>
     <rect x = "36" y = "0" height = "20" width = "100" stroke = "black" style = "fill: transparent;">
     </rect><text x = "36" y = "36" text-anchor = "start" style = "fill: black;">455</text>
  </g> 
</g>

 </svg>
 <div><button>Zoom to 228</button></div>

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

var svg = d3.select("svg");
var contents = svg.html();
svg.selectAll("*").remove();
var g = svg.append("g")
   .attr("transform", "translate(0,0)scale(1)")
   .html(contents);

var SVGwidth = 400;
var SVGheight = 180;

// For getting scale factor:
var scale = function(elementWidth,elementHeight,SVGWidth,SVGHeight) {
  return Math.min(SVGWidth/elementWidth*0.8,SVGHeight/elementHeight*0.8);
}

var current = 0;

function transition() {
     g.selectAll("rect").each(function(_,i,n) {
        if (i != current) return;
       
        
        // Bounds in local coordinates space:
        var bounds = n[i].getBBox();
        
        // Corners of rect in local coordinate space:
        var corners = [
          {x: bounds.x,y: bounds.y},
          {x: bounds.x + bounds.width, y: bounds.y},
          {x: bounds.x + bounds.width, y: bounds.y + bounds.height },
          {x: bounds.x, y: bounds.y + bounds.height }
        ];

        // Hold original transform:
        var oldTransform = g.attr("transform");
        // Reset transform:
        g.attr("transform","");
     
        // relevant transform:
        var t = n[i].getCTM();
        
        // Convert the points to global SVG coordainte space:
        for(var i = 0; i < corners.length; i++) {
          var point = svg.node().createSVGPoint();
          point.x = corners[i].x;
          point.y = corners[i].y;
          corners[i] = point.matrixTransform(t);
        }
        
        // get extents for x,y in global SVG space:
        var x = d3.extent(corners, function(d) { return d.x; });
        var y = d3.extent(corners, function(d) { return d.y; });
        var w = x[1] - x[0];
        var h = y[1] - y[0]; 
        var k = scale(w,h,SVGwidth,SVGheight);
        // Offset to center feature:
        var ox = (SVGwidth - w*k)/2;
        var oy = (SVGheight- h*k)/2;
         
        var newTransform = "translate("+[-x[0]*k+ox,-y[0]*k+oy]+")scale("+k+")"
                
        g.attr("transform",oldTransform)
          .transition()
          .on("start", function() {
           d3.select(n[current]).style("fill","orange");
          })
          .duration(2000)
          .attr("transform",newTransform)
          .on("end", function() {
             d3.select(n[current]).style("fill","none");
             current++;
             current = current%g.selectAll("rect").size();
             transition();         
          })

     })

  }

transition();
svg {
  border: 1px dotted black;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width = "400" height = "180" style = "background: url(&quot;/images/001.jpg&quot;) no-repeat;">   
<g id = "228" transform = "translate(2001,620)rotate(216) translate(0 -216)">
  <g>
    <rect x = "0" y = "0" height = "72" width = "36" stroke = "black" style = "fill: transparent;"></rect>
    <text x = "0" y = "36" text-anchor = "start" style = "fill: black;">228</text>
  </g>
  <g>
     <rect x = "36" y = "0" height = "72" width = "36" stroke = "black" style = "fill: transparent;">
     </rect><text x = "36" y = "36" text-anchor = "start" style = "fill: black;">229</text>
  </g>
</g>
<g id = "228" transform = "translate(1801,550)rotate(65) translate(0 0)">
   <g>
    <rect x = "0" y = "0" height = "100" width = "20" stroke = "black" style = "fill: transparent;"></rect>
    <text x = "0" y = "36" text-anchor = "start" style = "fill: black;">454</text>
  </g>
  <g>
     <rect x = "36" y = "0" height = "20" width = "100" stroke = "black" style = "fill: transparent;">
     </rect><text x = "36" y = "36" text-anchor = "start" style = "fill: black;">455</text>
  </g> 
</g>

 </svg>

вы применили преобразование к <g>, но можем ли мы применить преобразование к <SVG>, потому что при применении преобразования фоновое изображение и прямоугольники не находятся точно в той же позиции, где они должны быть... фоновое изображение svg и подобное <g> должно быть синхронизированный инструмент

Bilal lilla 20.12.2020 08:19

Большое спасибо @andrew Reid.. Вы сэкономили мне много времени...

Bilal lilla 20.12.2020 09:07

Другие вопросы по теме