简体   繁体   English

如何将一堆 svg 元素缩放到 100% 的视口

[英]How to scale a bunch of svg-elements to 100% of viewport


meanwhile I've read a lot of sites about viewbox, viewports and svg scaling.同时我已经阅读了很多关于视框、视口和 svg 缩放的网站。
I have an svg-box, with variable size, depending on the browser-window.我有一个 svg 框,大小可变,具体取决于浏览器窗口。
Inside this, I display some elements like rects, lines texts etc.在其中,我显示了一些元素,如矩形、行文本等。
These elements, or better the view is "zoomable" by using the mousewheel.这些元素,或者更好的是使用鼠标滚轮“缩放”视图。

<h1>Editor:</h1>
<div id="content">
<svg id="drawing"
     title="Layouteditor"
     viewBox="0 0 500 300"
     xmlns="http://www.w3.org/2000/svg"
     version="2.0"
     preserveAspectRatio="xMidYMid meet">
<g class="draggable preview" id="437" pointer-events="fill" transform="translate(150 50)">
  <rect fill="none" x="80" y="100" width="41" height="41"></rect>
  <line class="CDC300" fill="none" x1="140" y1="120" x2="121" y2="120" />
  <text class="CDC300" font-family="sans-serif" font-size="30px" fill="#000000" stroke-width="0.3" text-anchor="middle" x="100" y="131">T</text>
  <text class="CDC400" id="20010" text-anchor="end" x="75" y="125">=HTC1+HSP1</text>
  <circle class="insertpoint" cx="140" cy="120" r="3" />
  <circle class="pin" cx="140" cy="120" r="2" />
</g>
<g id="a123" style="touch-action: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
  <line class="hotwater graphic" id="g123" x1="100" y1="120" x2="200" y2="120"></line>
</g> 
</svg>
</div>

the scripts:脚本:

var svgDrawing = document.getElementById('drawing');
var viewbox = svgDrawing.viewBox.baseVal;

function zoomInOut(evt) {
    if (evt.deltaY > 1) {
        viewbox.width += 10;
        viewbox.height += 10;
    } else {
        viewbox.width -= 10;
        viewbox.height -= 10;
    }
}

function zoomFull(evt){
    console.log("Zoom 100%");
  //here, the zoom should be set to 100% of the drawing
  //###################################################
  //###################################################
  //###################################################
}
$(document).ready(function () {
  console.log("document ready");
    svgDrawing.setAttribute("height", window.innerHeight - 200);
    svgDrawing.addEventListener('wheel', e => zoomInOut(e));
    svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})

Here, it is on codepen在这里,它在代码笔上

now, I'd like to add a "fullZoom"-function.现在,我想添加一个“fullZoom”功能。
For this, I need to get the outer dimensions of all nested elements, I think.为此,我认为我需要获取所有嵌套元素的外部尺寸。 So perhaps like a boundingBox over all elements.所以也许就像所有元素上的 boundingBox 一样。 And here, you come into the game.在这里,你进入了游戏。 I am totally confused meanwhile.同时我完全困惑。

Thank you,谢谢,
Carsten卡斯腾

I don't know if you realize but your SVG is also changing size (specifically its width as you scroll)?我不知道您是否意识到,但您的 SVG 也在改变大小(特别是滚动时的宽度)? (I added a blue border to the <div/> and black border to the <svg/> ) (我在<div/>添加了蓝色边框,在<svg/>添加了黑色边框)

On load:负载:

在此处输入图片说明

On scroll-in:在滚动时:

在此处输入图片说明

This is because you only fix the height on document load.这是因为您只在文档加载时固定高度。 If you ifx the width too, this problem goes away: https://codepen.io/Alexander9111/pen/poJzWEN如果您也调整宽度,这个问题就会消失: https ://codepen.io/Alexander9111/pen/poJzWEN

$(document).ready(function () {
    console.log("document ready");
    svgDrawing.setAttribute("height", window.innerHeight - 200);
    svgDrawing.setAttribute("width", 1.5 * (window.innerHeight - 200));
    svgDrawing.addEventListener('wheel', e => zoomInOut(e));
    svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})

Then we can scroll without "growing" our width.然后我们可以在不“增加”宽度的情况下滚动。

Is your desired outcome on double-click something like this:双击时您想要的结果是这样的:

ie any elements within as close to the edge of the svg as allows?即尽可能靠近 svg 边缘的任何元素?

在此处输入图片说明

UPDATE更新

If you want to just return to original zoom, assuming you created the SVG with the original zoom first set up (hard-coded) etc. in the viewport etc.?如果您只想返回原始缩放,假设您首先在视口等中设置(硬编码)原始缩放创建了 SVG? Then you can simply save this original value and reset it when the user double clicks?然后你可以简单地保存这个原始值并在用户双击时重置它?

Demo: https://codepen.io/Alexander9111/pen/poJzWEN?editors=1010演示: https : //codepen.io/Alexander9111/pen/poJzWEN?editors=1010

JS: JS:

const svgDrawing = document.getElementById('drawing');
// console.log(svgDrawing.currentTranslate);
let viewbox = svgDrawing.viewBox.baseVal;
const original_w = viewbox.width;
const original_h = viewbox.height;
const w_h_ratio = 2;
const g = document.getElementById('437'); 
console.log(g.transform.baseVal.consolidate().matrix);
var point = svgDrawing.createSVGPoint();

function zoomEvent(evt) {
  evt.preventDefault();
  if (evt.deltaY > 1) {
    zoomInOut(-1, 10);
  } else {
    zoomInOut(+1, 10);
  }
}

function zoomInOut(direction, stepSize) {
  if (direction > 0) {
    viewbox.width -= w_h_ratio * stepSize;
    viewbox.height -= stepSize;
  } else {
    viewbox.width += w_h_ratio * stepSize;
    viewbox.height += stepSize;
  }
}

function zoomFull(evt){
  evt.preventDefault();
  viewbox.width = original_w;
  viewbox.height = original_h;
  console.log("Zoom 100%");    
}

function elementIsInside(el, box){
  var result = false;
  el_rect = el.getBoundingClientRect();
  point.x = el_rect.x;
  point.y = el_rect.y;
  point.width = el_rect.width;
  point.height = el_rect.height; 
  var invertedSVGMatrix = el.getScreenCTM().inverse();
  var p = point.matrixTransform(invertedSVGMatrix);

  box_rect = box.getBoundingClientRect();
  if (el_rect.left >= box_rect.left &&
      el_rect.right <= box_rect.right &&
      el_rect.bottom <= box_rect.bottom &&
      el_rect.top >= box_rect.top){
    result = true;
  } else {
    result = false;
  }
  // console.log("result_" + el.tagName, result)
  return result;
}

$(document).ready(function () {
  console.log("document ready");
  svgDrawing.setAttribute("height", window.innerHeight - 200);
  svgDrawing.setAttribute("width", w_h_ratio * (window.innerHeight - 200));
  svgDrawing.addEventListener('wheel', e => zoomEvent(e));
  svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})

UPDATE更新

I also tried to do the other idea (to get the max zoom that still shows all elements on-screen: https://codepen.io/Alexander9111/pen/NWqWEOw and I believe I have done it.我还尝试了另一个想法(以获得仍然在屏幕上显示所有元素的最大缩放: https : //codepen.io/Alexander9111/pen/NWqWEOw ,我相信我已经做到了。

I added a red circle on the screen to show that we maximized the viewport to the maximum possible.我在屏幕上添加了一个红色圆圈,以表明我们已将视口最大化到可能的最大值。 ie: IE:

在此处输入图片说明

OR或者

在此处输入图片说明

Wherever we move the elements around, the double-click event will fire the function that loops through the elements and saves the largest bottom and right points of the boundary boxes each element has and then alters the zoom port to that.无论我们将元素移动到何处,双击事件都会触发循环遍历元素并保存每个元素所具有的边界框的最大底部和右侧点的函数,然后将缩放端口更改为该值。

JS: JS:

const svgDrawing = document.getElementById('drawing');
// console.log(svgDrawing.currentTranslate);
let viewbox = svgDrawing.viewBox.baseVal;
let original_w = viewbox.width;
let original_h = viewbox.height;
let w_h_ratio = 2;
const g = document.getElementById('437'); 
console.log(g.transform.baseVal.consolidate().matrix);
// const extra = document.getElementById('example'); 
// console.log(extra.transform.baseVal.consolidate().matrix);
const b_box = document.getElementById('bounding_box');
const b_box_max = {right: 0, bottom: 0};
var point = svgDrawing.createSVGPoint();

function zoomEvent(evt) {
  evt.preventDefault();
  if (evt.deltaY > 1) {
    zoomInOut(-1, 10);
  } else {
    zoomInOut(+1, 10);
  }
}

function zoomInOut(direction, stepSize) {
  if (direction > 0) {
    viewbox.width -= w_h_ratio * stepSize;
    viewbox.height -= stepSize;
  } else {
    viewbox.width += w_h_ratio * stepSize;
    viewbox.height += stepSize;
  }
}

function zoomToSize(width, height) {
  viewbox.width = width;
  viewbox.height = height;
}

function zoomFull(evt){
  evt.preventDefault();
  viewbox.width = original_w;
  viewbox.height = original_h;
  console.log("Zoom 100%");
  const elements = [...svgDrawing.childNodes];
  console.log("elements", elements);
  var inside = true;
  for (let el of elements){            
    if (el.nodeName == "#text"){
      //this is not a real node
    } else if (elementIsInside(el, svgDrawing)){
      inside = true;
    } else {
      inside = false;
    }
  }
  // var dir = inside ? +1 : -1;
  // zoomInOut(dir, (100/(10**i)));
  zoomToSize(b_box_max.right,b_box_max.bottom);
}

function elementIsInside(el, box){
  var result = false;
  el_rect = el.getBoundingClientRect();
  point.x = el_rect.right;
  point.y = el_rect.bottom;
  var invertedSVGMatrix = el.getScreenCTM().inverse();
  var p = point.matrixTransform(invertedSVGMatrix);
  let trans = el.transform.baseVal;
  if (trans.length > 0){
    const matrix = trans.consolidate().matrix;
    p.x += matrix.e;
    p.y += matrix.f;
  }
  if (p.x > b_box_max.right){
    b_box.setAttribute('cx', p.x);
    b_box_max.right = p.x;
    b_box.setAttribute('cx', p.x);
  }
  if (p.y > b_box_max.bottom){
    b_box.setAttribute('cy', p.y);
    b_box_max.bottom = p.y;
    b_box.setAttribute('cy', p.y);
  }
  b_box.setAttribute('r', 5);  

  console.log(p.x, p.y);
  console.log(b_box_max.right,b_box_max.bottom);
  box_rect = box.getBoundingClientRect();
  if (el_rect.left >= box_rect.left &&
      el_rect.right <= box_rect.right &&
      el_rect.bottom <= box_rect.bottom &&
      el_rect.top >= box_rect.top){
    result = true;
  } else {
    result = false;
  }
  // console.log("result_" + el.tagName, result)
  return result;
}

$(document).ready(function () {
  console.log("document ready");
  resizeSVG();
  svgDrawing.addEventListener('wheel', e => zoomEvent(e));
  svgDrawing.addEventListener('dblclick', e=>zoomFull(e));
})

$(window).resize(function() {
  console.log($(window).width(), $(window).height());
  // console.log(window.innerWidth, window.innerHeight);
   resizeSVG();
});

function resizeSVG(){
  if ((window.innerWidth - 40) / w_h_ratio > (window.innerHeight - 120)){
    svgDrawing.setAttribute("height", window.innerHeight - 120);
    svgDrawing.setAttribute("width", w_h_ratio * (window.innerHeight - 120));
  } else{
    svgDrawing.setAttribute("height", (window.innerWidth - 40) / w_h_ratio);
    svgDrawing.setAttribute("width", (window.innerWidth - 40));
  }
  viewbox = svgDrawing.viewBox.baseVal;
  original_w = viewbox.width;
  original_h = viewbox.height;
  w_h_ratio = 2;
}

Most important lines:最重要的几行:

function zoomFull(evt){
  evt.preventDefault();
  viewbox.width = original_w;
  viewbox.height = original_h;
  console.log("Zoom 100%");
  const elements = [...svgDrawing.childNodes];
  console.log("elements", elements);
  var inside = true;
  for (let el of elements){            
    if (el.nodeName == "#text"){
      //this is not a real node
    } else if (elementIsInside(el, svgDrawing)){
      inside = true;
    } else {
      inside = false;
    }
  }
  zoomToSize(b_box_max.right,b_box_max.bottom);
}

Loop through all children elements in the svg and call the function elementIsInside() :循环遍历 svg 中的所有子元素并调用函数elementIsInside()

function elementIsInside(el, box){
  var result = false;
  el_rect = el.getBoundingClientRect();
  point.x = el_rect.right;
  point.y = el_rect.bottom;
  var invertedSVGMatrix = el.getScreenCTM().inverse();
  var p = point.matrixTransform(invertedSVGMatrix);
  let trans = el.transform.baseVal;
  if (trans.length > 0){
    const matrix = trans.consolidate().matrix;
    p.x += matrix.e;
    p.y += matrix.f;
  }
  if (p.x > b_box_max.right){
    b_box.setAttribute('cx', p.x);
    b_box_max.right = p.x;
    b_box.setAttribute('cx', p.x);
  }
  if (p.y > b_box_max.bottom){
    b_box.setAttribute('cy', p.y);
    b_box_max.bottom = p.y;
    b_box.setAttribute('cy', p.y);
  }
  b_box.setAttribute('r', 5);  

  console.log(p.x, p.y);
  console.log(b_box_max.right,b_box_max.bottom);
  box_rect = box.getBoundingClientRect();
  if (el_rect.left >= box_rect.left &&
      el_rect.right <= box_rect.right &&
      el_rect.bottom <= box_rect.bottom &&
      el_rect.top >= box_rect.top){
    result = true;
  } else {
    result = false;
  }
  // console.log("result_" + el.tagName, result)
  return result;
}

Translate bounding client rect from page coordinates to svg coordinates.将边界客户端矩形从页面坐标转换为 svg 坐标。

If the element has a transformation (like the group elements often do - such as transform="translate(200, 250)" , then we also add these (matrix.e and matrix.f) onto our points. If the right is larger than the currently saved maximum right, then replace it, same for the bottom. Move the circle to this point to highlight.如果元素有一个变换(就像组元素经常做的那样——比如transform="translate(200, 250)" ,那么我们也将这些(matrix.e 和 matrix.f)添加到我们的点上。如果右边更大比当前保存的最大值更右,然后替换它,底部相同。将圆圈移动到该点以突出显示。

UPDATE更新

https://codepen.io/Alexander9111/pen/NWqWEOw https://codepen.io/Alexander9111/pen/NWqWEOw

You can also add a top left minimum point (shown here in green):您还可以添加左上角的最小点(此处以绿色显示):

在此处输入图片说明

This comes from the following function, making a second point, p2:这来自以下函数,提出第二点 p2:

function elementIsInside(el, box){
  var result = false;
  el_rect = el.getBoundingClientRect();
  point.x = el_rect.right;
  point.y = el_rect.bottom;
  var invertedSVGMatrix = el.getScreenCTM().inverse();
  var p = point.matrixTransform(invertedSVGMatrix);
  point.x = el_rect.left;
  point.y = el_rect.top;
  var p2 = point.matrixTransform(invertedSVGMatrix);
  let trans = el.transform.baseVal;
  if (trans.length > 0){
    const matrix = trans.consolidate().matrix;
    p.x += matrix.e;
    p.y += matrix.f;
    p2.x += matrix.e;
    p2.y += matrix.f;
  }
  if (p.x > b_box_max.right){
    b_box_br.setAttribute('cx', p.x);
    b_box_max.right = p.x;
  }
  if (p.y > b_box_max.bottom){
    b_box_br.setAttribute('cy', p.y);
    b_box_max.bottom = p.y;
  }
  b_box_br.setAttribute('r', 5); 

  if (p2.x < b_box_max.left){
    b_box_tl.setAttribute('cx', p2.x);
    b_box_max.left = p2.x;
  }
  if (p2.y < b_box_max.top){
    b_box_tl.setAttribute('cy', p2.y);
    b_box_max.top = p2.y;
  }
  b_box_tl.setAttribute('r', 5); 

  b_box_max.width = b_box_max.right - b_box_max.left;
  b_box_max.height = b_box_max.bottom - b_box_max.top;

  console.log(p2.x, p2.y, p.x, p.y);
  console.log(b_box_max.right,b_box_max.bottom);
  box_rect = box.getBoundingClientRect();
  if (el_rect.left >= box_rect.left &&
      el_rect.right <= box_rect.right &&
      el_rect.bottom <= box_rect.bottom &&
      el_rect.top >= box_rect.top){
    result = true;
  } else {
    result = false;
  }
  // console.log("result_" + el.tagName, result)
  return result;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM